@venizia/ignis-docs 0.2.0 → 0.2.1-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 (174) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +24 -13
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +8 -4
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +247 -153
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -11,13 +11,13 @@ Technical reference for gRPC controller classes -- the foundation for building g
11
11
  IGNIS gRPC controllers follow the same patterns as REST controllers (decorator-based routing, `binding()` method, DI integration) while bridging to ConnectRPC's universal handler system. REST and gRPC controllers coexist in the same application, sharing the same DI container, middleware pipeline, and lifecycle.
12
12
 
13
13
  **Files:**
14
- - `packages/core/src/base/controllers/grpc/abstract.ts`
15
- - `packages/core/src/base/controllers/grpc/base.ts`
16
- - `packages/core/src/base/controllers/grpc/adapter.ts`
17
- - `packages/core/src/base/controllers/grpc/common/types.ts`
18
- - `packages/core/src/base/metadata/routes/rpc.ts`
19
- - `packages/core/src/components/controller/grpc/grpc.component.ts`
20
- - `packages/core/src/components/controller/grpc/common/types.ts`
14
+ - `packages/core-server/src/base/controllers/grpc/abstract.ts`
15
+ - `packages/core-server/src/base/controllers/grpc/base.ts`
16
+ - `packages/core-server/src/base/controllers/grpc/adapter.ts`
17
+ - `packages/core-server/src/base/controllers/grpc/common/types.ts`
18
+ - `packages/core-server/src/base/metadata/routes/rpc.ts`
19
+ - `packages/core-server/src/components/controller/grpc/grpc.component.ts`
20
+ - `packages/core-server/src/components/controller/grpc/common/types.ts`
21
21
 
22
22
  ## Quick Reference
23
23
 
