@venizia/ignis-docs 0.0.1-8 → 0.0.1

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 (43) hide show
  1. package/LICENSE.md +1 -0
  2. package/package.json +2 -2
  3. package/wiki/changelogs/2025-12-16-initial-architecture.md +145 -0
  4. package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +300 -0
  5. package/wiki/changelogs/2025-12-17-refactor.md +90 -0
  6. package/wiki/changelogs/2025-12-18-performance-optimizations.md +130 -0
  7. package/wiki/changelogs/2025-12-18-repository-validation-security.md +249 -0
  8. package/wiki/changelogs/index.md +33 -0
  9. package/wiki/changelogs/planned-transaction-support.md +216 -0
  10. package/wiki/changelogs/template.md +123 -0
  11. package/wiki/get-started/5-minute-quickstart.md +1 -1
  12. package/wiki/get-started/best-practices/api-usage-examples.md +12 -10
  13. package/wiki/get-started/best-practices/architectural-patterns.md +2 -2
  14. package/wiki/get-started/best-practices/common-pitfalls.md +7 -5
  15. package/wiki/get-started/best-practices/contribution-workflow.md +2 -0
  16. package/wiki/get-started/best-practices/data-modeling.md +91 -40
  17. package/wiki/get-started/best-practices/security-guidelines.md +3 -1
  18. package/wiki/get-started/building-a-crud-api.md +63 -78
  19. package/wiki/get-started/core-concepts/application.md +72 -3
  20. package/wiki/get-started/core-concepts/bootstrapping.md +566 -0
  21. package/wiki/get-started/core-concepts/components.md +4 -2
  22. package/wiki/get-started/core-concepts/controllers.md +14 -14
  23. package/wiki/get-started/core-concepts/persistent.md +383 -431
  24. package/wiki/get-started/core-concepts/services.md +21 -27
  25. package/wiki/get-started/quickstart.md +1 -1
  26. package/wiki/references/base/bootstrapping.md +789 -0
  27. package/wiki/references/base/components.md +1 -1
  28. package/wiki/references/base/controllers.md +40 -16
  29. package/wiki/references/base/datasources.md +195 -33
  30. package/wiki/references/base/dependency-injection.md +98 -5
  31. package/wiki/references/base/models.md +398 -28
  32. package/wiki/references/base/repositories.md +475 -22
  33. package/wiki/references/base/services.md +2 -2
  34. package/wiki/references/components/authentication.md +228 -10
  35. package/wiki/references/components/health-check.md +1 -1
  36. package/wiki/references/components/index.md +1 -1
  37. package/wiki/references/components/swagger.md +1 -1
  38. package/wiki/references/helpers/error.md +2 -2
  39. package/wiki/references/helpers/inversion.md +8 -3
  40. package/wiki/references/src-details/boot.md +379 -0
  41. package/wiki/references/src-details/core.md +8 -7
  42. package/wiki/references/src-details/inversion.md +4 -4
  43. package/wiki/references/utilities/request.md +16 -7
@@ -9,8 +9,9 @@ JWT-based authentication and authorization system for Ignis applications.
9
9
  | **AuthenticateComponent** | Main component registering auth services and controllers |
10
10
  | **AuthenticationStrategyRegistry** | Singleton managing available auth strategies |
11
11
  | **JWTAuthenticationStrategy** | JWT verification using `JWTTokenService` |
12
- | **JWTTokenService** | Generate, verify, encrypt/decrypt JWT tokens |
12
+ | **JWTTokenService** | Generate, verify, encrypt/decrypt JWT tokens (safely handles undefined/null) |
13
13
  | **IAuthService** | Interface for custom auth implementation (sign-in, sign-up) |
14
+ | **defineAuthController** | Factory function for creating custom auth controllers |
14
15
 
15
16
  ### Key Environment Variables
16
17
 
@@ -20,12 +21,26 @@ JWT-based authentication and authorization system for Ignis applications.
20
21
  | `APP_ENV_JWT_SECRET` | Sign and verify JWT signature | ✅ Yes |
21
22
  | `APP_ENV_JWT_EXPIRES_IN` | Token expiration (seconds) | Optional |
