eletrotupi / tcc/ commit / d597464

api: repair the base tsconfig so the project type checks

The only strict offenders in src were two orphaned util modules from the
JS migration, imported nowhere, so they are gone
Pedro Lucas Porcellis porcellis@eletrotupi.com · 12 days ago d597464ddbba25d2555e9bbfc543ffc8c881b781 Browse files
Parents 3889890
Changed files
5
Additions
+6
Deletions
-195
api/package.json modified+1
api/package.json
1 1
{
2 2
  "name": "orbit-api",
3 3
  "version": "0.0.0",
4 4
  "description": "",
5 5
  "license": "GPL-3.0-only",
8 8
  "directories": {
9 9
    "test": "tests"
10 10
  },
11 11
  "scripts": {
12 12
    "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
13 +
    "typecheck": "tsc -p tsconfig.test.json --noEmit",
13 14
    "prod": "node dist/index.js",
14 15
    "start": "nodemon src/index.ts",
15 16
    "test": "dotenv -e .env.test -o -- vitest run",
16 17
    "test:watch": "dotenv -e .env.test -o -- vitest",
17 18
    "test:setup": "npm run test:setup:db && npm run test:setup:generate && npm run test:setup:migrate",
68 69
    "typescript": "^6.0.2",
69 70
    "vite-tsconfig-paths": "^5.1.4",
70 71
    "vitest": "^3.0.0"
71 72
  }
72 73
}
api/src/utils/emotions.ts → /dev/null
1 -
// Emotion types enum
2 -
export const Emotions = {
3 -
  JOY: 'joy',
4 -
  TRUST: 'trust',
5 -
  FEAR: 'fear',
6 -
  SURPRISE: 'surprise',
7 -
  SAD: 'sad',
8 -
  DISGUST: 'disgust',
9 -
  ANGRY: 'angry',
10 -
  ANXIETY: 'anxiety'
11 -
};
12 -

13 -
// Valid emotion values for validation
14 -
export const VALID_EMOTIONS = Object.values(Emotions);
15 -

16 -
// Validate emotion type
17 -
export function isValidEmotion(emotion) {
18 -
  return VALID_EMOTIONS.includes(emotion?.toLowerCase());
19 -
}
20 -

21 -
// Validate intensity value
22 -
export function isValidIntensity(intensity) {
23 -
  const num = Number(intensity);
24 -
  return !isNaN(num) && num >= 1 && num <= 10;
25 -
}
26 -

27 -
// Validate rating value
28 -
export function isValidRating(rating) {
29 -
  const num = Number(rating);
30 -
  return !isNaN(num) && num >= 1 && num <= 10;
31 -
}
32 -

33 -
// Validate level values (stress, anxiety, energy)
34 -
export function isValidLevel(level) {
35 -
  const num = Number(level);
36 -
  return !isNaN(num) && num >= 1 && num <= 10;
37 -
}
38 -

39 -
// Validate mood components array
40 -
export function validateMoodComponents(components) {
41 -
  if (!Array.isArray(components)) {
42 -
    return { valid: false, error: 'Components must be an array' };
43 -
  }
44 -

45 -
  const emotions = new Set();
46 -

47 -
  for (const component of components) {
48 -
    if (!Object.hasOwn(component, 'emotion') || !Object.hasOwn(component, 'intensity')) {
49 -
      return { valid: false, error: 'Each component must have emotion and intensity' };
50 -
    }
51 -

52 -
    const emotionLower = component.emotion.toLowerCase();
53 -

54 -
    if (!isValidEmotion(emotionLower)) {
55 -
      return { valid: false, error: `Invalid emotion: ${component.emotion}` };
56 -
    }
57 -

58 -
    if (!isValidIntensity(component.intensity)) {
59 -
      return { valid: false, error: `Invalid intensity for ${component.emotion}: must be 1-10` };
60 -
    }
61 -

62 -
    if (emotions.has(emotionLower)) {
63 -
      return { valid: false, error: `Duplicate emotion: ${component.emotion}` };
64 -
    }
65 -

66 -
    emotions.add(emotionLower);
67 -
  }
68 -

69 -
  return { valid: true };
70 -
}
71 -

72 -
// Get emotion statistics
73 -
export function calculateEmotionStats(moodComponents) {
74 -
  const stats = {
75 -
    dominant: null,
76 -
    average: 0,
77 -
    breakdown: {}
78 -
  };
79 -

80 -
  if (!moodComponents || moodComponents.length === 0) {
81 -
    return stats;
82 -
  }
83 -

84 -
  let maxIntensity = 0;
85 -
  let totalIntensity = 0;
86 -

87 -
  moodComponents.forEach(component => {
88 -
    const intensity = Number(component.intensity);
89 -
    totalIntensity += intensity;
90 -

91 -
    stats.breakdown[component.emotion] = intensity;
92 -

93 -
    if (intensity > maxIntensity) {
94 -
      maxIntensity = intensity;
95 -
      stats.dominant = component.emotion;
96 -
    }
97 -
  });
98 -

99 -
  stats.average = totalIntensity / moodComponents.length;
100 -

101 -
  return stats;
102 -
}
api/src/utils/validators.ts → /dev/null
1 -
import { parseISO, isValid, isFuture, isAfter } from 'date-fns';
2 -

