@venizia/ignis-docs 0.0.2 → 0.0.4-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 (134) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -2
  3. package/wiki/best-practices/api-usage-examples.md +591 -0
  4. package/wiki/best-practices/architectural-patterns.md +415 -0
  5. package/wiki/best-practices/architecture-decisions.md +488 -0
  6. package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +647 -182
  7. package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
  8. package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
  9. package/wiki/best-practices/data-modeling.md +376 -0
  10. package/wiki/best-practices/deployment-strategies.md +698 -0
  11. package/wiki/best-practices/index.md +27 -0
  12. package/wiki/best-practices/performance-optimization.md +196 -0
  13. package/wiki/best-practices/security-guidelines.md +218 -0
  14. package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
  15. package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
  16. package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
  17. package/wiki/changelogs/2025-12-17-refactor.md +1 -1
  18. package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
  19. package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
  20. package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +86 -0
  21. package/wiki/changelogs/2025-12-26-transaction-support.md +57 -0
  22. package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
  23. package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
  24. package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
  25. package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
  26. package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
  27. package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
  28. package/wiki/changelogs/index.md +8 -1
  29. package/wiki/changelogs/planned-schema-migrator.md +2 -10
  30. package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
  31. package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
  32. package/wiki/guides/core-concepts/components-guide.md +509 -0
  33. package/wiki/guides/core-concepts/components.md +122 -0
  34. package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
  35. package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
  36. package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
  37. package/wiki/guides/core-concepts/persistent/index.md +119 -0
  38. package/wiki/guides/core-concepts/persistent/models.md +241 -0
  39. package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
  40. package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
  41. package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
  42. package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
  43. package/wiki/guides/get-started/philosophy.md +682 -0
  44. package/wiki/guides/get-started/setup.md +157 -0
  45. package/wiki/guides/index.md +89 -0
  46. package/wiki/guides/reference/glossary.md +243 -0
  47. package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
  48. package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
  49. package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
  50. package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
  51. package/wiki/guides/tutorials/realtime-chat.md +1261 -0
  52. package/wiki/guides/tutorials/testing.md +723 -0
  53. package/wiki/index.md +176 -37
  54. package/wiki/references/base/application.md +27 -0
  55. package/wiki/references/base/bootstrapping.md +30 -26
  56. package/wiki/references/base/components.md +532 -31
  57. package/wiki/references/base/controllers.md +136 -38
  58. package/wiki/references/base/datasources.md +108 -5
  59. package/wiki/references/base/dependency-injection.md +39 -3
  60. package/wiki/references/base/filter-system/application-usage.md +224 -0
  61. package/wiki/references/base/filter-system/array-operators.md +132 -0
  62. package/wiki/references/base/filter-system/comparison-operators.md +109 -0
  63. package/wiki/references/base/filter-system/default-filter.md +428 -0
  64. package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
  65. package/wiki/references/base/filter-system/index.md +127 -0
  66. package/wiki/references/base/filter-system/json-filtering.md +197 -0
  67. package/wiki/references/base/filter-system/list-operators.md +71 -0
  68. package/wiki/references/base/filter-system/logical-operators.md +156 -0
  69. package/wiki/references/base/filter-system/null-operators.md +58 -0
  70. package/wiki/references/base/filter-system/pattern-matching.md +108 -0
  71. package/wiki/references/base/filter-system/quick-reference.md +431 -0
  72. package/wiki/references/base/filter-system/range-operators.md +63 -0
  73. package/wiki/references/base/filter-system/tips.md +190 -0
  74. package/wiki/references/base/filter-system/use-cases.md +452 -0
  75. package/wiki/references/base/index.md +90 -0
  76. package/wiki/references/base/middlewares.md +602 -0
  77. package/wiki/references/base/models.md +215 -23
  78. package/wiki/references/base/providers.md +732 -0
  79. package/wiki/references/base/repositories/advanced.md +555 -0
  80. package/wiki/references/base/repositories/index.md +228 -0
  81. package/wiki/references/base/repositories/mixins.md +331 -0
  82. package/wiki/references/base/repositories/relations.md +486 -0
  83. package/wiki/references/base/repositories.md +40 -549
  84. package/wiki/references/base/services.md +28 -4
  85. package/wiki/references/components/authentication.md +22 -2
  86. package/wiki/references/components/health-check.md +12 -0
  87. package/wiki/references/components/index.md +23 -0
  88. package/wiki/references/components/mail.md +687 -0
  89. package/wiki/references/components/request-tracker.md +16 -0
  90. package/wiki/references/components/socket-io.md +18 -0
  91. package/wiki/references/components/static-asset.md +14 -26
  92. package/wiki/references/components/swagger.md +17 -0
  93. package/wiki/references/configuration/environment-variables.md +427 -0
  94. package/wiki/references/configuration/index.md +73 -0
  95. package/wiki/references/helpers/cron.md +14 -0
  96. package/wiki/references/helpers/crypto.md +15 -0
  97. package/wiki/references/helpers/env.md +16 -0
  98. package/wiki/references/helpers/error.md +17 -0
  99. package/wiki/references/helpers/index.md +15 -0
  100. package/wiki/references/helpers/inversion.md +24 -4
  101. package/wiki/references/helpers/logger.md +19 -0
  102. package/wiki/references/helpers/network.md +11 -0
  103. package/wiki/references/helpers/queue.md +19 -0
  104. package/wiki/references/helpers/redis.md +21 -0
  105. package/wiki/references/helpers/socket-io.md +24 -5
  106. package/wiki/references/helpers/storage.md +18 -10
  107. package/wiki/references/helpers/testing.md +18 -0
  108. package/wiki/references/helpers/types.md +167 -0
  109. package/wiki/references/helpers/uid.md +167 -0
  110. package/wiki/references/helpers/worker-thread.md +16 -0
  111. package/wiki/references/index.md +177 -0
  112. package/wiki/references/quick-reference.md +634 -0
  113. package/wiki/references/src-details/boot.md +3 -3
  114. package/wiki/references/src-details/dev-configs.md +0 -4
  115. package/wiki/references/src-details/docs.md +2 -2
  116. package/wiki/references/src-details/index.md +86 -0
  117. package/wiki/references/src-details/inversion.md +1 -6
  118. package/wiki/references/src-details/mcp-server.md +3 -15
  119. package/wiki/references/utilities/index.md +86 -10
  120. package/wiki/references/utilities/jsx.md +577 -0
  121. package/wiki/references/utilities/request.md +0 -2
  122. package/wiki/references/utilities/statuses.md +740 -0
  123. package/wiki/changelogs/planned-transaction-support.md +0 -216
  124. package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
  125. package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
  126. package/wiki/get-started/best-practices/data-modeling.md +0 -177
  127. package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
  128. package/wiki/get-started/best-practices/performance-optimization.md +0 -88
  129. package/wiki/get-started/best-practices/security-guidelines.md +0 -99
  130. package/wiki/get-started/core-concepts/components.md +0 -98
  131. package/wiki/get-started/core-concepts/persistent.md +0 -543
  132. package/wiki/get-started/index.md +0 -65
  133. package/wiki/get-started/philosophy.md +0 -296
  134. package/wiki/get-started/prerequisites.md +0 -113