22
23
 
24
+ ### Authentication Options Configuration
25
+
26
+ | Option | Type | Description |
27
+ |--------|------|-------------|
28
+ | `restOptions.useAuthController` | `boolean` | Enable/disable built-in auth controller (default: `false`) |
29
+ | `restOptions.controllerOpts` | `TDefineAuthControllerOpts` | Configuration for built-in auth controller (required if `useAuthController` is `true`) |
30
+ | `restOptions.controllerOpts.restPath` | `string` | Base path for auth endpoints (default: `/auth`) |
31
+ | `restOptions.controllerOpts.serviceKey` | `string` | Dependency injection key for auth service (default: `services.AuthenticationService`) |
32
+ | `restOptions.controllerOpts.requireAuthenticatedSignUp` | `boolean` | Whether sign-up requires authentication (default: `false`) |
33
+ | `restOptions.controllerOpts.payload` | `object` | Custom Zod schemas for request/response payloads |
34
+ | `alwaysAllowPaths` | `string[]` | Array of paths that bypass authentication |
35
+ | `tokenOptions` | `IJWTTokenServiceOptions` | JWT token configuration |
36
+
23
37
  ## Architecture Components
24
38
 
25
- - **`AuthenticateComponent`**: Registers all necessary services and controllers
39
+ - **`AuthenticateComponent`**: Registers all necessary services and optionally the authentication controller
26
40
  - **`AuthenticationStrategyRegistry`**: Singleton managing authentication strategies
27
41
  - **`JWTAuthenticationStrategy`**: JWT strategy implementation using `JWTTokenService`
28
- - **`JWTTokenService`**: Generates, verifies, encrypts/decrypts JWT payloads
42
+ - **`JWTTokenService`**: Generates, verifies, encrypts/decrypts JWT payloads (handles undefined/null values safely)
43
+ - **`defineAuthController`**: Factory function to create customizable authentication controller
29
44
  - **Protected Routes**: Use `authStrategies` in route configs to secure endpoints
30
45
 
31
46
  ## Implementation Details
@@ -62,6 +77,8 @@ APP_ENV_JWT_EXPIRES_IN=86400
62
77
 
63
78
  In `src/application.ts`, register the `AuthenticateComponent` and the `JWTAuthenticationStrategy`. You also need to provide an `AuthenticationService`.
64
79
 
