From 32f83c4122d52b9299ada35b88d32e5aa98eb1f2 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Sun, 2 Mar 2025 19:08:28 +0300 Subject: [PATCH] B: Improve typechecks and fix backend API data --- .../apps/oss/serializers/data_access.py | 1 + .../apps/rsform/serializers/__init__.py | 2 +- .../apps/rsform/serializers/data_access.py | 12 +++--- .../backend/apps/rsform/views/rsforms.py | 8 ++-- rsconcept/backend/apps/users/serializers.py | 14 +++++++ rsconcept/backend/apps/users/views.py | 2 +- rsconcept/frontend/eslint.config.js | 3 +- .../src/features/auth/backend/types.ts | 2 +- .../src/features/library/backend/types.ts | 10 ++--- .../library/dialogs/DlgChangeLocation.tsx | 2 +- .../src/features/oss/backend/types.ts | 24 +++++------ .../features/oss/pages/OssPage/OssPage.tsx | 2 +- .../features/rsform/backend/cctext/types.ts | 6 +-- .../src/features/rsform/backend/types.ts | 42 +++++++++---------- .../DlgEditReference/DlgEditReference.tsx | 7 +++- .../src/features/rsform/models/language.ts | 6 +-- .../rsform/pages/RSFormPage/RSFormPage.tsx | 2 +- .../src/features/users/backend/types.ts | 4 +- rsconcept/frontend/tsconfig.node.json | 9 +++- 19 files changed, 90 insertions(+), 68 deletions(-) diff --git a/rsconcept/backend/apps/oss/serializers/data_access.py b/rsconcept/backend/apps/oss/serializers/data_access.py index 5cb04e4f..d84e15f6 100644 --- a/rsconcept/backend/apps/oss/serializers/data_access.py +++ b/rsconcept/backend/apps/oss/serializers/data_access.py @@ -203,6 +203,7 @@ class OperationSchemaSerializer(serializers.ModelSerializer): def to_representation(self, instance: LibraryItem): result = LibraryItemDetailsSerializer(instance).data + del result['versions'] oss = OperationSchema(instance) result['items'] = [] for operation in oss.operations().order_by('pk'): diff --git a/rsconcept/backend/apps/rsform/serializers/__init__.py b/rsconcept/backend/apps/rsform/serializers/__init__.py index ead9c317..1eb28fff 100644 --- a/rsconcept/backend/apps/rsform/serializers/__init__.py +++ b/rsconcept/backend/apps/rsform/serializers/__init__.py @@ -13,10 +13,10 @@ from .basics import ( ) from .data_access import ( CstCreateSerializer, + CstInfoSerializer, CstListSerializer, CstMoveSerializer, CstRenameSerializer, - CstSerializer, CstSubstituteSerializer, CstTargetSerializer, CstUpdateSerializer, diff --git a/rsconcept/backend/apps/rsform/serializers/data_access.py b/rsconcept/backend/apps/rsform/serializers/data_access.py index c2c63d6b..a53e5e46 100644 --- a/rsconcept/backend/apps/rsform/serializers/data_access.py +++ b/rsconcept/backend/apps/rsform/serializers/data_access.py @@ -30,13 +30,12 @@ class CstBaseSerializer(serializers.ModelSerializer): read_only_fields = ('id',) -class CstSerializer(serializers.ModelSerializer): - ''' Serializer: Constituenta data. ''' +class CstInfoSerializer(serializers.ModelSerializer): + ''' Serializer: Constituenta public information. ''' class Meta: ''' serializer metadata. ''' model = Constituenta - exclude = ('order',) - read_only_fields = ('id', 'schema', 'alias', 'cst_type', 'definition_resolved', 'term_resolved') + exclude = ('order', 'schema') class CstUpdateSerializer(serializers.Serializer): @@ -100,7 +99,7 @@ class RSFormSerializer(serializers.ModelSerializer): child=serializers.IntegerField() ) items = serializers.ListField( - child=CstSerializer() + child=CstInfoSerializer() ) inheritance = serializers.ListField( child=InheritanceDataSerializer() @@ -136,8 +135,7 @@ class RSFormSerializer(serializers.ModelSerializer): result['oss'] = [] result['inheritance'] = [] for cst in RSForm(instance).constituents().defer('order').order_by('order'): - result['items'].append(CstSerializer(cst).data) - del result['items'][-1]['schema'] + result['items'].append(CstInfoSerializer(cst).data) for oss in LibraryItem.objects.filter(operations__result=instance).only('alias'): result['oss'].append({ 'id': oss.pk, diff --git a/rsconcept/backend/apps/rsform/views/rsforms.py b/rsconcept/backend/apps/rsform/views/rsforms.py index 53b3ad29..dd3df7db 100644 --- a/rsconcept/backend/apps/rsform/views/rsforms.py +++ b/rsconcept/backend/apps/rsform/views/rsforms.py @@ -92,7 +92,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr return Response( status=c.HTTP_201_CREATED, data={ - 'new_cst': s.CstSerializer(new_cst).data, + 'new_cst': s.CstInfoSerializer(new_cst).data, 'schema': s.RSFormParseSerializer(schema.model).data } ) @@ -102,7 +102,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr tags=['RSForm'], request=s.CstUpdateSerializer, responses={ - c.HTTP_200_OK: s.CstSerializer, + c.HTTP_200_OK: s.CstInfoSerializer, c.HTTP_400_BAD_REQUEST: None, c.HTTP_403_FORBIDDEN: None, c.HTTP_404_NOT_FOUND: None @@ -122,7 +122,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr PropagationFacade.after_update_cst(schema, cst, data, old_data) return Response( status=c.HTTP_200_OK, - data=s.CstSerializer(m.Constituenta.objects.get(pk=request.data['target'])).data + data=s.CstInfoSerializer(m.Constituenta.objects.get(pk=request.data['target'])).data ) @extend_schema( @@ -202,7 +202,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr return Response( status=c.HTTP_200_OK, data={ - 'new_cst': s.CstSerializer(cst).data, + 'new_cst': s.CstInfoSerializer(cst).data, 'schema': s.RSFormParseSerializer(schema.model).data } ) diff --git a/rsconcept/backend/apps/users/serializers.py b/rsconcept/backend/apps/users/serializers.py index 4dc1f13d..071f058c 100644 --- a/rsconcept/backend/apps/users/serializers.py +++ b/rsconcept/backend/apps/users/serializers.py @@ -117,6 +117,20 @@ class UserSerializer(serializers.ModelSerializer): return attrs +class UserInfoSerializer(serializers.ModelSerializer): + ''' Serializer: User open information. ''' + id = serializers.IntegerField(read_only=True) + + class Meta: + ''' serializer metadata. ''' + model = models.User + fields = [ + 'id', + 'first_name', + 'last_name', + ] + + class ChangePasswordSerializer(serializers.Serializer): ''' Serializer: Change password. ''' old_password = serializers.CharField(required=True) diff --git a/rsconcept/backend/apps/users/views.py b/rsconcept/backend/apps/users/views.py index ef67bb72..75f9cad1 100644 --- a/rsconcept/backend/apps/users/views.py +++ b/rsconcept/backend/apps/users/views.py @@ -73,7 +73,7 @@ class AuthAPIView(generics.RetrieveAPIView): class ActiveUsersView(generics.ListAPIView): ''' Endpoint: Get list of active users. ''' permission_classes = (permissions.AllowAny,) - serializer_class = s.UserSerializer + serializer_class = s.UserInfoSerializer def get_queryset(self): return m.User.objects.filter(is_active=True) diff --git a/rsconcept/frontend/eslint.config.js b/rsconcept/frontend/eslint.config.js index 7948a5ed..7c9a475d 100644 --- a/rsconcept/frontend/eslint.config.js +++ b/rsconcept/frontend/eslint.config.js @@ -20,7 +20,8 @@ export default [ ecmaVersion: 'latest', sourceType: 'module', globals: { ...globals.browser, ...globals.es2020, ...globals.jest }, - project: ['./tsconfig.json', './tsconfig.node.json'] + project: ['./tsconfig.json', './tsconfig.node.json'], + projectService: true } } }, diff --git a/rsconcept/frontend/src/features/auth/backend/types.ts b/rsconcept/frontend/src/features/auth/backend/types.ts index 0d760a15..6bf978aa 100644 --- a/rsconcept/frontend/src/features/auth/backend/types.ts +++ b/rsconcept/frontend/src/features/auth/backend/types.ts @@ -15,7 +15,7 @@ export interface ICurrentUser { /** * Represents login data, used to authenticate users. */ -export const schemaUserLogin = z.object({ +export const schemaUserLogin = z.strictObject({ username: z.string().nonempty(errorMsg.requiredField), password: z.string().nonempty(errorMsg.requiredField) }); diff --git a/rsconcept/frontend/src/features/library/backend/types.ts b/rsconcept/frontend/src/features/library/backend/types.ts index 488301fd..e99e826e 100644 --- a/rsconcept/frontend/src/features/library/backend/types.ts +++ b/rsconcept/frontend/src/features/library/backend/types.ts @@ -51,7 +51,7 @@ export type IVersionUpdateDTO = z.infer; // ======= SCHEMAS ========= -export const schemaLibraryItem = z.object({ +export const schemaLibraryItem = z.strictObject({ id: z.coerce.number(), item_type: z.nativeEnum(LibraryItemType), title: z.string(), @@ -112,7 +112,7 @@ export const schemaCreateLibraryItem = z message: errorMsg.requiredField }); -export const schemaUpdateLibraryItem = z.object({ +export const schemaUpdateLibraryItem = z.strictObject({ id: z.number(), item_type: z.nativeEnum(LibraryItemType), title: z.string().nonempty(errorMsg.requiredField), @@ -122,20 +122,20 @@ export const schemaUpdateLibraryItem = z.object({ read_only: z.boolean() }); -export const schemaVersionInfo = z.object({ +export const schemaVersionInfo = z.strictObject({ id: z.coerce.number(), version: z.string(), description: z.string(), time_create: z.string().datetime({ offset: true }) }); -export const schemaVersionUpdate = z.object({ +export const schemaVersionUpdate = z.strictObject({ id: z.number(), version: z.string().nonempty(errorMsg.requiredField), description: z.string() }); -export const schemaVersionCreate = z.object({ +export const schemaVersionCreate = z.strictObject({ version: z.string(), description: z.string(), items: z.array(z.number()) diff --git a/rsconcept/frontend/src/features/library/dialogs/DlgChangeLocation.tsx b/rsconcept/frontend/src/features/library/dialogs/DlgChangeLocation.tsx index 0d843ac3..88f8e67c 100644 --- a/rsconcept/frontend/src/features/library/dialogs/DlgChangeLocation.tsx +++ b/rsconcept/frontend/src/features/library/dialogs/DlgChangeLocation.tsx @@ -18,7 +18,7 @@ import { SelectLocationHead } from '../components/SelectLocationHead'; import { LocationHead } from '../models/library'; import { combineLocation, validateLocation } from '../models/libraryAPI'; -const schemaLocation = z.object({ +const schemaLocation = z.strictObject({ location: z.string().refine(data => validateLocation(data), { message: errorMsg.invalidLocation }) }); diff --git a/rsconcept/frontend/src/features/oss/backend/types.ts b/rsconcept/frontend/src/features/oss/backend/types.ts index 138964d5..74a425bd 100644 --- a/rsconcept/frontend/src/features/oss/backend/types.ts +++ b/rsconcept/frontend/src/features/oss/backend/types.ts @@ -58,7 +58,7 @@ export type IConstituentaReference = z.infer // ====== Schemas ====== -export const schemaOperation = z.object({ +export const schemaOperation = z.strictObject({ id: z.number(), operation_type: z.nativeEnum(OperationType), oss: z.number(), @@ -93,15 +93,15 @@ export const schemaOperationSchema = schemaLibraryItem.extend({ substitutions: z.array(schemaCstSubstituteInfo) }); -export const schemaOperationPosition = z.object({ +export const schemaOperationPosition = z.strictObject({ id: z.number(), position_x: z.number(), position_y: z.number() }); -export const schemaOperationCreate = z.object({ +export const schemaOperationCreate = z.strictObject({ positions: z.array(schemaOperationPosition), - item_data: z.object({ + item_data: z.strictObject({ alias: z.string().nonempty(), operation_type: z.nativeEnum(OperationType), title: z.string(), @@ -114,33 +114,33 @@ export const schemaOperationCreate = z.object({ create_schema: z.boolean() }); -export const schemaOperationCreatedResponse = z.object({ +export const schemaOperationCreatedResponse = z.strictObject({ new_operation: schemaOperation, oss: schemaOperationSchema }); -export const schemaOperationDelete = z.object({ +export const schemaOperationDelete = z.strictObject({ target: z.number(), positions: z.array(schemaOperationPosition), keep_constituents: z.boolean(), delete_schema: z.boolean() }); -export const schemaInputUpdate = z.object({ +export const schemaInputUpdate = z.strictObject({ target: z.number(), positions: z.array(schemaOperationPosition), input: z.number().nullable() }); -export const schemaInputCreatedResponse = z.object({ +export const schemaInputCreatedResponse = z.strictObject({ new_schema: schemaLibraryItem, oss: schemaOperationSchema }); -export const schemaOperationUpdate = z.object({ +export const schemaOperationUpdate = z.strictObject({ target: z.number(), positions: z.array(schemaOperationPosition), - item_data: z.object({ + item_data: z.strictObject({ alias: z.string().nonempty(errorMsg.requiredField), title: z.string(), comment: z.string() @@ -149,12 +149,12 @@ export const schemaOperationUpdate = z.object({ substitutions: z.array(schemaCstSubstitute) }); -export const schemaCstRelocate = z.object({ +export const schemaCstRelocate = z.strictObject({ destination: z.number().nullable(), items: z.array(z.number()).refine(data => data.length > 0) }); -export const schemaConstituentaReference = z.object({ +export const schemaConstituentaReference = z.strictObject({ id: z.number(), schema: z.number() }); diff --git a/rsconcept/frontend/src/features/oss/pages/OssPage/OssPage.tsx b/rsconcept/frontend/src/features/oss/pages/OssPage/OssPage.tsx index 122e910b..d9e7d23a 100644 --- a/rsconcept/frontend/src/features/oss/pages/OssPage/OssPage.tsx +++ b/rsconcept/frontend/src/features/oss/pages/OssPage/OssPage.tsx @@ -19,7 +19,7 @@ import { OperationTooltip } from '../../components/OperationTooltip'; import { OssEditState, OssTabID } from './OssEditContext'; import { OssTabs } from './OssTabs'; -const paramsSchema = z.object({ +const paramsSchema = z.strictObject({ id: z.coerce.number(), tab: z.preprocess(v => (v ? Number(v) : undefined), z.nativeEnum(OssTabID).default(OssTabID.GRAPH)) }); diff --git a/rsconcept/frontend/src/features/rsform/backend/cctext/types.ts b/rsconcept/frontend/src/features/rsform/backend/cctext/types.ts index 5c6f1bf6..512f0a13 100644 --- a/rsconcept/frontend/src/features/rsform/backend/cctext/types.ts +++ b/rsconcept/frontend/src/features/rsform/backend/cctext/types.ts @@ -11,15 +11,15 @@ export type ILexemeResponse = z.infer; // ====== Schemas ========= -export const schemaTextResult = z.object({ +export const schemaTextResult = z.strictObject({ result: z.string() }); -export const schemaWordForm = z.object({ +export const schemaWordForm = z.strictObject({ text: z.string(), grams: z.string() }); -export const schemaLexemeResponse = z.object({ +export const schemaLexemeResponse = z.strictObject({ items: z.array(schemaWordForm) }); diff --git a/rsconcept/frontend/src/features/rsform/backend/types.ts b/rsconcept/frontend/src/features/rsform/backend/types.ts index 43b695ea..fc5a5141 100644 --- a/rsconcept/frontend/src/features/rsform/backend/types.ts +++ b/rsconcept/frontend/src/features/rsform/backend/types.ts @@ -266,7 +266,7 @@ export enum RSErrorType { } // ========= SCHEMAS ======== -export const schemaConstituentaBasics = z.object({ +export const schemaConstituentaBasics = z.strictObject({ id: z.coerce.number(), alias: z.string().nonempty(errorMsg.requiredField), convention: z.string(), @@ -276,16 +276,16 @@ export const schemaConstituentaBasics = z.object({ definition_resolved: z.string(), term_raw: z.string(), term_resolved: z.string(), - term_forms: z.array(z.object({ text: z.string(), tags: z.string() })) + term_forms: z.array(z.strictObject({ text: z.string(), tags: z.string() })) }); export const schemaConstituenta = schemaConstituentaBasics.extend({ - parse: z.object({ + parse: z.strictObject({ status: z.nativeEnum(ParsingStatus), valueClass: z.nativeEnum(ValueClass), typification: z.string(), syntaxTree: z.string(), - args: z.array(z.object({ alias: z.string(), typification: z.string() })) + args: z.array(z.strictObject({ alias: z.string(), typification: z.string() })) }) }); @@ -297,17 +297,17 @@ export const schemaRSForm = schemaLibraryItem.extend({ items: z.array(schemaConstituenta), inheritance: z.array( - z.object({ + z.strictObject({ child: z.coerce.number(), child_source: z.coerce.number(), parent: z.coerce.number(), parent_source: z.coerce.number() }) ), - oss: z.array(z.object({ id: z.coerce.number(), alias: z.string() })) + oss: z.array(z.strictObject({ id: z.coerce.number(), alias: z.string() })) }); -export const schemaVersionCreatedResponse = z.object({ +export const schemaVersionCreatedResponse = z.strictObject({ version: z.number(), schema: schemaRSForm }); @@ -326,57 +326,57 @@ export const schemaCstCreate = schemaConstituentaBasics insert_after: z.number().nullable() }); -export const schemaCstCreatedResponse = z.object({ +export const schemaCstCreatedResponse = z.strictObject({ new_cst: schemaConstituentaBasics, schema: schemaRSForm }); -export const schemaCstUpdate = z.object({ +export const schemaCstUpdate = z.strictObject({ target: z.number(), - item_data: z.object({ + item_data: z.strictObject({ convention: z.string().optional(), definition_formal: z.string().optional(), definition_raw: z.string().optional(), term_raw: z.string().optional(), - term_forms: z.array(z.object({ text: z.string(), tags: z.string() })).optional() + term_forms: z.array(z.strictObject({ text: z.string(), tags: z.string() })).optional() }) }); -export const schemaCstRename = z.object({ +export const schemaCstRename = z.strictObject({ target: z.number(), alias: z.string(), cst_type: z.nativeEnum(CstType) }); -export const schemaProduceStructureResponse = z.object({ +export const schemaProduceStructureResponse = z.strictObject({ cst_list: z.array(z.number()), schema: schemaRSForm }); -export const schemaCstSubstitute = z.object({ +export const schemaCstSubstitute = z.strictObject({ original: z.number(), substitution: z.number() }); -export const schemaCstSubstitutions = z.object({ +export const schemaCstSubstitutions = z.strictObject({ substitutions: z.array(schemaCstSubstitute).min(1, { message: errorMsg.emptySubstitutions }) }); -export const schemaInlineSynthesis = z.object({ +export const schemaInlineSynthesis = z.strictObject({ receiver: z.number(), source: z.number().nullable(), items: z.array(z.number()), substitutions: z.array(schemaCstSubstitute) }); -export const schemaRSErrorDescription = z.object({ +export const schemaRSErrorDescription = z.strictObject({ errorType: z.nativeEnum(RSErrorType), position: z.number(), isCritical: z.boolean(), params: z.array(z.string()) }); -export const schemaExpressionParse = z.object({ +export const schemaExpressionParse = z.strictObject({ parseResult: z.boolean(), prefixLen: z.number(), syntax: z.nativeEnum(Syntax), @@ -385,17 +385,17 @@ export const schemaExpressionParse = z.object({ errors: z.array(schemaRSErrorDescription), astText: z.string(), ast: z.array( - z.object({ + z.strictObject({ uid: z.number(), parent: z.number(), typeID: z.nativeEnum(TokenID), start: z.number(), finish: z.number(), - data: z.object({ dataType: z.string(), value: z.unknown().refine(value => value !== undefined) }) + data: z.strictObject({ dataType: z.string(), value: z.unknown().refine(value => value !== undefined) }) }) ), args: z.array( - z.object({ + z.strictObject({ alias: z.string(), typification: z.string() }) diff --git a/rsconcept/frontend/src/features/rsform/dialogs/DlgEditReference/DlgEditReference.tsx b/rsconcept/frontend/src/features/rsform/dialogs/DlgEditReference/DlgEditReference.tsx index 968db4d7..2c2c825c 100644 --- a/rsconcept/frontend/src/features/rsform/dialogs/DlgEditReference/DlgEditReference.tsx +++ b/rsconcept/frontend/src/features/rsform/dialogs/DlgEditReference/DlgEditReference.tsx @@ -36,8 +36,11 @@ export interface IReferenceInputState { const schemaEditReferenceState = z .object({ type: z.nativeEnum(ReferenceType), - entity: z.object({ entity: z.string(), grams: z.array(z.object({ value: z.string(), label: z.string() })) }), - syntactic: z.object({ offset: z.coerce.number(), nominal: z.string() }) + entity: z.strictObject({ + entity: z.string(), + grams: z.array(z.strictObject({ value: z.string(), label: z.string() })) + }), + syntactic: z.strictObject({ offset: z.coerce.number(), nominal: z.string() }) }) .refine( data => diff --git a/rsconcept/frontend/src/features/rsform/models/language.ts b/rsconcept/frontend/src/features/rsform/models/language.ts index d9cb3e6b..c01de1a9 100644 --- a/rsconcept/frontend/src/features/rsform/models/language.ts +++ b/rsconcept/frontend/src/features/rsform/models/language.ts @@ -268,11 +268,11 @@ export interface ITextPosition { finish: number; } -export const schemaReference = z.object({ +export const schemaReference = z.strictObject({ type: z.nativeEnum(ReferenceType), data: z.union([ - z.object({ entity: z.string(), form: z.string() }), - z.object({ offset: z.number(), nominal: z.string() }) + z.strictObject({ entity: z.string(), form: z.string() }), + z.strictObject({ offset: z.number(), nominal: z.string() }) ]) }); diff --git a/rsconcept/frontend/src/features/rsform/pages/RSFormPage/RSFormPage.tsx b/rsconcept/frontend/src/features/rsform/pages/RSFormPage/RSFormPage.tsx index ad312acf..aa621310 100644 --- a/rsconcept/frontend/src/features/rsform/pages/RSFormPage/RSFormPage.tsx +++ b/rsconcept/frontend/src/features/rsform/pages/RSFormPage/RSFormPage.tsx @@ -19,7 +19,7 @@ import { ConstituentaTooltip } from '../../components/ConstituentaTooltip'; import { RSEditState, RSTabID } from './RSEditContext'; import { RSTabs } from './RSTabs'; -const paramsSchema = z.object({ +const paramsSchema = z.strictObject({ id: z.coerce.number(), version: z.coerce .number() diff --git a/rsconcept/frontend/src/features/users/backend/types.ts b/rsconcept/frontend/src/features/users/backend/types.ts index ead246b6..46ec7b8e 100644 --- a/rsconcept/frontend/src/features/users/backend/types.ts +++ b/rsconcept/frontend/src/features/users/backend/types.ts @@ -22,7 +22,7 @@ export type IUserSignupDTO = z.infer; export type IUpdateProfileDTO = z.infer; // ========= SCHEMAS ======== -export const schemaUser = z.object({ +export const schemaUser = z.strictObject({ id: z.coerce.number(), username: z.string().nonempty(errorMsg.requiredField), is_staff: z.boolean(), @@ -47,7 +47,7 @@ export const schemaUserSignup = z }) .refine(schema => schema.password === schema.password2, { path: ['password2'], message: errorMsg.passwordsMismatch }); -export const schemaUpdateProfile = z.object({ +export const schemaUpdateProfile = z.strictObject({ email: z.string().email(errorMsg.emailField), first_name: z.string(), last_name: z.string() diff --git a/rsconcept/frontend/tsconfig.node.json b/rsconcept/frontend/tsconfig.node.json index f1de0abc..1febc5ec 100644 --- a/rsconcept/frontend/tsconfig.node.json +++ b/rsconcept/frontend/tsconfig.node.json @@ -4,7 +4,12 @@ "skipLibCheck": true, "module": "ESNext", "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + + "baseUrl": "./src/", + "paths": { + "@/*": ["*"] + } }, - "include": ["vite.config.ts", "package.json", "playwright.config.ts", "tests"] + "include": ["vite.config.ts", "package.json", "playwright.config.ts", "tests", "src"] }