@lenne.tech/nest-server 11.7.3 → 11.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/config.env.js +3 -0
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/interfaces/server-options.interface.d.ts +35 -0
  4. package/dist/core/modules/auth/guards/roles.guard.js +4 -3
  5. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  6. package/dist/core/modules/auth/services/core-auth.service.js +5 -4
  7. package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
  8. package/dist/core/modules/error-code/core-error-code.controller.d.ts +7 -0
  9. package/dist/core/modules/error-code/core-error-code.controller.js +45 -0
  10. package/dist/core/modules/error-code/core-error-code.controller.js.map +1 -0
  11. package/dist/core/modules/error-code/core-error-code.service.d.ts +16 -0
  12. package/dist/core/modules/error-code/core-error-code.service.js +65 -0
  13. package/dist/core/modules/error-code/core-error-code.service.js.map +1 -0
  14. package/dist/core/modules/error-code/error-code.module.d.ts +7 -0
  15. package/dist/core/modules/error-code/error-code.module.js +64 -0
  16. package/dist/core/modules/error-code/error-code.module.js.map +1 -0
  17. package/dist/core/modules/error-code/error-codes.d.ts +219 -0
  18. package/dist/core/modules/error-code/error-codes.js +204 -0
  19. package/dist/core/modules/error-code/error-codes.js.map +1 -0
  20. package/dist/core/modules/error-code/index.d.ts +5 -0
  21. package/dist/core/modules/error-code/index.js +22 -0
  22. package/dist/core/modules/error-code/index.js.map +1 -0
  23. package/dist/core/modules/error-code/interfaces/error-code.interfaces.d.ts +12 -0
  24. package/dist/core/modules/error-code/interfaces/error-code.interfaces.js +3 -0
  25. package/dist/core/modules/error-code/interfaces/error-code.interfaces.js.map +1 -0
  26. package/dist/core/modules/file/core-file.controller.d.ts +1 -0
  27. package/dist/core/modules/file/core-file.controller.js +22 -0
  28. package/dist/core/modules/file/core-file.controller.js.map +1 -1
  29. package/dist/core/modules/tus/core-tus.controller.d.ts +9 -0
  30. package/dist/core/modules/tus/core-tus.controller.js +85 -0
  31. package/dist/core/modules/tus/core-tus.controller.js.map +1 -0
  32. package/dist/core/modules/tus/core-tus.service.d.ts +30 -0
  33. package/dist/core/modules/tus/core-tus.service.js +284 -0
  34. package/dist/core/modules/tus/core-tus.service.js.map +1 -0
  35. package/dist/core/modules/tus/index.d.ts +4 -0
  36. package/dist/core/modules/tus/index.js +21 -0
  37. package/dist/core/modules/tus/index.js.map +1 -0
  38. package/dist/core/modules/tus/interfaces/tus-config.interface.d.ts +10 -0
  39. package/dist/core/modules/tus/interfaces/tus-config.interface.js +59 -0
  40. package/dist/core/modules/tus/interfaces/tus-config.interface.js.map +1 -0
  41. package/dist/core/modules/tus/tus.module.d.ts +21 -0
  42. package/dist/core/modules/tus/tus.module.js +99 -0
  43. package/dist/core/modules/tus/tus.module.js.map +1 -0
  44. package/dist/core.module.js +8 -0
  45. package/dist/core.module.js.map +1 -1
  46. package/dist/index.d.ts +2 -0
  47. package/dist/index.js +2 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/server/modules/error-code/error-code.controller.d.ts +8 -0
  50. package/dist/server/modules/error-code/error-code.controller.js +55 -0
  51. package/dist/server/modules/error-code/error-code.controller.js.map +1 -0
  52. package/dist/server/modules/error-code/error-code.service.d.ts +4 -0
  53. package/dist/server/modules/error-code/error-code.service.js +27 -0
  54. package/dist/server/modules/error-code/error-code.service.js.map +1 -0
  55. package/dist/server/modules/error-code/error-codes.d.ts +45 -0
  56. package/dist/server/modules/error-code/error-codes.js +24 -0
  57. package/dist/server/modules/error-code/error-codes.js.map +1 -0
  58. package/dist/server/modules/error-code/index.d.ts +3 -0
  59. package/dist/server/modules/error-code/index.js +20 -0
  60. package/dist/server/modules/error-code/index.js.map +1 -0
  61. package/dist/server/modules/file/file.controller.d.ts +5 -7
  62. package/dist/server/modules/file/file.controller.js +3 -31
  63. package/dist/server/modules/file/file.controller.js.map +1 -1
  64. package/dist/server/server.module.js +10 -1
  65. package/dist/server/server.module.js.map +1 -1
  66. package/dist/tsconfig.build.tsbuildinfo +1 -1
  67. package/package.json +5 -2
  68. package/src/config.env.ts +4 -0
  69. package/src/core/common/interfaces/server-options.interface.ts +243 -0
  70. package/src/core/modules/auth/guards/roles.guard.ts +5 -4
  71. package/src/core/modules/auth/services/core-auth.service.ts +5 -4
  72. package/src/core/modules/error-code/INTEGRATION-CHECKLIST.md +288 -0
  73. package/src/core/modules/error-code/core-error-code.controller.ts +54 -0
  74. package/src/core/modules/error-code/core-error-code.service.ts +135 -0
  75. package/src/core/modules/error-code/error-code.module.ts +119 -0
  76. package/src/core/modules/error-code/error-codes.ts +405 -0
  77. package/src/core/modules/error-code/index.ts +14 -0
  78. package/src/core/modules/error-code/interfaces/error-code.interfaces.ts +99 -0
  79. package/src/core/modules/file/README.md +165 -0
  80. package/src/core/modules/file/core-file.controller.ts +27 -1
  81. package/src/core/modules/tus/INTEGRATION-CHECKLIST.md +176 -0
  82. package/src/core/modules/tus/README.md +439 -0
  83. package/src/core/modules/tus/core-tus.controller.ts +88 -0
  84. package/src/core/modules/tus/core-tus.service.ts +424 -0
  85. package/src/core/modules/tus/index.ts +5 -0
  86. package/src/core/modules/tus/interfaces/tus-config.interface.ts +107 -0
  87. package/src/core/modules/tus/tus.module.ts +187 -0
  88. package/src/core.module.ts +16 -0
  89. package/src/index.ts +12 -0
  90. package/src/server/modules/error-code/README.md +131 -0
  91. package/src/server/modules/error-code/error-code.controller.ts +91 -0
  92. package/src/server/modules/error-code/error-code.service.ts +42 -0
  93. package/src/server/modules/error-code/error-codes.ts +65 -0
  94. package/src/server/modules/error-code/index.ts +8 -0
  95. package/src/server/modules/file/file.controller.ts +14 -34
  96. package/src/server/server.module.ts +15 -1