80
+ **Basic Setup (without built-in auth controller):**
81
+
65
82
  ```typescript
66
83
  // src/application.ts
67
84
  import {
@@ -79,7 +96,116 @@ export class Application extends BaseApplication {
79
96
 
80
97
  registerAuth() {
81
98
  this.service(AuthenticationService);
82
- this.component(AuthenticateComponent);
99
+ this.component(AuthenticateComponent, {
100
+ restOptions: {
101
+ useAuthController: false, // Default: controller not registered
102
+ },
103
+ alwaysAllowPaths: [],
104
+ tokenOptions: {
105
+ applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
106
+ jwtSecret: process.env.APP_ENV_JWT_SECRET,
107
+ getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
108
+ },
109
+ });
110
+ AuthenticationStrategyRegistry.getInstance().register({
111
+ container: this,
112
+ name: Authentication.STRATEGY_JWT,
113
+ strategy: JWTAuthenticationStrategy,
114
+ });
115
+ }
116
+
117
+ preConfigure(): ValueOrPromise<void> {
118
+ // ...
119
+ this.registerAuth();
120
+ // ...
121
+ }
122
+ }
123
+ ```
124
+
125
+ **Advanced Setup (with built-in auth controller):**
126
+
127
+ ```typescript
128
+ import {
129
+ AuthenticateComponent,
130
+ Authentication,
131
+ AuthenticationStrategyRegistry,
132
+ JWTAuthenticationStrategy,
133
+ BaseApplication,
134
+ ValueOrPromise,
135
+ } from '@venizia/ignis';
136
+ import { z } from '@hono/zod-openapi';
137
+ import { AuthenticationService } from './services';
138
+
139
+ export class Application extends BaseApplication {
140
+ // ...
141
+
142
+ registerAuth() {
143
+ this.service(AuthenticationService);
144
+ this.component(AuthenticateComponent, {
145
+ restOptions: {
146
+ useAuthController: true, // Enable built-in auth controller
147
+ controllerOpts: {
148
+ restPath: '/auth', // Base path for auth endpoints
149
+ serviceKey: 'services.AuthenticationService', // Default service key
150
+ requireAuthenticatedSignUp: false, // Whether sign-up requires authentication
151
+ payload: {
152
+ signIn: {
153
+ request: {
154
+ schema: z.object({
155
+ identifier: z.object({
156
+ type: z.string(),
157
+ value: z.string(),
158
+ }),
159
+ credential: z.object({
160
+ type: z.string(),
161
+ value: z.string(),
162
+ }),
163
+ })
164
+ },
165
+ response: {
166
+ schema: z.object({
167
+ token: z.string(),
168
+ })
169
+ },
170
+ },
171
+ signUp: {
172
+ request: {
173
+ schema: z.object({
174
+ username: z.string(),
175
+ email: z.string().email(),
176
+ password: z.string().min(8),
177
+ })
178
+ },
179
+ response: {
180
+ schema: z.object({
181
+ token: z.string(),
182
+ userId: z.string(),
183
+ })
184
+ },
185
+ },
186
+ changePassword: {
187
+ request: {
188
+ schema: z.object({
189
+ oldPassword: z.string(),
190
+ newPassword: z.string().min(8),
191
+ })
192
+ },
193
+ response: {
194
+ schema: z.object({
195
+ success: z.boolean(),
196
+ })
197
+ },
198
+ },
199
+ },
200
+ },
201
+ },
202
+ alwaysAllowPaths: [],
203
+ tokenOptions: {
204
+ applicationSecret: process.env.APP_ENV_APPLICATION_SECRET,
205
+ jwtSecret: process.env.APP_ENV_JWT_SECRET,
206
+ getTokenExpiresFn: () => Number(process.env.APP_ENV_JWT_EXPIRES_IN || 86400),
207
+ },
208
+ });
83
209
  AuthenticationStrategyRegistry.getInstance().register({
84
210
  container: this,
85
211
  name: Authentication.STRATEGY_JWT,
@@ -110,6 +236,7 @@ import {
110
236
  IJWTTokenPayload,
111
237
  JWTTokenService,
112
238
  TSignInRequest,
239
+ getError,
113
240
  } from '@venizia/ignis';
114
241
  import { Context } from 'hono';
115
242
 
@@ -130,7 +257,7 @@ export class AuthenticationService extends BaseService implements IAuthService {
130
257
  const user = { id: 'user-id-from-db', roles: [] }; // Dummy user
131
258
 
132
259
  if (identifier.value !== 'test_username' || credential.value !== 'test_password') {
133
- throw new Error('Invalid credentials');
260
+ throw getError({ message: 'Invalid credentials' });
134
261
  }
135
262
  // --- End of custom logic ---
136
263
 
@@ -146,19 +273,107 @@ export class AuthenticationService extends BaseService implements IAuthService {
146
273
 
147
274
  async signUp(context: Context, opts: any): Promise<any> {
148
275
  // Implement your sign-up logic
149
- throw new Error('Method not implemented.');
276
+ throw getError({ message: 'Method not implemented.' });
150
277
  }
151
278
 
152
279
  async changePassword(context: Context, opts: any): Promise<any> {
153
280
  // Implement your change password logic
154
- throw new Error('Method not implemented.');
281
+ throw getError({ message: 'Method not implemented.' });
155
282
  }
156
283
  }
157
284
  ```
158
285
 
159
286
  This service is then registered in `application.ts` as shown in the previous step. It injects the `JWTTokenService` (provided by the `AuthenticateComponent`) to generate a token upon successful sign-in.
160
287
 