@@ -35,7 +35,7 @@ IGNIS gRPC controllers follow the same patterns as REST controllers (decorator-b
35
35
  | **@rpc** | Generic method decorator (requires explicit `method` in configs) |
36
36
 
37
37
  > [!WARNING]
38
- > **Current version supports unary RPCs only.** The `@serverStream`, `@clientStream`, and `@bidiStream` decorators still exist and set metadata correctly, but `BaseGrpcController.registerRoute()` will throw a clear error at boot time if a non-unary RPC is registered. This is because the Connect protocol over HTTP/1.1 cannot support streaming. The decorators are preserved for forward compatibility.
38
+ > **Current version supports unary RPCs only.** The `@serverStream`, `@clientStream`, and `@bidiStream` decorators still exist and set metadata correctly. But `BaseGrpcController.registerRoute()` throws a clear error at boot time if a non-unary RPC is registered - the Connect protocol over HTTP/1.1 cannot support streaming. The decorators are preserved for forward compatibility.
39
39
 
40
40
  ## Prerequisites
41
41
 
@@ -57,7 +57,7 @@ bun add @connectrpc/connect-web
57
57
  ```
58
58
 
59
59
  > [!NOTE]
60
- > `@connectrpc/connect` is an **optional** peer dependency of `@venizia/ignis` - it is only loaded at runtime when a gRPC controller is configured, via `createRequire` from the application's `node_modules`. If it is missing, `GrpcRequestAdapter.build()` throws a clear error at startup via `validateModule()`. `@bufbuild/protobuf` is required by your generated protobuf code (e.g. `create()`), not by the framework itself.
60
+ > `@connectrpc/connect` is an **optional** peer dependency of `@venizia/ignis`. It is only loaded at runtime when a gRPC controller is configured, via `createRequire` from the application's `node_modules`. If it is missing, `GrpcRequestAdapter.build()` throws a clear error at startup via `ModuleUtility.assertInstalled()`. A compiled binary has no `node_modules` to resolve against and must pass the peer through `IGrpcComponentConfig.module` - see [Peer Dependency Loading](#peer-dependency-loading). `@bufbuild/protobuf` is required by your generated protobuf code (e.g. `create()`), not by the framework itself.
61
61
 
62
62
  ### Protobuf Code Generation
63
63
 
@@ -229,7 +229,7 @@ The `configure()` method on `AbstractGrpcController` is idempotent (guarded by `
229
229
 
230
230
  ## RPC Decorators
231
231
 
232
- All RPC decorators live in `packages/core/src/base/metadata/routes/rpc.ts`. They register metadata in the `MetadataRegistry`, which is read during `configure()`.
232
+ All RPC decorators live in `packages/core-server/src/base/metadata/routes/rpc.ts`. They register metadata in the `MetadataRegistry`, which is read during `configure()`.
233
233
 
234
234
  ### `@rpc` -- Generic
235
235
 
@@ -415,7 +415,7 @@ Internal bridge between IGNIS gRPC controllers and ConnectRPC's universal handle
415
415
 
416
416
  ### Architecture
417
417
 
418
- The adapter solves a key challenge: ConnectRPC handlers have their own `(request, context) => response` signature, but IGNIS controllers need access to the Hono `Context` for middleware, auth, and request-scoped state. The adapter uses `AsyncLocalStorage` to provide request-scoped context isolation, ensuring concurrent requests never share state.
418
+ The adapter solves a key challenge: ConnectRPC handlers have their own `(request, context) => response` signature. IGNIS controllers, though, need access to the Hono `Context` for middleware, auth, and request-scoped state. The adapter uses `AsyncLocalStorage` to provide request-scoped context isolation, ensuring concurrent requests never share state.
419
419
 
420
420
  ```
421
421
  Hono Request
@@ -429,7 +429,7 @@ Hono Request
429
429
 
430
430
  ### Static `build()` Method
431
431
 
432
- The only public API. Validates peer deps via `validateModule()`, creates the adapter, and returns the middleware + registered paths:
432
+ The only public API. Validates peer deps via `ModuleUtility.assertInstalled()`, creates the adapter, and returns the middleware + registered paths:
433
433
 
434
434
  ```typescript
435
435
  static async build(opts: {
@@ -462,12 +462,27 @@ The optional `interceptors` array is passed to ConnectRPC's `createConnectRouter
462
462
 
463
463
  ### Peer Dependency Loading
464
464
 
465
- The adapter loads ConnectRPC modules at runtime using `createRequire` from the application's `node_modules`:
465
+ The adapter needs two entry points:
466
466
 
467
467
  - `@connectrpc/connect` -- for `createConnectRouter`
468
468
  - `@connectrpc/connect/protocol` -- for `universalServerRequestFromFetch` and `universalServerResponseToFetch`
469
469
 
470
- This approach supports single-file builds where the peer deps may not be resolvable via standard `import`.
470
+ By default it loads both at runtime using `createRequire` from the application's `node_modules`. That keeps the specifier invisible to `Bun.build`, so a consumer who never uses gRPC is not forced to install the peer.
471
+
472
+ **A compiled application must pass `module`.** A `bun build --compile` binary ships without `node_modules`, so `createRequire` has nothing to resolve against. Hand the peer over through the component options instead - the static import is what embeds it in the binary:
473
+
474
+ ```typescript
475
+ import * as connect from '@connectrpc/connect';
476
+ import * as protocol from '@connectrpc/connect/protocol';
477
+
478
+ this.bind({ key: GrpcBindingKeys.GRPC_COMPONENT_OPTIONS }).toValue({
479
+ module: { connect, protocol },
480
+ });
481
+ ```
482
+
483
+ `GrpcComponent` assigns the module to each controller before `configure()`, and the adapter skips both `assertInstalled` and `createRequire` when it is present.
484
+
485
+ `ModuleUtility.register` does not work here. The adapter resolves the specifier itself, so the registry never reaches it.
471
486
 
472
487
  ### Error Handling
473
488
 
@@ -477,7 +492,7 @@ On handler errors, the adapter returns a JSON response with:
477
492
  - `grpc-message` header: URL-encoded error message
478
493
  - Body: JSON `{ message, code }`
479
494
 
480
- The adapter uses a duck-type check on `error.code` to preserve gRPC status codes from ConnectRPC errors without importing `ConnectError` directly, avoiding tight coupling to the peer dependency.
495
+ The adapter uses a duck-type check on `error.code` to preserve gRPC status codes from ConnectRPC errors, without importing `ConnectError` directly. That avoids tight coupling to the peer dependency.
481
496
 
482
497
  ## `GrpcComponent`
483
498
 
@@ -488,9 +503,30 @@ Auto-discovers and configures gRPC controllers during the application lifecycle.
488
503
  ```typescript
489
504
  interface IGrpcComponentConfig {
490
505
  interceptors?: unknown[];
506
+ module?: IConnectRpcModule;
491
507
  }
492
508
  ```
493
509
 
510
+ | Option | Type | Default | Meaning |
511
+ |---|---|---|---|
512
+ | `interceptors` | `unknown[]` | none | ConnectRPC interceptors, passed to `createConnectRouter` for every gRPC controller. An empty list passes nothing |
513
+ | `module` | `IConnectRpcModule` | none | The ConnectRPC peer, as `{ connect, protocol }`. Required for a compiled binary - see [Peer Dependency Loading](#peer-dependency-loading) |
514
+
515
+ Both options are component-wide. `GrpcComponent` assigns them to every gRPC controller it discovers, before calling `configure()`:
516
+
517
+ ```typescript
518
+ import type { Interceptor } from '@connectrpc/connect';
519
+
520
+ const logging: Interceptor = next => async request => {
521
+ const response = await next(request);
522
+ return response;
523
+ };
524
+
525
+ this.bind({ key: GrpcBindingKeys.GRPC_COMPONENT_OPTIONS }).toValue({
526
+ interceptors: [logging],
527
+ });
528
+ ```
529
+
494
530
  The component registers a default (empty) config binding under the key `'@app/grpc/options'` (`GrpcBindingKeys.GRPC_COMPONENT_OPTIONS`).
495
531
 
496
532
  ### Behavior
@@ -623,7 +659,7 @@ export class Application extends BaseApplication {
623
659
  ```
624
660
 
625
661
  > [!WARNING]
626
- > If `transports` does not include `ControllerTransports.GRPC`, gRPC controllers are still registered in the DI container but the `GrpcComponent` is never mounted -- their `configure()` is never called and no routes are served.
662
+ > If `transports` does not include `ControllerTransports.GRPC`, gRPC controllers are still registered in the DI container. But the `GrpcComponent` is never mounted -- their `configure()` is never called and no routes are served.
627
663
 
628
664
  ### Dual Transport
629
665
 
@@ -639,7 +675,7 @@ preConfigure() {
639
675
  }
640
676
  ```
641
677
 
642
- REST controllers are handled by the `RestComponent` (active when transports includes `ControllerTransports.REST`, which is the default); gRPC controllers are handled by the `GrpcComponent` (active when transport is enabled). They share the same DI container and lifecycle.
678
+ REST controllers are handled by the `RestComponent`, active when `transports` includes `ControllerTransports.REST` (the default). gRPC controllers are handled by the `GrpcComponent`, active when that transport is enabled. They share the same DI container and lifecycle.
643
679
 
644
680
  ## Complete Example
645
681
 
@@ -9,7 +9,7 @@ Core classes that power every IGNIS application - from the Application entry poi
9
9
 
10
10
  | Class | Purpose | Extends |
11
11
  |-------|---------|---------|
12
- | `BaseApplication` | Application entry point, DI container | `AbstractApplication` |
12
+ | `BaseApplication` | Application entry point, DI container | `ServerApplication` -> `RestApplication` -> `AbstractApplication` |
13
13
  | `BaseRestController` | REST/HTTP route handlers | `AbstractRestController` |
14
14
  | `BaseGrpcController` | gRPC route handlers (ConnectRPC) | `AbstractGrpcController` |
15
15
  | `BaseService` | Business logic layer | - |
@@ -79,8 +79,10 @@ Core classes that power every IGNIS application - from the Application entry poi
79
79
  ## Class Hierarchy
80
80
 
81
81
  ```
82
- AbstractApplication
83
- └── BaseApplication ──────► Your Application
82
+ AbstractApplication (browser-pure, @venizia/ignis-kernel)
83
+ └── RestApplication (browser-pure, owns the router)
84
+ └── ServerApplication (opens the socket)
85
+ └── BaseApplication ──────► Your Application
84
86
 
85
87
  AbstractRepository (engine-neutral, src/base)
86
88
  ├── PostgresBaseRepository (connectors/postgres)
@@ -10,10 +10,10 @@ lastUpdated: 2026-06-14
10
10
  IGNIS provides built-in middleware functions and a provider-based middleware class for handling common HTTP concerns: error handling, request logging, 404 responses, and favicon serving. These are registered automatically by `BaseApplication` during startup - you do not import or wire them manually.
11
11
 
12
12
  **Files:**
13
- - `packages/core/src/base/middlewares/app-error/app-error.middleware.ts`
14
- - `packages/core/src/base/middlewares/not-found/not-found.middleware.ts`
15
- - `packages/core/src/base/middlewares/request-spy/request-spy.middleware.ts`
16
- - `packages/core/src/base/middlewares/emoji-favicon/emoji-favicon.middleware.ts`
13
+ - `packages/core-server/src/base/middlewares/app-error/app-error.middleware.ts`
14
+ - `packages/core-server/src/base/middlewares/not-found/not-found.middleware.ts`
15
+ - `packages/core-server/src/base/middlewares/request-spy/request-spy.middleware.ts`
16
+ - `packages/core-server/src/base/middlewares/emoji-favicon/emoji-favicon.middleware.ts`
17
17
 
18
18
  ## Prerequisites
19
19
 
@@ -26,7 +26,7 @@ Before reading this document, you should understand:
26
26
 
27
27
  | Middleware | Type | Purpose |
28
28
  |-----------|------|---------|
29
- | `appErrorHandler` | `ErrorHandler` | Global error handler (Zod, DB constraints, generic) |
29
+ | `AppErrorMiddleware` | `IProvider<ErrorHandler>` | Global error handler (Zod, DB constraints, generic) |
30
30
  | `notFoundHandler` | `NotFoundHandler` | JSON 404 response for unknown routes |
31
31
  | `RequestSpyMiddleware` | `IProvider<MiddlewareHandler>` | Request/response logging with timing |
32
32
  | `emojiFavicon` | `MiddlewareHandler` | Serves an emoji as SVG favicon |
@@ -40,7 +40,7 @@ protected async registerDefaultMiddlewares() {
40
40
  const server = this.getServer();
41
41
 
42
42
  // 1. Global error handler
43
- server.onError(appErrorHandler({ logger, rootKey }));
43
+ server.onError(new AppErrorMiddleware({ logger, rootKey }).value());
44
44
 
45
45
  // 2. Async context storage (if enabled)
46
46
  if (this.configs.asyncContext?.enable) {
@@ -60,24 +60,24 @@ protected async registerDefaultMiddlewares() {
60
60
 
61
61
  After `registerDefaultMiddlewares()`, the application calls user-defined `staticConfigure()`, `preConfigure()`, and so on. The user's `setupMiddlewares()` hook runs after `initialize()` but before the server starts.
62
62
 
63
- ## appErrorHandler
63
+ ## AppErrorMiddleware
64
64
 
65
- Global error handler registered via `server.onError()`. Handles ZodError validation errors, PostgreSQL constraint violations, and generic errors.
65
+ Global error handler registered via `server.onError()`. Handles ZodError validation errors, PostgreSQL constraint violations, and generic errors. Like `RequestSpyMiddleware`, it is an `IProvider` - build it, then call `value()` for the handler.
66
66
 
67
- **Not exported from `@venizia/ignis`** - registered automatically by `BaseApplication`.
67
+ Registered automatically by `BaseApplication`.
68
68
 
69
69
  ### Signature
70
70
 
71
71
  ```typescript
72
- function appErrorHandler(opts: {
73
- logger: Logger;
74
- rootKey?: string;
75
- }): ErrorHandler
72
+ class AppErrorMiddleware extends BaseHelper implements IProvider<ErrorHandler> {
73
+ constructor(opts?: { logger?: ILogger; rootKey?: string });
74
+ value(): ErrorHandler;
75
+ }
76
76
  ```
77
77
 
78
78
  | Parameter | Type | Description |
79
79
  |-----------|------|-------------|
80
- | `logger` | `Logger` | Logger instance for error logging |
80
+ | `logger` | `ILogger \| undefined` | Overrides the middleware's own scoped logger - `BaseApplication` passes its own so error lines stay in its scope |
81
81
  | `rootKey` | `string \| undefined` | Optional root key to wrap the error response object |
82
82
 
83
83
  ### Error Handling Logic
@@ -86,13 +86,17 @@ function appErrorHandler(opts: {
86
86
 
87
87
  When `error.name === 'ZodError'`, returns HTTP `422 Unprocessable Entity`.
88
88
 
89
- Top-level `message`/`messageCode` come from the first failing issue - its `params.code` if the schema set one, otherwise its raw Zod code. The full per-field list stays under `details.cause`.
89
+ `message` and `normalized.code` come from the first failing issue. `message` is that issue's message; `normalized.code` is its `params.code` if the schema set one, otherwise its raw Zod code. The full per-field list stays under `details.cause`. `normalized.args` is always `{}` - a Zod issue carries no interpolation values.
90
90
 
91
91
  ```json
92
92
  {
93
93
  "message": "Invalid email address",
94
- "messageCode": "user.email.invalid",
95
94
  "statusCode": 422,
95
+ "normalized": {
96
+ "text": "Invalid email address",
97
+ "code": "user.email.invalid",
98
+ "args": {}
99
+ },
96
100
  "requestId": "abc-123",
97
101
  "details": {
98
102
  "url": "http://localhost:3000/users",
@@ -111,22 +115,22 @@ Top-level `message`/`messageCode` come from the first failing issue - its `param
111
115
  }
112
116
  ```
113
117
 
114
- To emit a stable, domain-specific `messageCode`, attach `params.code` to a custom check:
118
+ To emit a stable, domain-specific `normalized.code`, attach `params.code` to a custom check:
115
119
 
116
120
  ```typescript
117
121
  z.string().refine(isEmail, {
118
122
  message: 'Invalid email address',
119
123
  params: { code: 'user.email.invalid' }
120
124
  });
121
- // produces "messageCode": "user.email.invalid"
125
+ // produces "normalized": { "code": "user.email.invalid", ... }
122
126
  ```
123
127
 
124
128
  > [!NOTE]
125
- > When `error.message` cannot be parsed as the expected Zod issue array (a malformed or unrecognized `ZodError`), no issue-derived `messageCode` exists - the response still carries one, resolved to `MessageCode.DEFAULT` (`"core.system_error"`) via `MessageCode.resolve(undefined)`. No error response from this middleware is ever missing `messageCode`.
129
+ > When `error.message` cannot be parsed as the expected Zod issue array (a malformed or unrecognized `ZodError`), no issue-derived code exists. `normalized.code` still resolves to `MessageCode.DEFAULT` (`"core.system_error"`) via `MessageCode.resolve(undefined)`. No error response from this middleware is ever missing `normalized.code`.
126
130
 
127
131
  #### 2. PostgreSQL Constraint Violations
128
132
 
129
- Database errors in SQLSTATE class `22` (data exception), `23` (integrity constraint), and `44` (WITH CHECK OPTION violation) are detected by class and returned as HTTP `400 Bad Request`. A known code uses its specific message; any other in-class code uses `"Invalid database request"` as a fallback.
133
+ Database errors in SQLSTATE class `22` (data exception), `23` (integrity constraint), and `44` (WITH CHECK OPTION violation) are detected by class. They return HTTP `400 Bad Request`. A known code uses its specific message; any other in-class code uses `"Invalid database request"` as a fallback.
130
134
 
131
135
  | Class | Codes with a specific message |
132
136
  |-------|-------------------------------|
@@ -135,7 +139,7 @@ Database errors in SQLSTATE class `22` (data exception), `23` (integrity constra
135
139
  | `44` View check | `44000` WITH CHECK OPTION violation |
136
140
 
137
141
  :::tip Transient conflicts return 409, not 400/500
138
- Class `40` (`40001` serialization failure, `40P01` deadlock) is transient/retryable and returns **409 Conflict** with `messageCode: "database.conflict"` and a safe "please retry" message - the client can safely retry the same request. Programming/infra classes (`42` syntax, `53` resources, `0A`, `25`, `28`) remain 500.
142
+ Class `40` (`40001` serialization failure, `40P01` deadlock) is transient/retryable. It returns **409 Conflict** with `normalized.code: "database.conflict"` and a safe "please retry" message - the client can safely retry the same request. Programming/infra classes (`42` syntax, `53` resources, `0A`, `25`, `28`) remain 500.
139
143
  :::
140
144
 
141
145
  :::warning Production sanitizes database internals
@@ -151,8 +155,12 @@ All other errors use the `statusCode` property from the error if present, otherw
151
155
  ```json
152
156
  {
153
157
  "message": "Error message",
154
- "messageCode": "core.system_error",
155
158
  "statusCode": 500,
159
+ "normalized": {
160
+ "text": "Error message",
161
+ "code": "core.system_error",
162
+ "args": {}
163
+ },
156
164
  "requestId": "abc-123",
157
165
  "details": {
158
166
  "url": "http://localhost:3000/users",
@@ -163,14 +171,20 @@ All other errors use the `statusCode` property from the error if present, otherw
163
171
  }
164
172
  ```
165
173
 
174
+ An intentional `getError(...)` throw also carries `extra` when the throw site attached context of its own; every other branch never does. There is no top-level `messageCode` - the code always lives at `normalized.code`.
175
+
166
176
  When `rootKey` is provided (e.g., `rootKey: 'error'`), the response is wrapped:
167
177
 
168
178
  ```json
169
179
  {
170
180
  "error": {
171
181
  "message": "Error message",
172
- "messageCode": "core.system_error",
173
182
  "statusCode": 500,
183
+ "normalized": {
184
+ "text": "Error message",
185
+ "code": "core.system_error",
186
+ "args": {}
187
+ },
174
188
  "requestId": "abc-123",
175
189
  "details": { ... }
176
190
  }
@@ -215,13 +229,13 @@ Returns a JSON 404 response when no route matches. Registered via `server.notFou
215
229
 
216
230
  ```typescript
217
231
  function notFoundHandler(opts: {
218
- logger?: Logger;
232
+ logger?: ILogger;
219
233
  }): NotFoundHandler
220
234
  ```
221
235
 
222
236
  | Parameter | Type | Description |
223
237
  |-----------|------|-------------|
224
- | `logger` | `Logger \| undefined` | Logger instance (defaults to `console`) |
238
+ | `logger` | `ILogger \| undefined` | Logger instance (defaults to `console`) |
225
239
 
226
240
  ### Response Format
227
241
 
@@ -235,7 +249,8 @@ function notFoundHandler(opts: {
235
249
  }
236
250
  ```
237
251
 
238
- The handler logs the 404 at error level with the request ID, path, and full URL.
252
+ The handler logs the 404 at warn level with the request ID, path, and full URL. An unrouted path is a
253
+ client mistake, not a server fault - alerting tuned to error level should not fire on it.
239
254
 
240
255
 
241
256
  ## RequestSpyMiddleware
@@ -363,7 +378,7 @@ Several middleware behaviors are configured through `IApplicationConfigs`:
363
378
  ```typescript
364
379
  interface IApplicationConfigs {
365
380
  favicon?: string; // Emoji for emojiFavicon (default: '🔥')
366
- error?: { rootKey: string }; // Root key wrapper for appErrorHandler
381
+ error?: { rootKey: string }; // Root key wrapper for AppErrorMiddleware
367
382
  asyncContext?: { enable: boolean }; // Enable Hono contextStorage() middleware
368
383
  // ...
369
384
  }
@@ -482,7 +497,7 @@ export class MyMiddleware extends BaseHelper implements IProvider<MiddlewareHand
482
497
  }
483
498
  ```
484
499
 
485
- Register it with `.toProvider()` (the same pattern `RequestTrackerComponent` uses for `RequestSpyMiddleware`), then use the resolved handler inside `setupMiddlewares()` - `get()` returns the produced `MiddlewareHandler` because the container calls `value()` for provider bindings:
500
+ Register it with `.toProvider()` (the same pattern `RequestTrackerComponent` uses for `RequestSpyMiddleware`). Then use the resolved handler inside `setupMiddlewares()`: `get()` returns the produced `MiddlewareHandler`, because the container calls `value()` for provider bindings.
486
501
 
487
502
  ```typescript
488
503
  export class MyApplication extends BaseApplication {