@@ -0,0 +1,54 @@
1
+ import { Controller, Get, NotFoundException, Param } from '@nestjs/common';
2
+
3
+ import { Roles } from '../../common/decorators/roles.decorator';
4
+ import { RoleEnum } from '../../common/enums/role.enum';
5
+ import { CoreErrorCodeService } from './core-error-code.service';
6
+ import { IErrorTranslationResponse, SupportedLocale } from './interfaces/error-code.interfaces';
7
+
8
+ /**
9
+ * Core Error Code Controller
10
+ *
11
+ * Provides REST endpoints for error translations.
12
+ * This controller is publicly accessible (no authentication required).
13
+ *
14
+ * @example
15
+ * GET /api/i18n/errors/de - Get German translations
16
+ * GET /api/i18n/errors/en - Get English translations
17
+ */
18
+ @Controller('api/i18n/errors')
19
+ export class CoreErrorCodeController {
20
+ constructor(protected readonly errorCodeService: CoreErrorCodeService) {}
21
+
22
+ /**
23
+ * Get error translations for a specific locale
24
+ *
25
+ * Returns all error codes with their translations in Nuxt i18n compatible format.
26
+ *
27
+ * @param locale - Locale code (e.g., 'de', 'en')
28
+ * @returns Translations object
29
+ * @throws NotFoundException if locale is not supported
30
+ *
31
+ * @example
32
+ * Response:
33
+ * ```json
34
+ * {
35
+ * "errors": {
36
+ * "LTNS_0001": "Benutzer mit E-Mail {email} wurde nicht gefunden.",
37
+ * "LTNS_0002": "Das eingegebene Passwort ist ungültig."
38
+ * }
39
+ * }
40
+ * ```
41
+ */
42
+ @Get(':locale')
43
+ @Roles(RoleEnum.S_EVERYONE)
44
+ getTranslations(@Param('locale') locale: string): IErrorTranslationResponse {
45
+ if (!this.errorCodeService.isLocaleSupported(locale)) {
46
+ throw new NotFoundException(
47
+ `Locale "${locale}" is not supported. ` +
48
+ `Supported locales: ${this.errorCodeService.getSupportedLocales().join(', ')}`,
49
+ );
50
+ }
51
+
52
+ return this.errorCodeService.getTranslations(locale as SupportedLocale);
53
+ }
54
+ }
@@ -0,0 +1,135 @@
1
+ import { Injectable } from '@nestjs/common';
2
+
3
+ import { getAllErrorDefinitions, IErrorRegistry } from './error-codes';
4
+ import { SupportedLocale } from './interfaces/error-code.interfaces';
5
+
6
+ /**
7
+ * Core Error Code Service
8
+ *
9
+ * Serves error code translations from the structured ErrorRegistry.
10
+ * Translations are defined in error-codes.ts as Single Source of Truth.
11
+ *
12
+ * Projects can extend this service to add custom error registries.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // In consuming project:
17
+ * import { CoreErrorCodeService, IErrorRegistry } from '@lenne.tech/nest-server';
18
+ *
19
+ * const ProjectErrors = {
20
+ * ORDER_NOT_FOUND: {
21
+ * code: 'PROJ_0001',
22
+ * message: 'Order not found',
23
+ * translations: { de: 'Bestellung nicht gefunden.', en: 'Order not found.' }
24
+ * }
25
+ * } as const satisfies IErrorRegistry;
26
+ *
27
+ * @Injectable()
28
+ * export class ErrorCodeService extends CoreErrorCodeService {
29
+ * constructor() {
30
+ * super();
31
+ * this.registerErrorRegistry(ProjectErrors);
32
+ * }
33
+ * }
34
+ * ```
35
+ */
36
+ @Injectable()
37
+ export class CoreErrorCodeService {
38
+ /**
39
+ * Supported locales
40
+ */
41
+ protected supportedLocales: SupportedLocale[] = ['de', 'en'];
42
+
43
+ /**
44
+ * Cached translations per locale
45
+ */
46
+ protected translations: Map<SupportedLocale, Record<string, string>> = new Map();
47
+
48
+ /**
49
+ * Registered error registries
50
+ */
51
+ protected registries: IErrorRegistry[] = [];
52
+
53
+ constructor() {
54
+ // Initialize with core errors
55
+ this.registerErrorRegistry(getAllErrorDefinitions());
56
+ }
57
+
58
+ /**
59
+ * Register an error registry and generate translations
60
+ *
61
+ * @param registry - Error registry to register
62
+ */
63
+ registerErrorRegistry(registry: IErrorRegistry): void {
64
+ this.registries.push(registry);
65
+ this.generateTranslationsFromRegistry(registry);
66
+ }
67
+
68
+ /**
69
+ * Generate translations from error registry
70
+ *
71
+ * @param registry - Error registry to extract translations from
72
+ */
73
+ protected generateTranslationsFromRegistry(registry: IErrorRegistry): void {
74
+ for (const [, definition] of Object.entries(registry)) {
75
+ const { code, translations: defTranslations } = definition;
76
+
77
+ for (const locale of this.supportedLocales) {
78
+ const translation = defTranslations[locale];
79
+ if (translation) {
80
+ const existing = this.translations.get(locale) || {};
81
+ this.translations.set(locale, { ...existing, [code]: translation });
82
+ }
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Check if a locale is supported
89
+ *
90
+ * @param locale - Locale to check
91
+ * @returns True if locale is supported
92
+ */
93
+ isLocaleSupported(locale: string): locale is SupportedLocale {
94
+ return this.supportedLocales.includes(locale as SupportedLocale);
95
+ }
96
+
97
+ /**
98
+ * Get supported locales
99
+ *
100
+ * @returns Array of supported locales
101
+ */
102
+ getSupportedLocales(): SupportedLocale[] {
103
+ return [...this.supportedLocales];
104
+ }
105
+
106
+ /**
107
+ * Get all translations for a locale
108
+ *
109
+ * @param locale - Locale code (e.g., 'de', 'en')
110
+ * @returns Translations object wrapped in { errors: ... } for Nuxt i18n compatibility
111
+ * @throws Error if locale is not supported
112
+ */
113
+ getTranslations(locale: SupportedLocale): { errors: Record<string, string> } {
114
+ if (!this.isLocaleSupported(locale)) {
115
+ throw new Error(`Locale "${locale}" is not supported. Supported: ${this.supportedLocales.join(', ')}`);
116
+ }
117
+
118
+ return { errors: this.translations.get(locale) || {} };
119
+ }
120
+
121
+ /**
122
+ * Get all error codes
123
+ *
124
+ * @returns Array of error codes from all registries
125
+ */
126
+ getErrorCodes(): string[] {
127
+ const codes = new Set<string>();
128
+ for (const translations of this.translations.values()) {
129
+ for (const code of Object.keys(translations)) {
130
+ codes.add(code);
131
+ }
132
+ }
133
+ return Array.from(codes).sort();
134
+ }
135
+ }
@@ -0,0 +1,119 @@
1
+ import { DynamicModule, Global, Module, Type } from '@nestjs/common';
2
+
3
+ import { CoreErrorCodeController } from './core-error-code.controller';
4
+ import { CoreErrorCodeService } from './core-error-code.service';
5
+ import { IErrorCodeModuleConfig } from './interfaces/error-code.interfaces';
6
+
7
+ /**
8
+ * Error Code Module
9
+ *
10
+ * Provides error code translations via REST endpoint.
11
+ * Translations are defined in the error registry (Single Source of Truth).
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // Basic usage (auto-register in CoreModule)
16
+ * // No explicit import needed - included in CoreModule
17
+ *
18
+ * // Extended usage (with custom error registry - RECOMMENDED)
19
+ * const ProjectErrors = {
20
+ * ORDER_NOT_FOUND: {
21
+ * code: 'PROJ_0001',
22
+ * message: 'Order not found',
23
+ * translations: { de: 'Bestellung nicht gefunden.', en: 'Order not found.' }
24
+ * }
25
+ * } as const satisfies IErrorRegistry;
26
+ *
27
+ * ErrorCodeModule.forRoot({ additionalErrorRegistry: ProjectErrors })
28
+ *
29
+ * // Extended usage (with custom controller and service)
30
+ * ErrorCodeModule.forRoot({
31
+ * additionalErrorRegistry: ProjectErrors,
32
+ * controller: ErrorCodeController,
33
+ * service: ErrorCodeService,
34
+ * })
35
+ * ```
36
+ */
37
+ @Global()
38
+ @Module({
39
+ // IMPORTANT: Controllers are NOT registered here - they are registered via forRoot()
40
+ // This prevents duplicate controller registration when using custom controllers,
41
+ // which would cause route conflicts (e.g., :locale intercepting /codes)
42
+ exports: [CoreErrorCodeService],
43
+ providers: [CoreErrorCodeService],
44
+ })
45
+ export class ErrorCodeModule {
46
+ /**
47
+ * Gets the controller class to use (custom or default)
48
+ */
49
+ private static getControllerClass(config?: IErrorCodeModuleConfig): Type<any> {
50
+ return config?.controller || CoreErrorCodeController;
51
+ }
52
+
53
+ /**
54
+ * Gets the service class to use (custom or default)
55
+ */
56
+ private static getServiceClass(config?: IErrorCodeModuleConfig): Type<CoreErrorCodeService> {
57
+ return config?.service || CoreErrorCodeService;
58
+ }
59
+
60
+ /**
61
+ * Register the module with configuration
62
+ *
63
+ * Supports the following patterns:
64
+ * 1. **No config**: Uses CoreErrorCodeService and CoreErrorCodeController with core errors only
65
+ * 2. **additionalErrorRegistry only**: Adds project errors to core errors
66
+ * 3. **service only**: Uses custom service class (should register its own errors in constructor)
67
+ * 4. **controller only**: Uses custom controller class with CoreErrorCodeService
68
+ * 5. **Full config**: Uses custom service and controller
69
+ *
70
+ * @param config - Module configuration
71
+ * @returns Dynamic module
72
+ */
73
+ static forRoot(config?: IErrorCodeModuleConfig): DynamicModule {
74
+ const ControllerClass = this.getControllerClass(config);
75
+ const ServiceClass = this.getServiceClass(config);
76
+
77
+ // Build providers array
78
+ const providers: any[] = [];
79
+
80
+ if (config?.service) {
81
+ // If a custom service is provided, register it under both tokens:
82
+ // 1. Its own class (for direct injection in custom controllers)
83
+ // 2. CoreErrorCodeService (for backward compatibility)
84
+ providers.push(ServiceClass);
85
+ providers.push({
86
+ provide: CoreErrorCodeService,
87
+ useExisting: ServiceClass,
88
+ });
89
+ } else {
90
+ // Use factory to register additional errors
91
+ providers.push({
92
+ provide: CoreErrorCodeService,
93
+ useFactory: () => {
94
+ const service = new CoreErrorCodeService();
95
+
96
+ // Add additional error registry
97
+ if (config?.additionalErrorRegistry) {
98
+ service.registerErrorRegistry(config.additionalErrorRegistry);
99
+ }
100
+
101
+ return service;
102
+ },
103
+ });
104
+ }
105
+
106
+ // Export both the base class and custom service class (if provided)
107
+ const exports: any[] = [CoreErrorCodeService];
108
+ if (config?.service && ServiceClass !== CoreErrorCodeService) {
109
+ exports.push(ServiceClass);
110
+ }
111
+
112
+ return {
113
+ controllers: [ControllerClass],
114
+ exports,
115
+ module: ErrorCodeModule,
116
+ providers,
117
+ };
118
+ }
119
+ }
@@ -0,0 +1,405 @@
1
+ /**
2
+ * Lenne Tech Nest Server Error Codes - Structured Registry
3
+ *
4
+ * Single Source of Truth for all error codes, messages, and translations.
5
+ * This file serves as the central registry that:
6
+ * - Defines error codes with human-readable messages
7
+ * - Provides translations for multiple languages
8
+ * - Generates the i18n JSON endpoint automatically
9
+ *
10
+ * API Response Format: #PREFIX_XXXX: Short technical description
11
+ * - # = Marker for machine parsing
12
+ * - PREFIX = LTNS (Lenne Tech Nest Server) or project-specific
13
+ * - XXXX = Unique number within range
14
+ * - : = Separator
15
+ * - Description = English developer-friendly message
16
+ *
17
+ * Error code ranges:
18
+ * - LTNS_0001-LTNS_0099: Authentication errors
19
+ * - LTNS_0100-LTNS_0199: Authorization errors
20
+ * - LTNS_0200-LTNS_0299: User errors
21
+ * - LTNS_0300-LTNS_0399: Validation errors
22
+ * - LTNS_0400-LTNS_0499: Resource errors
23
+ * - LTNS_0500-LTNS_0599: File errors
24
+ * - LTNS_0900-LTNS_0999: Internal errors
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * import { UnauthorizedException } from '@nestjs/common';
29
+ * import { ErrorCode } from '@lenne.tech/nest-server';
30
+ *
31
+ * throw new UnauthorizedException(ErrorCode.UNAUTHORIZED);
32
+ * // Response: { statusCode: 401, message: "#LTNS_0100: Unauthorized - User is not logged in" }
33
+ * ```
34
+ */
35
+
36
+ // =====================================================
37
+ // Type Definitions
38
+ // =====================================================
39
+
40
+ /**
41
+ * Structure for a single error definition
42
+ */
43
+ export interface IErrorDefinition {
44
+ /** Unique error code (e.g., LTNS_0100) */
45
+ code: string;
46
+ /** English developer message */
47
+ message: string;
48
+ /** Translations for end users */
49
+ translations: {
50
+ [locale: string]: string;
51
+ de: string;
52
+ en: string;
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Registry type for error definitions
58
+ */
59
+ export type IErrorRegistry = Record<string, IErrorDefinition>;
60
+
61
+ // =====================================================
62
+ // Core Error Registry (Single Source of Truth)
63
+ // =====================================================
64
+
65
+ /**
66
+ * LTNS Error Registry - Contains all core error definitions
67
+ *
68
+ * Each entry includes:
69
+ * - code: The unique identifier
70
+ * - message: English technical description for developers
71
+ * - translations: Localized messages for end users
72
+ *
73
+ * NOTE: Entries are grouped by error code range for maintainability.
74
+ * Do not sort alphabetically - the numeric grouping is intentional.
75
+ */
76
+ /* eslint-disable perfectionist/sort-objects */
77
+ export const LtnsErrors = {
78
+ // =====================================================
79
+ // Authentication Errors (LTNS_0001-LTNS_0099)
80
+ // =====================================================
81
+
82
+ USER_NOT_FOUND: {
83
+ code: 'LTNS_0001',
84
+ message: 'User not found',
85
+ translations: {
86
+ de: 'Benutzer wurde nicht gefunden.',
87
+ en: 'User not found.',
88
+ },
89
+ },
90
+
91
+ INVALID_PASSWORD: {
92
+ code: 'LTNS_0002',
93
+ message: 'Invalid password',
94
+ translations: {
95
+ de: 'Das eingegebene Passwort ist ungültig.',
96
+ en: 'The provided password is invalid.',
97
+ },
98
+ },
99
+
100
+ INVALID_TOKEN: {
101
+ code: 'LTNS_0003',
102
+ message: 'Invalid or malformed token',
103
+ translations: {
104
+ de: 'Der Token ist ungültig oder fehlerhaft.',
105
+ en: 'The token is invalid or malformed.',
106
+ },
107
+ },
108
+
109
+ TOKEN_EXPIRED: {
110
+ code: 'LTNS_0004',
111
+ message: 'Token has expired',
112
+ translations: {
113
+ de: 'Der Token ist abgelaufen. Bitte melden Sie sich erneut an.',
114
+ en: 'The token has expired. Please sign in again.',
115
+ },
116
+ },
117
+
118
+ REFRESH_TOKEN_REQUIRED: {
119
+ code: 'LTNS_0005',
120
+ message: 'Refresh token required',
121
+ translations: {
122
+ de: 'Ein Refresh-Token wird benötigt.',
123
+ en: 'A refresh token is required.',
124
+ },
125
+ },
126
+
127
+ USER_NOT_VERIFIED: {
128
+ code: 'LTNS_0006',
129
+ message: 'User email not verified',
130
+ translations: {
131
+ de: 'Ihre E-Mail-Adresse wurde noch nicht verifiziert.',
132
+ en: 'Your email address has not been verified yet.',
133
+ },
134
+ },
135
+
136
+ // =====================================================
137
+ // Authorization Errors (LTNS_0100-LTNS_0199)
138
+ // =====================================================
139
+
140
+ UNAUTHORIZED: {
141
+ code: 'LTNS_0100',
142
+ message: 'Unauthorized - User is not logged in',
143
+ translations: {
144
+ de: 'Sie sind nicht angemeldet.',
145
+ en: 'You are not logged in.',
146
+ },
147
+ },
148
+
149
+ ACCESS_DENIED: {
150
+ code: 'LTNS_0101',
151
+ message: 'Access denied - Insufficient permissions',
152
+ translations: {
153
+ de: 'Zugriff verweigert. Sie haben nicht die erforderlichen Berechtigungen.',
154
+ en: 'Access denied. You do not have the required permissions.',
155
+ },
156
+ },
157
+
158
+ RESOURCE_FORBIDDEN: {
159
+ code: 'LTNS_0102',
160
+ message: 'Resource access forbidden',
161
+ translations: {
162
+ de: 'Der Zugriff auf diese Ressource ist nicht erlaubt.',
163
+ en: 'Access to this resource is forbidden.',
164
+ },
165
+ },
166
+
167
+ // =====================================================
168
+ // User Errors (LTNS_0200-LTNS_0299)
169
+ // =====================================================
170
+
171
+ EMAIL_ALREADY_EXISTS: {
172
+ code: 'LTNS_0200',
173
+ message: 'Email already registered',
174
+ translations: {
175
+ de: 'Diese E-Mail-Adresse ist bereits registriert.',
176
+ en: 'This email address is already registered.',
177
+ },
178
+ },
179
+
180
+ USERNAME_ALREADY_EXISTS: {
181
+ code: 'LTNS_0201',
182
+ message: 'Username already taken',
183
+ translations: {
184
+ de: 'Dieser Benutzername ist bereits vergeben.',
185
+ en: 'This username is already taken.',
186
+ },
187
+ },
188
+
189
+ // =====================================================
190
+ // Validation Errors (LTNS_0300-LTNS_0399)
191
+ // =====================================================
192
+
193
+ VALIDATION_FAILED: {
194
+ code: 'LTNS_0300',
195
+ message: 'Validation failed',
196
+ translations: {
197
+ de: 'Die Validierung ist fehlgeschlagen.',
198
+ en: 'Validation failed.',
199
+ },
200
+ },
201
+
202
+ REQUIRED_FIELD_MISSING: {
203
+ code: 'LTNS_0301',
204
+ message: 'Required field missing',
205
+ translations: {
206
+ de: 'Ein erforderliches Feld fehlt.',
207
+ en: 'A required field is missing.',
208
+ },
209
+ },
210
+
211
+ INVALID_FIELD_FORMAT: {
212
+ code: 'LTNS_0302',
213
+ message: 'Invalid field format',
214
+ translations: {
215
+ de: 'Das Feldformat ist ungültig.',
216
+ en: 'The field format is invalid.',
217
+ },
218
+ },
219
+
220
+ // =====================================================
221
+ // Resource Errors (LTNS_0400-LTNS_0499)
222
+ // =====================================================
223
+
224
+ RESOURCE_NOT_FOUND: {
225
+ code: 'LTNS_0400',
226
+ message: 'Resource not found',
227
+ translations: {
228
+ de: 'Die angeforderte Ressource wurde nicht gefunden.',
229
+ en: 'The requested resource was not found.',
230
+ },
231
+ },
232
+
233
+ RESOURCE_ALREADY_EXISTS: {
234
+ code: 'LTNS_0401',
235
+ message: 'Resource already exists',
236
+ translations: {
237
+ de: 'Diese Ressource existiert bereits.',
238
+ en: 'This resource already exists.',
239
+ },
240
+ },
241
+
242
+ // =====================================================
243
+ // File Errors (LTNS_0500-LTNS_0599)
244
+ // =====================================================
245
+
246
+ FILE_NOT_FOUND: {
247
+ code: 'LTNS_0500',
248
+ message: 'File not found',
249
+ translations: {
250
+ de: 'Die Datei wurde nicht gefunden.',
251
+ en: 'The file was not found.',
252
+ },
253
+ },
254
+
255
+ FILE_UPLOAD_FAILED: {
256
+ code: 'LTNS_0501',
257
+ message: 'File upload failed',
258
+ translations: {
259
+ de: 'Der Datei-Upload ist fehlgeschlagen.',
260
+ en: 'The file upload failed.',
261
+ },
262
+ },
263
+
264
+ FILE_TYPE_NOT_ALLOWED: {
265
+ code: 'LTNS_0502',
266
+ message: 'File type not allowed',
267
+ translations: {
268
+ de: 'Dieser Dateityp ist nicht erlaubt.',
269
+ en: 'This file type is not allowed.',
270
+ },
271
+ },
272
+
273
+ // =====================================================
274
+ // Internal Errors (LTNS_0900-LTNS_0999)
275
+ // =====================================================
276
+
277
+ INTERNAL_ERROR: {
278
+ code: 'LTNS_0900',
279
+ message: 'Internal server error',
280
+ translations: {
281
+ de: 'Ein interner Serverfehler ist aufgetreten.',
282
+ en: 'An internal server error occurred.',
283
+ },
284
+ },
285
+
286
+ SERVICE_UNAVAILABLE: {
287
+ code: 'LTNS_0901',
288
+ message: 'Service temporarily unavailable',
289
+ translations: {
290
+ de: 'Der Dienst ist vorübergehend nicht verfügbar.',
291
+ en: 'The service is temporarily unavailable.',
292
+ },
293
+ },
294
+
295
+ LEGACY_AUTH_DISABLED: {
296
+ code: 'LTNS_0902',
297
+ message: 'Legacy authentication is disabled',
298
+ translations: {
299
+ de: 'Die Legacy-Authentifizierung ist deaktiviert. Bitte nutzen Sie die neue Authentifizierung.',
300
+ en: 'Legacy authentication is disabled. Please use the new authentication.',
301
+ },
302
+ },
303
+ } as const satisfies IErrorRegistry;
304
+ /* eslint-enable perfectionist/sort-objects */
305
+
306
+ // =====================================================
307
+ // Generated ErrorCode Object
308
+ // =====================================================
309
+
310
+ /**
311
+ * Generate formatted error message from definition
312
+ * Format: #CODE: Message
313
+ */
314
+ function formatErrorMessage(def: IErrorDefinition): string {
315
+ return `#${def.code}: ${def.message}`;
316
+ }
317
+
318
+ /**
319
+ * Generate ErrorCode object from registry
320
+ * Maps each key to its formatted error string
321
+ */
322
+ function generateErrorCodes<T extends IErrorRegistry>(registry: T): { [K in keyof T]: string } {
323
+ const result = {} as { [K in keyof T]: string };
324
+ for (const key of Object.keys(registry) as Array<keyof T>) {
325
+ result[key] = formatErrorMessage(registry[key]);
326
+ }
327
+ return result;
328
+ }
329
+
330
+ /**
331
+ * ErrorCode - Use this in your code with NestJS exceptions
332
+ *
333
+ * @example
334
+ * ```typescript
335
+ * throw new UnauthorizedException(ErrorCode.UNAUTHORIZED);
336
+ * // Response: { statusCode: 401, message: "#LTNS_0100: Unauthorized - User is not logged in" }
337
+ * ```
338
+ */
339
+ export const ErrorCode = generateErrorCodes(LtnsErrors);
340
+
341
+ // =====================================================
342
+ // Type Exports
343
+ // =====================================================
344
+
345
+ /**
346
+ * Type for error code keys (readable names like USER_NOT_FOUND)
347
+ */
348
+ export type ErrorCodeKey = keyof typeof ErrorCode;
349
+
350
+ /**
351
+ * Type for all error code formatted strings
352
+ */
353
+ export type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
354
+
355
+ /**
356
+ * Type for raw error codes (like LTNS_0100)
357
+ */
358
+ export type RawErrorCode = (typeof LtnsErrors)[keyof typeof LtnsErrors]['code'];
359
+
360
+ // =====================================================
361
+ // Helper Functions
362
+ // =====================================================
363
+
364
+ /**
365
+ * Get all error definitions (for i18n endpoint)
366
+ *
367
+ * @returns All error definitions from the registry
368
+ */
369
+ export function getAllErrorDefinitions(): typeof LtnsErrors {
370
+ return LtnsErrors;
371
+ }
372
+
373
+ // =====================================================
374
+ // Extension Support
375
+ // =====================================================
376
+
377
+ /**
378
+ * Merge project-specific errors with core errors
379
+ *
380
+ * @param projectErrors - Project-specific error registry
381
+ * @returns Merged error codes object
382
+ *
383
+ * @example
384
+ * ```typescript
385
+ * // In your project
386
+ * const ProjectErrors = {
387
+ * ORDER_NOT_FOUND: {
388
+ * code: 'PROJ_0001',
389
+ * message: 'Order not found',
390
+ * translations: { de: 'Bestellung nicht gefunden.', en: 'Order not found.' }
391
+ * }
392
+ * } as const satisfies IErrorRegistry;
393
+ *
394
+ * export const ErrorCode = mergeErrorCodes(ProjectErrors);
395
+ * // Contains both LTNS_* and PROJ_* errors
396
+ * ```
397
+ */
398
+ export function mergeErrorCodes<T extends IErrorRegistry>(
399
+ projectErrors: T,
400
+ ): { [K in keyof T | keyof typeof LtnsErrors]: string } {
401
+ return {
402
+ ...ErrorCode,
403
+ ...generateErrorCodes(projectErrors),
404
+ } as { [K in keyof T | keyof typeof LtnsErrors]: string };
405
+ }