161
- #### 3. Securing Routes
288
+ #### 3. Custom Authentication Controller (Optional)
289
+
290
+ If you need more control over the authentication endpoints, you can create a custom controller using the `defineAuthController` factory function.
291
+
292
+ ```typescript
293
+ // src/controllers/custom-auth.controller.ts
294
+ import { defineAuthController } from '@venizia/ignis';
295
+ import { z } from '@hono/zod-openapi';
296
+
297
+ export const CustomAuthController = defineAuthController({
298
+ restPath: '/api/auth',
299
+ serviceKey: 'services.AuthenticationService',
300
+ requireAuthenticatedSignUp: true, // Require authentication for sign-up
301
+ payload: {
302
+ signIn: {
303
+ request: {
304
+ schema: z.object({
305
+ email: z.string().email(),
306
+ password: z.string(),
307
+ }),
308
+ },
309
+ response: {
310
+ schema: z.object({
311
+ token: z.string(),
312
+ expiresIn: z.number(),
313
+ }),
314
+ },
315
+ },
316
+ signUp: {
317
+ request: {
318
+ schema: z.object({
319
+ email: z.string().email(),
320
+ password: z.string().min(8),
321
+ firstName: z.string(),
322
+ lastName: z.string(),
323
+ }),
324
+ },
325
+ response: {
326
+ schema: z.object({
327
+ token: z.string(),
328
+ userId: z.string(),
329
+ }),
330
+ },
331
+ },
332
+ changePassword: {
333
+ request: {
334
+ schema: z.object({
335
+ currentPassword: z.string(),
336
+ newPassword: z.string().min(8),
337
+ }),
338
+ },
339
+ response: {
340
+ schema: z.object({
341
+ success: z.boolean(),
342
+ message: z.string().optional(),
343
+ }),
344
+ },
345
+ },
346
+ },
347
+ });
348
+ ```
349
+
350
+ Then register it in your application:
351
+
352
+ ```typescript
353
+ // src/application.ts
354
+ import { CustomAuthController } from './controllers/custom-auth.controller';
355
+
356
+ export class Application extends BaseApplication {
357
+ registerAuth() {
358
+ this.service(AuthenticationService);
359
+ this.component(AuthenticateComponent, {
360
+ restOptions: { useAuthController: false }, // Disable built-in controller
361
+ // ... other options
362
+ });
363
+
364
+ // Register your custom controller
365
+ this.controller(CustomAuthController);
366
+
367
+ AuthenticationStrategyRegistry.getInstance().register({
368
+ container: this,
369
+ name: Authentication.STRATEGY_JWT,
370
+ strategy: JWTAuthenticationStrategy,
371
+ });
372
+ }
373
+ }
374
+ ```
375
+
376
+ #### 4. Securing Routes
162
377
 
163
378
  In your controllers, use decorator-based routing (`@get`, `@post`, etc.) with the `authStrategies` property in the `configs` object to protect endpoints. This will automatically run the necessary authentication middlewares and attach the authenticated user to the Hono `Context`, which can then be accessed type-safely using `TRouteContext`.
164
379
 
@@ -199,12 +414,15 @@ export class TestController extends BaseController {
199
414
  secureData(c: TRouteContext<typeof SECURE_ROUTE_CONFIG>) {
200
415
  // 'c' is fully typed here, including c.get and c.json return type
201
416
  const user = c.get(Authentication.CURRENT_USER) as IJWTTokenPayload | undefined;
202
- return c.json({ message: `Hello, ${user?.userId || 'guest'} from protected data` });
417
+ return c.json(
418
+ { message: `Hello, ${user?.userId || 'guest'} from protected data` },
419
+ HTTP.ResultCodes.RS_2.Ok,
420
+ );
203
421
  }
204
422
  }
