@venizia/ignis-docs 0.0.1 → 0.0.3

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.
@@ -15,7 +15,7 @@ This package provides:
15
15
  - **Prettier settings** - Consistent formatting across all Ignis projects
16
16
  - **TypeScript configs** - Shared base and common configurations
17
17
 
18
- ## Prettier Configuration
18
+ ### Prettier Configuration
19
19
 
20
20
  Automatic code formatting eliminates style debates.
21
21
 
@@ -50,9 +50,7 @@ bun run prettier:cli # Check formatting
50
50
  bun run prettier:fix # Auto-fix
51
51
  ```
52
52
 
53
- **IDE Integration:** Configure your editor to format on save.
54
-
55
- ## ESLint Configuration
53
+ ### ESLint Configuration
56
54
 
57
55
  Prevents common errors and enforces best practices.
58
56
 
@@ -88,9 +86,7 @@ bun run eslint --fix # Auto-fix issues
88
86
  bun run lint:fix # Run both ESLint + Prettier
89
87
  ```
90
88
 
91
- **Pre-commit Hook:** Add ESLint to your pre-commit hooks to catch issues before committing.
92
-
93
- ## TypeScript Configuration
89
+ ### TypeScript Configuration
94
90
 
95
91
  Use the centralized TypeScript configs:
96
92
 
@@ -121,6 +117,548 @@ Use the centralized TypeScript configs:
121
117
 
122
118
  See [`@venizia/dev-configs` documentation](../../references/src-details/dev-configs.md) for full details.
123
119
 