@@ -1,3 +1,9 @@
1
+ ---
2
+ title: Components Reference
3
+ description: Technical reference for BaseComponent and pluggable modules
4
+ difficulty: advanced
5
+ ---
6
+
1
7
  # Deep Dive: Components
2
8
 
3
9
  Technical reference for `BaseComponent`—the foundation for creating reusable, pluggable features in Ignis. Components are powerful containers that can group together multiple providers, services, controllers, repositories, and even entire mini-applications into a single, redistributable module.
@@ -12,17 +18,220 @@ Technical reference for `BaseComponent`—the foundation for creating reusable,
12
18
  | **Lifecycle Management** | Auto-called `binding()` method during startup |
13
19
  | **Default Bindings** | Self-contained with automatic DI registration |
14
20
 
15
- ## `BaseComponent` Class
16
21
 
17
- Abstract class for all components - structures resource binding and lifecycle management.
22
+ ## Component Directory Structure
23
+
24
+ A well-organized component follows a consistent directory structure that separates concerns and makes the codebase maintainable.
25
+
26
+ ### Simple Component
27
+
28
+ ```
29
+ src/components/health-check/
30
+ ├── index.ts # Barrel exports (re-exports everything)
31
+ ├── component.ts # Component class with binding logic
32
+ ├── controller.ts # Controller class(es)
33
+ └── common/
34
+ ├── index.ts # Barrel exports for common/
35
+ ├── keys.ts # Binding key constants
36
+ ├── types.ts # Interfaces and type definitions
37
+ ├── constants.ts # Static class constants (optional)
38
+ └── rest-paths.ts # Route path constants (optional)
39
+ ```
40
+
41
+ ### Complex Component (with services, models, strategies)
42
+
43
+ ```
44
+ src/components/auth/
45
+ ├── index.ts
46
+ ├── authenticate/
47
+ │ ├── index.ts
48
+ │ ├── component.ts
49
+ │ ├── common/
50
+ │ │ ├── index.ts
51
+ │ │ ├── keys.ts
52
+ │ │ ├── types.ts
53
+ │ │ └── constants.ts
54
+ │ ├── controllers/
55
+ │ │ ├── index.ts
56
+ │ │ └── auth.controller.ts
57
+ │ ├── services/
58
+ │ │ ├── index.ts
59
+ │ │ └── jwt-token.service.ts
60
+ │ └── strategies/
61
+ │ ├── index.ts
62
+ │ ├── jwt.strategy.ts
63
+ │ └── basic.strategy.ts
64
+ └── models/
65
+ ├── index.ts
66
+ ├── entities/
67
+ │ └── user-token.model.ts
68
+ └── requests/
69
+ ├── sign-in.schema.ts
70
+ └── sign-up.schema.ts
71
+ ```
72
+
73
+
74
+ ## The `common/` Directory
75
+
76
+ The `common/` directory contains shared definitions that are used throughout the component. Every component should have this directory with at least `keys.ts` and `types.ts`.
77
+
78
+ ### 1. Binding Keys (`keys.ts`)
79
+
80
+ Binding keys are string constants used to register and retrieve values from the DI container. They follow the pattern `@app/[component]/[feature]`.
81
+
82
+ ```typescript
83
+ // src/components/health-check/common/keys.ts
84
+ export class HealthCheckBindingKeys {
85
+ static readonly HEALTH_CHECK_OPTIONS = '@app/health-check/options';
86
+ }
87
+ ```
88
+
89
+ **For components with multiple features:**
90
+
91
+ ```typescript
92
+ // src/components/auth/authenticate/common/keys.ts
93
+ export class AuthenticateBindingKeys {
94
+ static readonly AUTHENTICATE_OPTIONS = '@app/authenticate/options';
95
+ static readonly JWT_OPTIONS = '@app/authenticate/jwt/options';
96
+ }
97
+ ```
98
+
99
+ **Naming Convention:**
100
+ - Class name: `[Feature]BindingKeys`
101
+ - Key format: `@app/[component]/[feature]` or `@app/[component]/[sub-feature]/[name]`
102
+
103
+ ### 2. Types (`types.ts`)
104
+
105
+ Define all interfaces and type aliases that the component exposes or uses internally.
106
+
107
+ ```typescript
108
+ // src/components/health-check/common/types.ts
109
+ export interface IHealthCheckOptions {
110
+ restOptions: { path: string };
111
+ }
112
+ ```
113
+
114
+ **For complex components with service interfaces:**
115
+
116
+ ```typescript
117
+ // src/components/auth/authenticate/common/types.ts
118
+ import { Context } from 'hono';
119
+ import { AnyObject, ValueOrPromise } from '@venizia/ignis-helpers';
120
+
121
+ // Options interface for the component
122
+ export interface IAuthenticateOptions {
123
+ alwaysAllowPaths: Array<string>;
124
+ tokenOptions: IJWTTokenServiceOptions;
125
+ restOptions?: {
126
+ useAuthController?: boolean;
127
+ controllerOpts?: TDefineAuthControllerOpts;
128
+ };
129
+ }
130
+
131
+ // Service options interface
132
+ export interface IJWTTokenServiceOptions {
133
+ jwtSecret: string;
134
+ applicationSecret: string;
135
+ getTokenExpiresFn: () => ValueOrPromise<number>;
136
+ }
137
+
138
+ // Service contract interface
139
+ export interface IAuthService<
140
+ SIRQ = AnyObject,
141
+ SIRS = AnyObject,
142
+ > {
143
+ signIn(context: Context, opts: SIRQ): Promise<SIRS>;
144
+ signUp(context: Context, opts: SIRQ): Promise<SIRS>;
145
+ }
146
+
147
+ // Auth user type
148
+ export interface IAuthUser {
149
+ userId: string;
150
+ [extra: string | symbol]: any;
151
+ }
152
+ ```
18
153
 
