@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.
- package/README.md +1 -1
- package/package.json +4 -2
- package/wiki/best-practices/api-usage-examples.md +591 -0
- package/wiki/best-practices/architectural-patterns.md +415 -0
- package/wiki/best-practices/architecture-decisions.md +488 -0
- package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +647 -182
- package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
- package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
- package/wiki/best-practices/data-modeling.md +376 -0
- package/wiki/best-practices/deployment-strategies.md +698 -0
- package/wiki/best-practices/index.md +27 -0
- package/wiki/best-practices/performance-optimization.md +196 -0
- package/wiki/best-practices/security-guidelines.md +218 -0
- package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
- package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
- package/wiki/changelogs/2025-12-17-refactor.md +1 -1
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
- package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +86 -0
- package/wiki/changelogs/2025-12-26-transaction-support.md +57 -0
- package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
- package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
- package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
- package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
- package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
- package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
- package/wiki/changelogs/index.md +8 -1
- package/wiki/changelogs/planned-schema-migrator.md +2 -10
- package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
- package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
- package/wiki/guides/core-concepts/components-guide.md +509 -0
- package/wiki/guides/core-concepts/components.md +122 -0
- package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
- package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
- package/wiki/guides/core-concepts/persistent/index.md +119 -0
- package/wiki/guides/core-concepts/persistent/models.md +241 -0
- package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
- package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
- package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
- package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
- package/wiki/guides/get-started/philosophy.md +682 -0
- package/wiki/guides/get-started/setup.md +157 -0
- package/wiki/guides/index.md +89 -0
- package/wiki/guides/reference/glossary.md +243 -0
- package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
- package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
- package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
- package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
- package/wiki/guides/tutorials/realtime-chat.md +1261 -0
- package/wiki/guides/tutorials/testing.md +723 -0
- package/wiki/index.md +176 -37
- package/wiki/references/base/application.md +27 -0
- package/wiki/references/base/bootstrapping.md +30 -26
- package/wiki/references/base/components.md +532 -31
- package/wiki/references/base/controllers.md +136 -38
- package/wiki/references/base/datasources.md +108 -5
- package/wiki/references/base/dependency-injection.md +39 -3
- package/wiki/references/base/filter-system/application-usage.md +224 -0
- package/wiki/references/base/filter-system/array-operators.md +132 -0
- package/wiki/references/base/filter-system/comparison-operators.md +109 -0
- package/wiki/references/base/filter-system/default-filter.md +428 -0
- package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
- package/wiki/references/base/filter-system/index.md +127 -0
- package/wiki/references/base/filter-system/json-filtering.md +197 -0
- package/wiki/references/base/filter-system/list-operators.md +71 -0
- package/wiki/references/base/filter-system/logical-operators.md +156 -0
- package/wiki/references/base/filter-system/null-operators.md +58 -0
- package/wiki/references/base/filter-system/pattern-matching.md +108 -0
- package/wiki/references/base/filter-system/quick-reference.md +431 -0
- package/wiki/references/base/filter-system/range-operators.md +63 -0
- package/wiki/references/base/filter-system/tips.md +190 -0
- package/wiki/references/base/filter-system/use-cases.md +452 -0
- package/wiki/references/base/index.md +90 -0
- package/wiki/references/base/middlewares.md +602 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +732 -0
- package/wiki/references/base/repositories/advanced.md +555 -0
- package/wiki/references/base/repositories/index.md +228 -0
- package/wiki/references/base/repositories/mixins.md +331 -0
- package/wiki/references/base/repositories/relations.md +486 -0
- package/wiki/references/base/repositories.md +40 -549
- package/wiki/references/base/services.md +28 -4
- package/wiki/references/components/authentication.md +22 -2
- package/wiki/references/components/health-check.md +12 -0
- package/wiki/references/components/index.md +23 -0
- package/wiki/references/components/mail.md +687 -0
- package/wiki/references/components/request-tracker.md +16 -0
- package/wiki/references/components/socket-io.md +18 -0
- package/wiki/references/components/static-asset.md +14 -26
- package/wiki/references/components/swagger.md +17 -0
- package/wiki/references/configuration/environment-variables.md +427 -0
- package/wiki/references/configuration/index.md +73 -0
- package/wiki/references/helpers/cron.md +14 -0
- package/wiki/references/helpers/crypto.md +15 -0
- package/wiki/references/helpers/env.md +16 -0
- package/wiki/references/helpers/error.md +17 -0
- package/wiki/references/helpers/index.md +15 -0
- package/wiki/references/helpers/inversion.md +24 -4
- package/wiki/references/helpers/logger.md +19 -0
- package/wiki/references/helpers/network.md +11 -0
- package/wiki/references/helpers/queue.md +19 -0
- package/wiki/references/helpers/redis.md +21 -0
- package/wiki/references/helpers/socket-io.md +24 -5
- package/wiki/references/helpers/storage.md +18 -10
- package/wiki/references/helpers/testing.md +18 -0
- package/wiki/references/helpers/types.md +167 -0
- package/wiki/references/helpers/uid.md +167 -0
- package/wiki/references/helpers/worker-thread.md +16 -0
- package/wiki/references/index.md +177 -0
- package/wiki/references/quick-reference.md +634 -0
- package/wiki/references/src-details/boot.md +3 -3
- package/wiki/references/src-details/dev-configs.md +0 -4
- package/wiki/references/src-details/docs.md +2 -2
- package/wiki/references/src-details/index.md +86 -0
- package/wiki/references/src-details/inversion.md +1 -6
- package/wiki/references/src-details/mcp-server.md +3 -15
- package/wiki/references/utilities/index.md +86 -10
- package/wiki/references/utilities/jsx.md +577 -0
- package/wiki/references/utilities/request.md +0 -2
- package/wiki/references/utilities/statuses.md +740 -0
- package/wiki/changelogs/planned-transaction-support.md +0 -216
- package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
- package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
- package/wiki/get-started/best-practices/data-modeling.md +0 -177
- package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
- package/wiki/get-started/best-practices/performance-optimization.md +0 -88
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/components.md +0 -98
- package/wiki/get-started/core-concepts/persistent.md +0 -543
- package/wiki/get-started/index.md +0 -65
- package/wiki/get-started/philosophy.md +0 -296
- 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
|
-
|
|
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
|
-
|
|
154
|
+
**Naming Conventions:**
|
|
155
|
+
- Interfaces: `I` prefix (e.g., `IHealthCheckOptions`, `IAuthService`)
|
|
156
|
+
- Type aliases: `T` prefix (e.g., `TDefineAuthControllerOpts`)
|
|
20
157
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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.
|
|
40
|
-
2.
|
|
41
|
-
3.
|
|
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
|
-
###
|
|
255
|
+
### Basic Component
|
|
44
256
|
|
|
45
257
|
```typescript
|
|
46
|
-
|
|
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
|
-
//
|
|
49
|
-
|
|
263
|
+
// 1. Define default options
|
|
264
|
+
const DEFAULT_OPTIONS: IHealthCheckOptions = {
|
|
265
|
+
restOptions: { path: '/health' },
|
|
266
|
+
};
|
|
50
267
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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 })
|
|
534
|
+
@inject({ key: CoreBindings.APPLICATION_INSTANCE })
|
|
535
|
+
private application: BaseApplication,
|
|
61
536
|
) {
|
|
62
537
|
super({
|
|
63
|
-
scope:
|
|
538
|
+
scope: MyComponent.name,
|
|
64
539
|
initDefault: { enable: true, container: application },
|
|
65
540
|
bindings: {
|
|
66
|
-
|
|
67
|
-
.
|
|
68
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|