120
+ ## Directory Structure
121
+
122
+ ### Component Organization
123
+
124
+ ```
125
+ src/components/[feature]/
126
+ ├── index.ts # Barrel exports
127
+ ├── component.ts # IoC binding setup
128
+ ├── controller.ts # Route handlers
129
+ └── common/
130
+ ├── index.ts # Barrel exports
131
+ ├── keys.ts # Binding key constants
132
+ ├── types.ts # Interfaces and types
133
+ └── rest-paths.ts # Route path constants
134
+ ```
135
+
136
+ ### Complex Component (with multiple features)
137
+
138
+ ```
139
+ src/components/auth/
140
+ ├── index.ts
141
+ ├── authenticate/
142
+ │ ├── index.ts
143
+ │ ├── component.ts
144
+ │ ├── common/
145
+ │ ├── controllers/
146
+ │ ├── services/
147
+ │ └── strategies/
148
+ └── models/
149
+ ├── entities/ # Database models
150
+ └── requests/ # Request schemas
151
+ ```
152
+
153
+ ### Barrel Exports
154
+
155
+ Every folder should have an `index.ts` that re-exports its contents:
156
+
157
+ ```typescript
158
+ // components/health-check/index.ts
159
+ export * from './common';
160
+ export * from './component';
161
+ export * from './controller';
162
+
163
+ // components/health-check/common/index.ts
164
+ export * from './keys';
165
+ export * from './rest-paths';
166
+ export * from './types';
167
+ ```
168
+
169
+ ## Naming Conventions
170
+
171
+ ### Class Names
172
+
173
+ | Type | Pattern | Example |
174
+ |------|---------|---------|
175
+ | Components | `[Feature]Component` | `HealthCheckComponent`, `AuthComponent` |
176
+ | Controllers | `[Feature]Controller` | `UserController`, `AuthController` |
177
+ | Services | `[Feature]Service` | `JWTTokenService`, `PaymentService` |
178
+ | Repositories | `[Feature]Repository` | `UserRepository`, `OrderRepository` |
179
+ | Strategies | `[Feature]Strategy` | `JWTAuthenticationStrategy` |
180
+ | Factories | `[Feature]Factory` | `UIProviderFactory` |
181
+
182
+ ### File Names
183
+
184
+ Both styles are acceptable: `[type].ts` or `[name].[type].ts`
185
+
186
+ | Type | Single File | Multiple Files |
187
+ |------|-------------|----------------|
188
+ | Components | `component.ts` | `auth.component.ts` |
189
+ | Controllers | `controller.ts` | `user.controller.ts` |
190
+ | Services | `service.ts` | `jwt-token.service.ts` |
191
+ | Repositories | `repository.ts` | `user.repository.ts` |
192
+ | Types/Interfaces | `types.ts` | `user.types.ts` |
193
+ | Constants | `constants.ts` | `keys.ts`, `rest-paths.ts` |
194
+ | Schemas | `schema.ts` | `sign-in.schema.ts` |
195
+
196
+ **Guidelines:**
197
+ - Use `[type].ts` when there's only one file of that type in the folder
198
+ - Use `[name].[type].ts` when there are multiple files of the same type
199
+ - Use kebab-case for multi-word names: `jwt-token.service.ts`
200
+
201
+ ### Type and Interface Prefixes
202
+
203
+ ```typescript
204
+ // Interfaces use 'I' prefix
205
+ interface IHealthCheckOptions {
206
+ restOptions: { path: string };
207
+ }
208
+
209
+ interface IAuthService {
210
+ signIn(context: Context): Promise<void>;
211
+ }
212
+
213
+ // Type aliases use 'T' prefix
214
+ type TSignInRequest = z.infer<typeof SignInRequestSchema>;
215
+ type TRouteContext = Context<Env, Path, Input>;
216
+
217
+ // Generic constraints
218
+ type TTableSchemaWithId = { id: PgColumn };
219
+ ```
220
+
221
+ ### Binding Keys
222
+
223
+ Use static class with `@app/[component]/[feature]` format:
224
+
225
+ ```typescript
226
+ export class HealthCheckBindingKeys {
227
+ static readonly HEALTH_CHECK_OPTIONS = '@app/health-check/options';
228
+ }
229
+
230
+ export class SocketIOBindingKeys {
231
+ static readonly SOCKET_IO_INSTANCE = '@app/socket-io/instance';
232
+ static readonly SERVER_OPTIONS = '@app/socket-io/server-options';
233
+ }
234
+ ```
235
+
236
+ ## Type Safety
237
+
238
+ To ensure long-term maintainability and catch errors at compile-time, Ignis enforces strict type safety.
239
+
240
+ ### Avoid `any` and `unknown`
241
+
242
+ **Never use `any` or `unknown` as much as possible.** You must specify clear, descriptive types for all variables, parameters, and return values.
243
+
244
+ - **`any`**: Bypasses all type checking and leads to "runtime surprises". It is strictly discouraged.
245
+ - **`unknown`**: While safer than `any`, it still forces consumers to perform manual type checking. Prefer using generics or specific interfaces.
246
+
247
+ **Why?**
248
+ - **Maintenance**: Developers reading your code in the future will know exactly what the data structure is.
249
+ - **Refactoring**: Changing an interface automatically highlights all broken code across the monorepo.
250
+ - **Documentation**: Types act as a self-documenting contract for your APIs and services.
251
+
252
+ ## Type Definitions
253
+
254
+ ### Explicit Return Types
255
+ Always define explicit return types for **public methods** and **API handlers**.
256
+
257
+ **Why?**
258
+ - **Compiler Performance:** Speeds up TypeScript type checking in large projects.
259
+ - **Safety:** Prevents accidental exposure of internal types or sensitive data.
260
+
261
+ ```typescript
262
+ // ✅ GOOD
263
+ public async findUser(id: string): Promise<User | null> { ... }
264
+
265
+ // ❌ BAD (Implicit inference)
266
+ public async findUser(id: string) { ... }
267
+ ```
268
+
269
+ ## Type Inference Patterns
270
+
271
+ ### Zod Schema to Type
272
+
273
+ ```typescript
274
+ // Define schema
275
+ export const SignInRequestSchema = z.object({
276
+ email: z.string().email(),
277
+ password: z.string().min(8),
278
+ });
279
+
280
+ // Infer type from schema
281
+ export type TSignInRequest = z.infer<typeof SignInRequestSchema>;
282
+ ```
283
+
284
+ ### Const Assertion for Literal Types
285
+
286
+ ```typescript
287
+ const ROUTE_CONFIGS = {
288
+ '/users': { method: 'GET', path: '/users' },
289
+ '/users/:id': { method: 'GET', path: '/users/:id' },
290
+ } as const;
291
+
292
+ // Type is now narrowed to literal values
293
+ type RouteKey = keyof typeof ROUTE_CONFIGS; // '/users' | '/users/:id'
294
+ ```
295
+
296
+ ### Generic Type Constraints
297
+
298
+ ```typescript
299
+ export class DefaultCRUDRepository<
300
+ Schema extends TTableSchemaWithId = TTableSchemaWithId
301
+ > {
302
+ // Schema is constrained to have an 'id' column
303
+ }
304
+
305
+ export interface IAuthService<
306
+ SIRQ extends TSignInRequest = TSignInRequest,
307
+ SIRS = AnyObject,
308
+ > {
309
+ signIn(context: Context, opts: SIRQ): Promise<SIRS>;
310
+ }
311
+ ```
312
+
313
+ ## Module Exports
314
+
315
+ ### Prefer Named Exports
316
+ Avoid `export default` except for configuration files (e.g., `eslint.config.mjs`) or lazy-loaded components. Use named exports for all classes, functions, and constants.
317
+
318
+ **Why?**
319
+ - **Refactoring:** Renaming a symbol automatically updates imports across the monorepo.
320
+ - **Consistency:** Enforces consistent naming across all files importing the module.
321
+
322
+ ```typescript
323
+ // ✅ GOOD
324
+ export class UserController { ... }
325
+
326
+ // ❌ BAD
327
+ export default class UserController { ... }
328
+ ```
329
+
330
+ ## Function Signatures
331
+
332
+ ### The Options Object Pattern
333
+ Prefer using a single object parameter (`opts`) over multiple positional arguments, especially for constructors and public methods with more than 2 arguments.
334
+
335
+ **Why?**
336
+ - **Extensibility:** You can add new properties without breaking existing calls.
337
+ - **Readability:** Named keys act as documentation at the call site.
338
+
339
+ ```typescript
340
+ // ✅ GOOD
341
+ class UserService {
342
+ createUser(opts: { name: string; email: string; role?: string }) { ... }
343
+ }
344
+ // Usage: service.createUser({ name: 'John', email: 'john@example.com' });
345
+
346
+ // ❌ BAD
347
+ class UserService {
348
+ createUser(name: string, email: string, role?: string) { ... }
349
+ }
350
+ // Usage: service.createUser('John', 'john@example.com');
351
+ ```
352
+
353
+ ## Route Definition Patterns
354
+
355
+ Ignis supports three methods for defining routes. Choose based on your needs:
356
+
357
+ ### Method 1: Config-Driven Routes
358
+
359
+ Define route configurations as constants:
360
+
361
+ ```typescript
362
+ // common/rest-paths.ts
363
+ export class UserRestPaths {
364
+ static readonly ROOT = '/';
365
+ static readonly BY_ID = '/:id';
366
+ static readonly PROFILE = '/profile';
367
+ }
368
+
369
+ // common/route-configs.ts
370
+ export const ROUTE_CONFIGS = {
371
+ [UserRestPaths.ROOT]: {
372
+ method: HTTP.Methods.GET,
373
+ path: UserRestPaths.ROOT,
374
+ responses: jsonResponse({
375
+ [HTTP.ResultCodes.RS_2.Ok]: UserListSchema,
376
+ }),
377
+ },
378
+ [UserRestPaths.BY_ID]: {
379
+ method: HTTP.Methods.GET,
380
+ path: UserRestPaths.BY_ID,
381
+ request: {
382
+ params: z.object({ id: z.string().uuid() }),
383
+ },
384
+ responses: jsonResponse({
385
+ [HTTP.ResultCodes.RS_2.Ok]: UserSchema,
386
+ [HTTP.ResultCodes.RS_4.NotFound]: ErrorSchema,
387
+ }),
388
+ },
389
+ } as const;
390
+ ```
391
+
392
+ ### Method 2: Using `@api` Decorator
393
+
394
+ ```typescript
395
+ @controller({ path: '/users' })
396
+ export class UserController extends BaseController {
397
+
398
+ @api({ configs: ROUTE_CONFIGS[UserRestPaths.ROOT] })
399
+ list(context: TRouteContext<typeof ROUTE_CONFIGS[typeof UserRestPaths.ROOT]>) {
400
+ return context.json({ users: [] }, HTTP.ResultCodes.RS_2.Ok);
401
+ }
402
+
403
+ @api({ configs: ROUTE_CONFIGS[UserRestPaths.BY_ID] })
404
+ getById(context: TRouteContext<typeof ROUTE_CONFIGS[typeof UserRestPaths.BY_ID]>) {
405
+ const { id } = context.req.valid('param');
406
+ return context.json({ id, name: 'User' }, HTTP.ResultCodes.RS_2.Ok);
407
+ }
408
+ }
409
+ ```
410
+
411
+ ### Method 3: Using `bindRoute` (Programmatic)
412
+
413
+ ```typescript
414
+ @controller({ path: '/health' })
415
+ export class HealthCheckController extends BaseController {
416
+ constructor() {
417
+ super({ scope: HealthCheckController.name });
418
+
419
+ this.bindRoute({ configs: ROUTE_CONFIGS['/'] }).to({
420
+ handler: context => context.json({ status: 'ok' }),
421
+ });
422
+ }
423
+ }
424
+ ```
425
+
426
+ ### Method 4: Using `defineRoute` (Inline)
427
+
428
+ ```typescript
429
+ @controller({ path: '/health' })
430
+ export class HealthCheckController extends BaseController {
431
+ constructor() {
432
+ super({ scope: HealthCheckController.name });
433
+
434
+ this.defineRoute({
435
+ configs: ROUTE_CONFIGS['/ping'],
436
+ handler: context => {
437
+ const { message } = context.req.valid('json');
438
+ return context.json({ echo: message }, HTTP.ResultCodes.RS_2.Ok);
439
+ },
440
+ });
441
+ }
442
+ }
443
+ ```
444
+
445
+ ### OpenAPI Schema Integration
446
+
447
+ Use Zod with `.openapi()` for automatic documentation:
448
+
449
+ ```typescript
450
+ const CreateUserSchema = z.object({
451
+ email: z.string().email(),
452
+ name: z.string().min(1).max(100),
453
+ }).openapi({
454
+ description: 'Create user request body',
455
+ example: { email: 'user@example.com', name: 'John Doe' },
456
+ });
457
+
458
+ const UserSchema = z.object({
459
+ id: z.string().uuid(),
460
+ email: z.string().email(),
461
+ name: z.string(),
462
+ createdAt: z.string().datetime(),
463
+ }).openapi({
464
+ description: 'User response',
465
+ });
466
+ ```
467
+
468
+ ## Constants Pattern
469
+
470
+ **Prefer static classes over enums** for better tree-shaking and extensibility.
471
+
472
+ ### Basic Constants
473
+
474
+ ```typescript
475
+ export class Authentication {
476
+ static readonly STRATEGY_BASIC = 'basic';
477
+ static readonly STRATEGY_JWT = 'jwt';
478
+ static readonly TYPE_BEARER = 'Bearer';
479
+ }
480
+
481
+ export class HealthCheckRestPaths {
482
+ static readonly ROOT = '/';
483
+ static readonly PING = '/ping';
484
+ static readonly METRICS = '/metrics';
485
+ }
486
+ ```
487
+
488
+ ### Typed Constants with Validation
489
+
490
+ For constants that need type extraction and runtime validation, use this pattern:
491
+
492
+ ```typescript
493
+ import { TConstValue } from '@venizia/ignis-helpers';
494
+
495
+ export class DocumentUITypes {
496
+ // 1. Define static readonly values
497
+ static readonly SWAGGER = 'swagger';
498
+ static readonly SCALAR = 'scalar';
499
+
500
+ // 2. Create a Set for O(1) validation lookup
501
+ static readonly SCHEME_SET = new Set([this.SWAGGER, this.SCALAR]);
502
+
503
+ // 3. Validation helper method
504
+ static isValid(value: string): boolean {
505
+ return this.SCHEME_SET.has(value);
506
+ }
507
+ }
508
+
509
+ // 4. Extract union type from class values
510
+ export type TDocumentUIType = TConstValue<typeof DocumentUITypes>;
511
+ // Result: 'swagger' | 'scalar'
512
+ ```
513
+
514
+ **Full Example with Usage:**
515
+
516
+ ```typescript
517
+ import { TConstValue } from '@venizia/ignis-helpers';
518
+
519
+ export class UserStatuses {
520
+ static readonly ACTIVE = 'active';
521
+ static readonly INACTIVE = 'inactive';
522
+ static readonly PENDING = 'pending';
523
+ static readonly BANNED = 'banned';
524
+
525
+ static readonly SCHEME_SET = new Set([
526
+ this.ACTIVE,
527
+ this.INACTIVE,
528
+ this.PENDING,
529
+ this.BANNED,
530
+ ]);
531
+
532
+ static isValid(value: string): boolean {
533
+ return this.SCHEME_SET.has(value);
534
+ }
535
+
536
+ // Optional: get all values as array
537
+ static values(): string[] {
538
+ return [...this.SCHEME_SET];
539
+ }
540
+ }
541
+
542
+ // Type-safe union type
543
+ export type TUserStatus = TConstValue<typeof UserStatuses>;
544
+ // Result: 'active' | 'inactive' | 'pending' | 'banned'
545
+
546
+ // Usage in interfaces
547
+ interface IUser {
548
+ id: string;
549
+ status: TUserStatus; // Type-safe!
550
+ }
551
+
552
+ // Usage with validation
553
+ function updateUserStatus(userId: string, status: string) {
554
+ if (!UserStatuses.isValid(status)) {
555
+ throw getError({
556
+ statusCode: HTTP.ResultCodes.RS_4.BadRequest,
557
+ message: `Invalid status: ${status}. Valid: ${UserStatuses.values().join(', ')}`,
558
+ });
559
+ }
560
+ // status is validated at runtime
561
+ }
562
+ ```
563
+
564
+ ### Enum vs Static Class Comparison
565
+
566
+ | Aspect | Static Class | TypeScript Enum |
567
+ |--------|--------------|-----------------|
568
+ | Tree-shaking | Full support | Partial (IIFE blocks it) |
569
+ | Bundle size | Minimal | Larger (IIFE wrapper) |
570
+ | Runtime validation | O(1) with `Set` | O(n) with `Object.values()` |
571
+ | Type extraction | `TConstValue<typeof X>` → values | `keyof typeof X` → keys (not values!) |
572
+ | Add methods | Yes | Not possible |
573
+ | Compiled output | Clean class | IIFE wrapper |
574
+
575
+ **Compiled JavaScript:**
576
+
577
+ ```typescript
578
+ // Enum compiles to IIFE (not tree-shakable)
579
+ var UserStatus;
580
+ (function (UserStatus) {
581
+ UserStatus["ACTIVE"] = "active";
582
+ })(UserStatus || (UserStatus = {}));
583
+
584
+ // Static class compiles cleanly
585
+ class UserStatuses { }
586
+ UserStatuses.ACTIVE = 'active';
587
+ ```
588
+
589
+ **Type Extraction Difference:**
590
+
591
+ ```typescript
592
+ // Enum - extracts KEYS
593
+ type T = keyof typeof UserStatus; // 'ACTIVE' | 'INACTIVE'
594
+
595
+ // Static Class - extracts VALUES
596
+ type T = TConstValue<typeof UserStatuses>; // 'active' | 'inactive'
597
+ ```
598
+
599
+ **When to use `const enum`:** Only for numeric flags with no iteration needed (values are inlined, zero runtime). But doesn't work with `--isolatedModules`.
600
+
601
+ **Verdict:** Use Static Class for 90% of cases - better tree-shaking, easy validation, type-safe values, extensible with methods.
602
+
603
+ ## Configuration Patterns
604
+
605
+ ### Default Options
606
+
607
+ Every configurable class should define `DEFAULT_OPTIONS`:
608
+
609
+ ```typescript
610
+ const DEFAULT_OPTIONS: IHealthCheckOptions = {
611
+ restOptions: { path: '/health' },
612
+ };
613
+
614
+ const DEFAULT_SERVER_OPTIONS: Partial<IServerOptions> = {
615
+ identifier: 'SOCKET_IO_SERVER',
616
+ path: '/io',
617
+ cors: {
618
+ origin: '*',
619
+ methods: ['GET', 'POST'],
620
+ },
621
+ };
622
+ ```
623
+
624
+ ### Option Merging
625
+
626
+ ```typescript
627
+ // In component constructor or binding
628
+ const extraOptions = this.application.get<Partial<IServerOptions>>({
629
+ key: BindingKeys.SERVER_OPTIONS,
630
+ isOptional: true,
631
+ }) ?? {};
632
+
633
+ this.options = Object.assign({}, DEFAULT_OPTIONS, extraOptions);
634
+ ```
635
+
636
+ ### Constructor Validation
637
+
638
+ Validate required options in the constructor:
639
+
640
+ ```typescript
641
+ constructor(options: IJWTTokenServiceOptions) {
642
+ super({ scope: JWTTokenService.name });
643
+
644
+ if (!options.jwtSecret) {
645
+ throw getError({
646
+ statusCode: HTTP.ResultCodes.RS_5.InternalServerError,
647
+ message: '[JWTTokenService] Invalid jwtSecret',
648
+ });
649
+ }
650
+
651
+ if (!options.applicationSecret) {
652
+ throw getError({
653
+ statusCode: HTTP.ResultCodes.RS_5.InternalServerError,
654
+ message: '[JWTTokenService] Invalid applicationSecret',
655
+ });
656
+ }
657
+
658
+ this.options = options;
659
+ }
660
+ ```
661
+
124
662
  ## Environment Variables Management