19
- ### Key Features
154
+ **Naming Conventions:**
155
+ - Interfaces: `I` prefix (e.g., `IHealthCheckOptions`, `IAuthService`)
156
+ - Type aliases: `T` prefix (e.g., `TDefineAuthControllerOpts`)
20
157
 
21
- | Feature | Description |
22
- | :--- | :--- |
23
- | **Encapsulation** | Bundles necessary bindings (services, controllers) for a feature |
24
- | **Lifecycle Management** | `binding()` method auto-called during startup |
25
- | **Default Bindings** | Auto-registers with application container (self-contained) |
158
+ ### 3. Constants (`constants.ts`)
159
+
160
+ Use static classes (not enums) for constants that need type extraction and validation.
161
+
162
+ ```typescript
163
+ // src/components/auth/authenticate/common/constants.ts
164
+ export class Authentication {
165
+ // Strategy identifiers
166
+ static readonly STRATEGY_BASIC = 'basic';
167
+ static readonly STRATEGY_JWT = 'jwt';
168
+
169
+ // Token types
170
+ static readonly TYPE_BASIC = 'Basic';
171
+ static readonly TYPE_BEARER = 'Bearer';
172
+
173
+ // Context keys
174
+ static readonly CURRENT_USER = 'auth.current.user';
175
+ static readonly SKIP_AUTHENTICATION = 'authentication.skip';
176
+ }
177
+ ```
178
+
179
+ **With validation (for user-configurable values):**
180
+
181
+ ```typescript
182
+ // src/components/swagger/common/constants.ts
183
+ import { TConstValue } from '@venizia/ignis-helpers';
184
+
185
+ export class DocumentUITypes {
186
+ static readonly SWAGGER = 'swagger';
187
+ static readonly SCALAR = 'scalar';
188
+
189
+ // Set for O(1) validation
190
+ static readonly SCHEME_SET = new Set([this.SWAGGER, this.SCALAR]);
191
+
192
+ // Validation helper
193
+ static isValid(value: string): boolean {
194
+ return this.SCHEME_SET.has(value);
195
+ }
196
+ }
197
+
198
+ // Extract union type: 'swagger' | 'scalar'
199
+ export type TDocumentUIType = TConstValue<typeof DocumentUITypes>;
200
+ ```
201
+
202
+ ### 4. REST Paths (`rest-paths.ts`)
203
+
204
+ Define route path constants for controllers.
205
+
206
+ ```typescript
207
+ // src/components/health-check/common/rest-paths.ts
208
+ export class HealthCheckRestPaths {
209
+ static readonly ROOT = '/';
210
+ static readonly PING = '/ping';
211
+ static readonly METRICS = '/metrics';
212
+ }
213
+ ```
214
+
215
+ ### 5. Barrel Exports (`index.ts`)
216
+
217
+ Every folder should have an `index.ts` that re-exports its contents:
218
+
219
+ ```typescript
220
+ // src/components/health-check/common/index.ts
221
+ export * from './keys';
222
+ export * from './rest-paths';
223
+ export * from './types';
224
+
225
+ // src/components/health-check/index.ts
226
+ export * from './common';
227
+ export * from './component';
228
+ export * from './controller';
229
+ ```
230
+
231
+
232
+ ## `BaseComponent` Class
233
+
234
+ Abstract class for all components - structures resource binding and lifecycle management.
26
235
 