205
423
  ```
206
424
 
207
- #### 4. Accessing the Current User in Context
425
+ #### 5. Accessing the Current User in Context
208
426
 
209
427
  After a route has been processed, the authenticated user's payload is available directly on the Hono `Context` object, using the `Authentication.CURRENT_USER` key.
210
428
 
@@ -127,7 +127,7 @@ export class HealthCheckController extends BaseController {
127
127
 
128
128
  @api({ configs: ROUTE_CONFIGS['/'] })
129
129
  checkHealth(c: TRouteContext<typeof ROUTE_CONFIGS['/']>) {
130
- return c.json({ status: 'ok' });
130
+ return c.json({ status: 'ok' }, HTTP.ResultCodes.RS_2.Ok);
131
131
  }
132
132
 
133
133
  @api({ configs: ROUTE_CONFIGS['/ping'] })
@@ -1,6 +1,6 @@
1
1
  # Components
2
2
 
3
- Reusable, pluggable modules that encapsulate specific features in Ignis applications.
3
+ Reusable, pluggable modules that group together related features. A component can encapsulate various resources such as providers, services, controllers, repositories, or even an entire mini-application, providing a clean way to modularize and share complex logic across Ignis applications.
4
4
 
5
5
  ## Built-in Components
6
6
 
@@ -158,7 +158,7 @@ export class HelloController extends BaseController {
158
158
  },
159
159
  },
160
160
  handler: (c) => {
161
- return c.json({ message: 'Hello, `Ignis`!' });
161
+ return c.json({ message: 'Hello, `Ignis`!' }, HTTP.ResultCodes.RS_2.Ok);
162
162
  },
163
163
  });
164
164
  }
@@ -26,10 +26,10 @@ Extends native `Error` with HTTP status codes and machine-readable message codes
26
26
  You can create a new `ApplicationError` with a message, status code, and an optional message code.
27
27
 
28
28
  ```typescript
29
- import { ApplicationError, HTTP } from '@venizia/ignis';
29
+ import { getError, HTTP } from '@venizia/ignis';
30
30
 
31
31
  // Throw an error for a resource not found
32
- throw new ApplicationError({
32
+ throw getError({
33
33
  message: 'User not found',
34
34
  statusCode: HTTP.ResultCodes.RS_4.NotFound,
35
35
  messageCode: 'USER_NOT_FOUND',
@@ -2,7 +2,12 @@
2
2
 
3
3
  Core DI system enabling loosely coupled, testable, and extensible code.
4
4
 
5
- > **Package Architecture Note:** The core DI container functionality has been extracted to a standalone package `@venizia/ignis-inversion`. The `@venizia/ignis-helpers` package extends and re-exports this functionality with application-specific enhancements (logging, framework metadata). All imports from `@venizia/ignis-helpers` or `@venizia/ignis` continue to work as before - backward compatibility is maintained.
5
+ > **Architecture Update:** The core DI container functionality has been extracted to a standalone package `@venizia/ignis-inversion`.
6
+ >
7
+ > * **Standalone Container:** `@venizia/ignis-inversion` (Generic DI)
8
+ > * **Framework Integration:** `@venizia/ignis` (extends Core DI with Framework Metadata)
9
+ >
10
+ > Previously, this module resided in `@venizia/ignis-helpers`. It has now been moved to **Core** (`@venizia/ignis`) to better align with the framework architecture.
6
11
 
7
12
  ## Quick Reference
8
13
 
@@ -141,11 +146,11 @@ export class UserController extends BaseController {
141
146
  The `MetadataRegistry` is a crucial part of the DI and routing systems. It's a singleton class responsible for storing and retrieving all the metadata attached by decorators like `@inject`, `@controller`, `@get`, etc.
142
147
 
143
148
  - **Base File:** `packages/inversion/src/registry.ts` (core MetadataRegistry)
144
- - **Extended File:** `packages/helpers/src/helpers/inversion/registry.ts` (with framework metadata)
149
+ - **Extended File:** `packages/core/src/helpers/inversion/registry.ts` (with framework metadata)
145
150
 
146
151
  ### Role in DI
147
152
 
148
153
  - When you use a decorator (e.g., `@inject`), it calls a method on the `MetadataRegistry.getInstance()` to store information about the injection (like the binding key and target property/parameter).
149
154
  - When the `Container` instantiates a class, it queries the `MetadataRegistry` to find out which dependencies need to be injected and where.
150
155
 
151
- You typically won't interact with the `MetadataRegistry` directly, but it's the underlying mechanism that makes the decorator-based DI and routing systems work seamlessly.
156
+ You typically won't interact with the `MetadataRegistry` directly, but it's the underlying mechanism that makes the decorator-based DI and routing systems work seamlessly.