3 -
// Email validation
4 -
const isValidEmail = (email: string) => {
5 -
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
6 -
  return emailRegex.test(email);
7 -
}
8 -

9 -
// Password validation (minimum 6 characters)
10 -
const isValidPassword = (password: string) => {
11 -
  return password && password.length >= 6;
12 -
}
13 -

14 -
// Date validation
15 -
const isValidDate = (dateString: string) => {
16 -
  if (!dateString) return false;
17 -
  try {
18 -
    const date = parseISO(dateString);
19 -
    return isValid(date);
20 -
  } catch {
21 -
    return false;
22 -
  }
23 -
}
24 -

25 -
// Check if date is not in the future
26 -
const isNotFutureDate = (dateString: string) => {
27 -
  if (!dateString) return false;
28 -
  try {
29 -
    const date = parseISO(dateString);
30 -
    return isValid(date) && !isFuture(date);
31 -
  } catch {
32 -
    return false;
33 -
  }
34 -
}
35 -

36 -
// Validate date range
37 -
const isValidDateRange = (startDate: string, endDate: string) => {
38 -
  if (!startDate || !endDate) return false;
39 -
  try {
40 -
    const start = parseISO(startDate);
41 -
    const end = parseISO(endDate);
42 -

43 -
    if (!isValid(start) || !isValid(end)) {
44 -
      return false;
45 -
    }
46 -

47 -
    return !isAfter(start, end);
48 -
  } catch {
49 -
    return false;
50 -
  }
51 -
}
52 -

53 -
// Sanitize string input
54 -
const sanitizeString = (str: string) => {
55 -
  if (typeof str !== 'string') return '';
56 -
  return str.trim().slice(0, 500); // Limit to 500 chars
57 -
}
58 -

59 -
// Validate required fields
60 -

61 -
const validateRequiredFields = (data: Record<string, unknown>, fields: string[]) => {
62 -
  const missing: string[] = [];
63 -

64 -
  for (const field of fields) {
65 -
    if (data[field] === undefined || data[field] === null || data[field] === '') {
66 -
      missing.push(field);
67 -
    }
68 -
  }
69 -

70 -
  return {
71 -
    valid: missing.length === 0,
72 -
    missing
73 -
  };
74 -
}
75 -

76 -
// Validate number in range
77 -
const isInRange = (value: number, min: number, max: number) => {
78 -
  const num = Number(value);
79 -
  return !isNaN(num) && num >= min && num <= max;
80 -
}
81 -

82 -
export {
83 -
  validateRequiredFields,
84 -
  sanitizeString,
85 -
  isInRange,
86 -
  isValidEmail,
87 -
  isValidPassword,
88 -
  isValidDateRange,
89 -
  isNotFutureDate,
90 -
  isValidDate,
91 -
};
api/tests/medicine-logs/create.test.ts
1 1
import { describe, it, expect, afterAll, beforeEach } from 'vitest'
2 2
import request from 'supertest'
3 3
import { createApp } from '@app/createApp'
4 4
import {
5 5
  buildPrisma,
44 44
      where: { regimenId: regimen.id },
45 45
      include: { regimen: true },
46 46
    })
47 47

48 48
    expect(log).not.toBeNull()
49 -
    expect(log!.regimen.name).toBe('Sertralina')
50 -
    expect(log!.regimen.dosage).toBe('50mg')
49 +
    expect(log!.regimen!.name).toBe('Sertralina')
50 +
    expect(log!.regimen!.dosage).toBe('50mg')
51 51
  })
52 52

53 53
  it('creates a medicine log and a new regimen when sending inline medicine', async () => {
54 54
    const user = await createTestUser(db)
55 55
    const token = generateTestToken(user.id, user.email)
144 144
    expect(
145 145
      new Date(log!.takenAt).getTime()
146 146
    ).toBeCloseTo(new Date(takenAt).getTime(), -3)
147 147
  })
148 148
})
api/tsconfig.json modified+3
api/tsconfig.json
1 1
{
2 2
  "compilerOptions": {
3 +
    "ignoreDeprecations": "6.0",
3 4
    "target": "ES2020",
4 5
    "module": "CommonJS",
5 6
    "moduleResolution": "node",
7 +
    "lib": ["ES2022", "DOM"],
6 8
    "outDir": "dist",
7 9
    "strict": true,
8 10
    "esModuleInterop": true,
11 +
    "skipLibCheck": true,
9 12
    "baseUrl": ".",
10 13
    "paths": {
11 14
      "@app/*": ["src/*"]
12 15
    },
13 16
    "jsx": "react"
14 17
  }
15 18
}
16 19