27
236
  ### Constructor Options
28
237
 
@@ -32,49 +241,341 @@ The `super()` constructor in your component can take the following options:
32
241
  | :--- | :--- | :--- |
33
242
  | `scope` | `string` | **Required.** A unique name for the component, typically `MyComponent.name`. Used for logging. |
34
243
  | `initDefault` | `{ enable: boolean; container: Container }` | If `enable` is `true`, the `bindings` defined below will be automatically registered with the provided `container` (usually the application instance) if they are not already bound. |
35
- | `bindings` | `Record<string, Binding>` | An object where keys are binding keys (e.g., `'services.MyService'`) and values are `Binding` instances. These are the default services, values, or providers that your component offers. |
244
+ | `bindings` | `Record<string, Binding>` | An object where keys are binding keys and values are `Binding` instances. These are the default services, values, or providers that your component offers. |
36
245
 
37
246
  ### Lifecycle Flow
38
247
 
39
- 1. **Application Instantiates Component**: When you call `this.component(MyComponent)` in your application, the DI container creates an instance of your component.
40
- 2. **Constructor Runs**: Your component's constructor calls `super()`, setting up its scope and defining its default `bindings`. If `initDefault` is enabled, these bindings are immediately registered with the application container.
41
- 3. **Application Calls `binding()`**: During the `registerComponents` phase of the application startup, the `binding()` method of your component is called. This is where you can perform additional setup that might depend on the default bindings being available.
248
+ 1. **Application Instantiates Component**: When you call `this.component(MyComponent)` in your application, the DI container creates an instance of your component.
249
+ 2. **Constructor Runs**: Your component's constructor calls `super()`, setting up its scope and defining its default `bindings`. If `initDefault` is enabled, these bindings are immediately registered with the application container.
250
+ 3. **Application Calls `binding()`**: During the `registerComponents` phase of the application startup, the `binding()` method of your component is called. This is where you can perform additional setup that might depend on the default bindings being available.
251
+
252
+
253
+ ## Component Implementation Patterns
42
254
 