125
663
 
126
664
  Avoid using `process.env` directly in your business logic. Instead, use the `applicationEnvironment` helper and define your keys as constants. This ensures type safety and centralized management.
@@ -143,11 +681,36 @@ const stripeKey = applicationEnvironment.get<string>(EnvironmentKeys.APP_ENV_STR
143
681
  const retries = applicationEnvironment.get<number>(EnvironmentKeys.APP_ENV_MAX_RETRIES);
144
682
  ```
145
683
 
684
+ ## Logging Patterns
685
+
686
+ ### Method Context Prefix
687
+
688
+ Always include class and method context in log messages:
689
+
690
+ ```typescript
691
+ // Format: [ClassName][methodName] Message with %s placeholders
692
+ this.logger.info('[binding] Asset storage bound | Key: %s | Type: %s', key, storageType);
693
+ this.logger.debug('[authenticate] Token validated | User: %s', userId);
694
+ this.logger.warn('[register] Skipping duplicate registration | Type: %s', opts.type);
695
+ this.logger.error('[generate] Token generation failed | Error: %s', error.message);
696
+ ```
697
+
698
+ ### Structured Data
699
+
700
+ Use format specifiers for structured logging:
701
+
702
+ ```typescript
703
+ // %s - string, %d - number, %j - JSON object
704
+ this.logger.info('[create] User created | ID: %s | Email: %s', user.id, user.email);
705
+ this.logger.debug('[config] Server options: %j', this.serverOptions);
706
+ ```
707
+
146
708
  ## Standardized Error Handling
147
709
 
148
710
  Use the `getError` helper and `HTTP` constants to throw consistent, formatted exceptions that the framework's error handler can process correctly.
149
711
 
150
- **Example:**
712
+ ### Basic Error
713
+
151
714
  ```typescript
152
715
  import { getError, HTTP } from '@venizia/ignis';
153
716
 
@@ -155,9 +718,87 @@ if (!record) {
155
718
  throw getError({
156
719
  statusCode: HTTP.ResultCodes.RS_4.NotFound,
157
720
  message: 'Record not found',
158
- // Optional details
159
- details: { id: requestedId }
721
+ details: { id: requestedId },
160
722
  });
161
723
  }
162
724
  ```
163
725
 
726
+ ### Error with Context
727
+
728
+ Include class/method context in error messages:
729
+
730
+ ```typescript
731
+ // Format: [ClassName][methodName] Descriptive message
732
+ throw getError({
733
+ statusCode: HTTP.ResultCodes.RS_5.InternalServerError,
734
+ message: '[JWTTokenService][generate] Failed to generate token',
735
+ });
736
+
737
+ throw getError({
738
+ statusCode: HTTP.ResultCodes.RS_4.Unauthorized,
739
+ message: '[AuthMiddleware][authenticate] Missing authorization header',
740
+ });
741
+ ```
742
+
743
+ ### Validation Errors
744
+
745
+ ```typescript
746
+ constructor(options: IServiceOptions) {
747
+ if (!options.apiKey) {
748
+ throw getError({
749
+ statusCode: HTTP.ResultCodes.RS_5.InternalServerError,
750
+ message: '[PaymentService] Missing required apiKey configuration',
751
+ });
752
+ }
753
+ }
754
+ ```
755
+
756
+ ### HTTP Status Code Categories
757
+
758
+ | Category | Constant | Use Case |
759
+ |----------|----------|----------|
760
+ | Success | `HTTP.ResultCodes.RS_2.Ok` | Successful response |
761
+ | Created | `HTTP.ResultCodes.RS_2.Created` | Resource created |
762
+ | Bad Request | `HTTP.ResultCodes.RS_4.BadRequest` | Invalid input |
763
+ | Unauthorized | `HTTP.ResultCodes.RS_4.Unauthorized` | Missing/invalid auth |
764
+ | Forbidden | `HTTP.ResultCodes.RS_4.Forbidden` | Insufficient permissions |
765
+ | Not Found | `HTTP.ResultCodes.RS_4.NotFound` | Resource not found |
766
+ | Internal Error | `HTTP.ResultCodes.RS_5.InternalServerError` | Server errors |
767
+
768
+ ## Scope Naming
769
+
770
+ Every class extending a base class should set its scope using `ClassName.name`:
771
+
772
+ ```typescript
773
+ export class JWTTokenService extends BaseService {
774
+ constructor() {
775
+ super({ scope: JWTTokenService.name });
776
+ }
777
+ }
778
+
779
+ export class UserController extends BaseController {
780
+ constructor() {
781
+ super({ scope: UserController.name });
782
+ }
783
+ }
784
+ ```
785
+
786
+ ## Summary Table
787
+
788
+ | Aspect | Standard |
789
+ |--------|----------|
790
+ | Interface prefix | `I` (e.g., `IUserService`) |
791
+ | Type alias prefix | `T` (e.g., `TUserRequest`) |
792
+ | Class naming | PascalCase with suffix (e.g., `UserController`) |
793
+ | File naming | kebab-case (e.g., `user.controller.ts`) |
794
+ | Binding keys | `@app/[component]/[feature]` |
795
+ | Constants | Static readonly class (not enums) |
796
+ | Barrel exports | `index.ts` at every folder level |
797
+ | Error format | `[ClassName][method] Message` |
798
+ | Logging format | `[method] Message \| Key: %s` |
799
+ | Default options | `DEFAULT_OPTIONS` constant |
800
+ | Type safety | No `any` or `unknown` allowed |
801
+ | Scope naming | `ClassName.name` |
802
+ | Arguments | Options object (`opts`) |
803
+ | Exports | Named exports only |
804
+ | Return types | Explicitly defined |