43
- ### Example Implementation
255
+ ### Basic Component
44
256
 
45
257
  ```typescript
46
- import { BaseApplication, BaseComponent, inject, CoreBindings, ValueOrPromise, Binding } from '@venizia/ignis';
258
+ // src/components/health-check/component.ts
259
+ import { BaseApplication, BaseComponent, inject, CoreBindings, Binding, ValueOrPromise } from '@venizia/ignis';
260
+ import { HealthCheckBindingKeys, IHealthCheckOptions } from './common';
261
+ import { HealthCheckController } from './controller';
47
262
 
48
- // A service this component provides
49
- class MyComponentService { /* ... */ }
263
+ // 1. Define default options
264
+ const DEFAULT_OPTIONS: IHealthCheckOptions = {
265
+ restOptions: { path: '/health' },
266
+ };
50
267
 
51
- // A controller that uses the service
52
- @controller({ path: '/my-feature' })
53
- class MyComponentController extends BaseController {
54
- constructor(@inject({ key: 'services.MyComponentService' }) service: MyComponentService) { /* ... */ }
55
- // ...
268
+ export class HealthCheckComponent extends BaseComponent {
269
+ constructor(
270
+ // 2. Inject the application instance
271
+ @inject({ key: CoreBindings.APPLICATION_INSTANCE })
272
+ private application: BaseApplication,
273
+ ) {
274
+ super({
275
+ scope: HealthCheckComponent.name,
276
+ // 3. Enable automatic binding registration
277
+ initDefault: { enable: true, container: application },
278
+ // 4. Define default bindings
279
+ bindings: {
280
+ [HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS]: Binding.bind<IHealthCheckOptions>({
281
+ key: HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS,
282
+ }).toValue(DEFAULT_OPTIONS),
283
+ },
284
+ });
285
+ }
286
+
287
+ // 5. Configure resources in binding()
288
+ override binding(): ValueOrPromise<void> {
289
+ // Read options (may have been overridden by user)
290
+ const healthOptions = this.application.get<IHealthCheckOptions>({
291
+ key: HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS,
292
+ isOptional: true,
293
+ }) ?? DEFAULT_OPTIONS;
294
+
295
+ // Register controller with dynamic path
296
+ Reflect.decorate(
297
+ [controller({ path: healthOptions.restOptions.path })],
298
+ HealthCheckController,
299
+ );
300
+ this.application.controller(HealthCheckController);
301
+ }
302
+ }
303
+ ```
304
+
305
+ ### Component with Services
306
+
307
+ ```typescript
308
+ // src/components/auth/authenticate/component.ts
309
+ import { BaseApplication, BaseComponent, inject, CoreBindings, Binding, ValueOrPromise, getError } from '@venizia/ignis';
310
+ import { AuthenticateBindingKeys, IAuthenticateOptions, IJWTTokenServiceOptions } from './common';
311
+ import { JWTTokenService } from './services';
312
+ import { defineAuthController } from './controllers';
313
+
314
+ const DEFAULT_OPTIONS: IAuthenticateOptions = {
315
+ alwaysAllowPaths: [],
316
+ tokenOptions: {
317
+ applicationSecret: process.env.APP_ENV_APPLICATION_SECRET ?? '',
318
+ jwtSecret: process.env.APP_ENV_JWT_SECRET ?? '',
319
+ getTokenExpiresFn: () => parseInt(process.env.APP_ENV_JWT_EXPIRES_IN ?? '86400'),
320
+ },
321
+ };
322
+
323
+ export class AuthenticateComponent extends BaseComponent {
324
+ constructor(
325
+ @inject({ key: CoreBindings.APPLICATION_INSTANCE })
326
+ private application: BaseApplication,
327
+ ) {
328
+ super({
329
+ scope: AuthenticateComponent.name,
330
+ initDefault: { enable: true, container: application },
331
+ bindings: {
332
+ [AuthenticateBindingKeys.AUTHENTICATE_OPTIONS]: Binding.bind<IAuthenticateOptions>({
333
+ key: AuthenticateBindingKeys.AUTHENTICATE_OPTIONS,
334
+ }).toValue(DEFAULT_OPTIONS),
335
+ },
336
+ });
337
+ }
338
+
339
+ // Split complex logic into private methods
340
+ private defineAuth(): void {
341
+ const options = this.application.get<IAuthenticateOptions>({
342
+ key: AuthenticateBindingKeys.AUTHENTICATE_OPTIONS,
343
+ });
344
+
345
+ // Validate required configuration
346
+ if (!options?.tokenOptions.jwtSecret) {
347
+ throw getError({
348
+ message: '[defineAuth] Missing required jwtSecret configuration',
349
+ });
350
+ }
351
+
352
+ // Bind service options
353
+ this.application
354
+ .bind<IJWTTokenServiceOptions>({ key: AuthenticateBindingKeys.JWT_OPTIONS })
355
+ .toValue(options.tokenOptions);
356
+
357
+ // Register service
358
+ this.application.service(JWTTokenService);
359
+
360
+ // Conditionally register controller
361
+ if (options.restOptions?.useAuthController) {
362
+ this.application.controller(
363
+ defineAuthController(options.restOptions.controllerOpts),
364
+ );
365
+ }
366
+ }
367
+
368
+ override binding(): ValueOrPromise<void> {
369
+ this.defineAuth();
370
+ }
371
+ }
372
+ ```
373
+
374
+ ### Component with Factory Controllers
375
+
376
+ When controllers need to be dynamically configured:
377
+
378
+ ```typescript
379
+ // src/components/static-asset/component.ts
380
+ override binding(): ValueOrPromise<void> {
381
+ const componentOptions = this.application.get<TStaticAssetsComponentOptions>({
382
+ key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
383
+ });
384
+
385
+ // Create multiple controllers from configuration
386
+ for (const [key, opt] of Object.entries(componentOptions)) {
387
+ this.application.controller(
388
+ AssetControllerFactory.defineAssetController({
389
+ controller: opt.controller,
390
+ storage: opt.storage,
391
+ helper: opt.helper,
392
+ }),
393
+ );
394
+
395
+ this.application.logger.info(
396
+ '[binding] Asset storage bound | Key: %s | Type: %s',
397
+ key,
398
+ opt.storage,
399
+ );
400
+ }
401
+ }
402
+ ```
403
+
404
+
405
+ ## Exposing and Consuming Component Options
406
+
407
+ ### Pattern 1: Override Before Registration
408
+
409
+ The most common pattern - override options before registering the component:
410
+
411
+ ```typescript
412
+ // src/application.ts
413
+ import { HealthCheckComponent, HealthCheckBindingKeys, IHealthCheckOptions } from '@venizia/ignis';
414
+
415
+ export class Application extends BaseApplication {
416
+ preConfigure(): ValueOrPromise<void> {
417
+ // 1. Override options BEFORE registering component
418
+ this.bind<IHealthCheckOptions>({ key: HealthCheckBindingKeys.HEALTH_CHECK_OPTIONS })
419
+ .toValue({
420
+ restOptions: { path: '/api/health' }, // Custom path
421
+ });
422
+
423
+ // 2. Register component (will use overridden options)
424
+ this.component(HealthCheckComponent);
425
+ }
426
+ }
427
+ ```
428
+
429
+ ### Pattern 2: Merge with Defaults
430
+
431
+ For partial overrides, merge with defaults in the component:
432
+
433
+ ```typescript
434
+ // In your component's binding() method
435
+ override binding(): ValueOrPromise<void> {
436
+ const extraOptions = this.application.get<Partial<IMyOptions>>({
437
+ key: MyBindingKeys.OPTIONS,
438
+ isOptional: true,
439
+ }) ?? {};
440
+
441
+ // Merge with defaults
442
+ const options = { ...DEFAULT_OPTIONS, ...extraOptions };
443
+
444
+ // Use merged options...
445
+ }
446
+ ```
447
+
448
+ ### Pattern 3: Deep Merge for Nested Options
449
+
450
+ For complex nested configurations:
451
+
452
+ ```typescript
453
+ override binding(): ValueOrPromise<void> {
454
+ const extraOptions = this.application.get<Partial<ISwaggerOptions>>({
455
+ key: SwaggerBindingKeys.SWAGGER_OPTIONS,
456
+ isOptional: true,
457
+ }) ?? {};
458
+
459
+ // Deep merge nested objects
460
+ const options: ISwaggerOptions = {
461
+ ...DEFAULT_OPTIONS,
462
+ ...extraOptions,
463
+ restOptions: {
464
+ ...DEFAULT_OPTIONS.restOptions,
465
+ ...extraOptions.restOptions,
466
+ },
467
+ explorer: {
468
+ ...DEFAULT_OPTIONS.explorer,
469
+ ...extraOptions.explorer,
470
+ },
471
+ };
472
+ }
473
+ ```
474
+
475
+
476
+ ## Best Practices Summary
477
+
478
+ | Aspect | Recommendation |
479
+ |--------|----------------|
480
+ | **Directory** | Use `common/` for shared keys, types, constants |
481
+ | **Keys** | Use `@app/[component]/[feature]` format |
482
+ | **Types** | `I` prefix for interfaces, `T` prefix for type aliases |
483
+ | **Constants** | Use static classes with `SCHEME_SET` for validation |
484
+ | **Defaults** | Define `DEFAULT_OPTIONS` constant at file top |
485
+ | **Exports** | Use barrel exports (`index.ts`) at every level |
486
+ | **Validation** | Validate required options in `binding()` |
487
+ | **Logging** | Log binding activity with structured messages |
488
+ | **Scope** | Always set `scope: ComponentName.name` |
489
+
490
+
491
+ ## Quick Reference Template
492
+
493
+ ```typescript
494
+ // common/keys.ts
495
+ export class MyComponentBindingKeys {
496
+ static readonly OPTIONS = '@app/my-component/options';
497
+ }
498
+
499
+ // common/types.ts
500
+ export interface IMyComponentOptions {
501
+ restOptions: { path: string };
502
+ // ... other options
503
+ }
504
+
505
+ // common/constants.ts (optional)
506
+ export class MyConstants {
507
+ static readonly VALUE_A = 'a';
508
+ static readonly VALUE_B = 'b';
56
509
  }
57
510
 
58
- export class MyCustomComponent extends BaseComponent {
511
+ // common/rest-paths.ts (optional)
512
+ export class MyRestPaths {
513
+ static readonly ROOT = '/';
514
+ static readonly BY_ID = '/:id';
515
+ }
516
+
517
+ // common/index.ts
518
+ export * from './keys';
519
+ export * from './types';
520
+ export * from './constants';
521
+ export * from './rest-paths';
522
+
523
+ // component.ts
524
+ import { BaseApplication, BaseComponent, inject, CoreBindings, Binding, ValueOrPromise } from '@venizia/ignis';
525
+ import { MyComponentBindingKeys, IMyComponentOptions } from './common';
526
+ import { MyController } from './controller';
527
+
528
+ const DEFAULT_OPTIONS: IMyComponentOptions = {
529
+ restOptions: { path: '/my-feature' },
530
+ };
531
+
532
+ export class MyComponent extends BaseComponent {
59
533
  constructor(
60
- @inject({ key: CoreBindings.APPLICATION_INSTANCE }) private application: BaseApplication,
534
+ @inject({ key: CoreBindings.APPLICATION_INSTANCE })
535
+ private application: BaseApplication,
61
536
  ) {
62
537
  super({
63
- scope: MyCustomComponent.name,
538
+ scope: MyComponent.name,
64
539
  initDefault: { enable: true, container: application },
65
540
  bindings: {
66
- 'services.MyComponentService': Binding.bind({ key: 'services.MyComponentService' })
67
- .toClass(MyComponentService)
68
- .setScope(BindingScopes.SINGLETON),
541
+ [MyComponentBindingKeys.OPTIONS]: Binding.bind<IMyComponentOptions>({
542
+ key: MyComponentBindingKeys.OPTIONS,
543
+ }).toValue(DEFAULT_OPTIONS),
69
544
  },
70
545
  });
71
546
  }
72
547
 
73
- // This method is called after the default bindings are registered.
74
548
  override binding(): ValueOrPromise<void> {
75
- // We can now register the controller, which depends on the service.
76
- this.application.controller(MyComponentController);
549
+ const options = this.application.get<IMyComponentOptions>({
550
+ key: MyComponentBindingKeys.OPTIONS,
551
+ isOptional: true,
552
+ }) ?? DEFAULT_OPTIONS;
553
+
554
+ // Register controllers, services, etc.
555
+ this.application.controller(MyController);
77
556
  }
78
557
  }
558
+
559
+ // index.ts
560
+ export * from './common';
561
+ export * from './component';
562
+ export * from './controller';
79
563
  ```
80
- This architecture makes features modular. The `AuthenticateComponent`, for example, uses this pattern to provide the `JWTTokenService` as a default binding and then registers the `AuthController` which depends on it.
564
+
565
+ ## See Also
566
+
567
+ - **Related Concepts:**
568
+ - [Components Overview](/guides/core-concepts/components) - What components are
569
+ - [Creating Components](/guides/core-concepts/components-guide) - Build your own components
570
+ - [Application](/guides/core-concepts/application/) - Registering components
571
+ - [Dependency Injection](/guides/core-concepts/dependency-injection) - Component bindings
572
+
573
+ - **Built-in Components:**
574
+ - [Authentication Component](/references/components/authentication) - JWT authentication
575
+ - [Health Check Component](/references/components/health-check) - Health endpoints
576
+ - [Swagger Component](/references/components/swagger) - API documentation
577
+ - [Socket.IO Component](/references/components/socket-io) - WebSocket support
578
+
579
+ - **Best Practices:**
580
+ - [Architectural Patterns](/best-practices/architectural-patterns) - Component design patterns
581
+ - [Code Style Standards](/best-practices/code-style-standards) - Component coding standards