@venizia/ignis-docs 0.2.0 → 0.2.1-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 (142) 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 +22 -11
  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 +26 -2
  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 +6 -2
  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 +182 -93
  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 +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. package/package.json +8 -8
@@ -1,6 +1,6 @@
1
1
  # Request Tracker
2
2
 
3
- Automatic request logging middleware with unique request IDs, client IP detection, body parsing, and timing -- auto-registered by the framework.
3
+ Automatic request logging middleware that assigns a UUID request ID to every request, then logs method, path, client IP, and timing on the way in and out.
4
4
 
5
5
  > [!IMPORTANT]
6
6
  > This component is **auto-registered** by `BaseApplication` during `initialize()`. No manual registration is needed.
@@ -20,225 +20,112 @@ Automatic request logging middleware with unique request IDs, client IP detectio
20
20
  import { RequestTrackerComponent } from '@venizia/ignis';
21
21
  ```
22
22
 
23
- ## Setup
23
+ ## In one example
24
24
 
25
- ### Step 1: Bind Configuration
26
-
27
- No configuration binding is required. The component has no user-facing configuration options.
28
-
29
- ### Step 2: Register Component
30
-
31
- This happens automatically inside `BaseApplication.initialize()`:
32
-
33
- ```typescript
34
- // Internal to BaseApplication -- shown for reference only
35
- this.component(RequestTrackerComponent);
36
- ```
37
-
38
- The component registers two Hono middlewares on the application server during its `binding()` phase:
39
- 1. `requestId()` from `hono/request-id` -- generates a UUID and stores it on the Hono context under the key `'requestId'`
40
- 2. `RequestSpyMiddleware` -- logs request start/end with IP, method, path, query, body, and timing
41
-
42
- ### Step 3: Use
43
-
44
- No injection or manual usage is needed. Once the application starts, every incoming request is automatically logged with a unique request ID.
45
-
46
- A sample log output in **non-production** mode looks like this:
25
+ Nothing to configure - once the application starts, every request is logged automatically.
47
26
 
48
27
  ```
49
28
  [SpyMW] [<request-id>][127.0.0.1][=>] GET /hello | query: {} | body: null
50
29
  [SpyMW] [<request-id>][127.0.0.1][<=] GET /hello | Took: 1.23 (ms)
51
30
  ```
52
31
 
53
- In **production** mode (`NODE_ENV=production`), body is excluded but query is still logged:
32
+ In **production** (`NODE_ENV=production`), the body is omitted; query is still logged:
54
33
 
55
34
  ```
56
35
  [SpyMW] [<request-id>][127.0.0.1][=>] GET /hello | query: {}
57
36
  [SpyMW] [<request-id>][127.0.0.1][<=] GET /hello | Took: 1.23 (ms)
58
37
  ```
59
38
 
60
- The log format follows this structure:
61
-
62
39
  | Direction | Format |
63
40
  |-----------|--------|
64
41
  | Incoming (`=>`) | `[requestId][clientIp][=>] METHOD path \| query: {...} \| body: {...}` |
65
42
  | Outgoing (`<=`) | `[requestId][clientIp][<=] METHOD path \| Took: X.XX (ms)` |
66
43
 
67
- The HTTP method is padded to 8 characters for consistent alignment in log output.
68
-
69
- > [!TIP]
70
- > The request ID is also available in the framework's error handlers (`notFoundHandler`, `appErrorHandler`), making it easy to correlate error logs with the original request.
71
-
72
- ## Configuration
73
-
74
- The Request Tracker component has no user-configurable options. Its behavior is fully automatic.
75
-
76
- | Behavior | Description |
77
- |----------|-------------|
78
- | **Request ID** | Generated automatically via `hono/request-id` middleware (UUID), stored on context as `'requestId'` |
79
- | **IP Detection** | Priority: (1) connection info via `getIncomingIp()`, (2) `x-real-ip` header, (3) `x-forwarded-for` header |
80
- | **Body Logging** | Logs request body in non-production environments only. Query is always logged |
81
- | **Timing** | Measures and logs request duration in milliseconds (2 decimal places) via `performance.now()` |
82
- | **Scope** | Registered as a singleton provider |
83
-
84
- > [!NOTE]
85
- > In **production** (`NODE_ENV=production`), only the request **body** is excluded from log output to prevent sensitive data exposure. Query parameters are still logged in all environments.
86
-
87
- ## Internals
88
-
89
- ### RequestTrackerComponent
90
-
91
- The component class extends `BaseComponent`. It receives `BaseApplication` via `@inject({ key: CoreBindings.APPLICATION_INSTANCE })` in its constructor.
44
+ The HTTP method is padded to 8 characters for consistent alignment.
92
45
 
93
- During construction, it creates a singleton binding for the middleware:
46
+ ## How it works
94
47
 
95
- ```typescript
96
- Binding.bind({ key: RequestTrackerComponent.REQUEST_TRACKER_MW_BINDING_KEY })
97
- .toProvider(RequestSpyMiddleware)
98
- .setScope(BindingScopes.SINGLETON)
99
- ```
100
-
101
- The binding key is constructed as `BindingNamespaces.MIDDLEWARE + '.' + RequestSpyMiddleware.name`, which resolves to `'middlewares.RequestSpyMiddleware'`.
102
-
103
- #### Component Lifecycle
104
-
105
- 1. **`constructor()`** -- Receives `BaseApplication` via DI. Defines the middleware binding as a singleton provider.
106
- 2. **`binding()`** -- Registers `requestId()` middleware on the server. Resolves the `RequestSpyMiddleware` binding from the DI container. Throws if the middleware cannot be resolved. Registers the resolved middleware on the server.
107
-
108
- ### RequestSpyMiddleware
109
-
110
- The middleware class extends `BaseHelper` with scope `'SpyMW'` and implements `IProvider<MiddlewareHandler>`.
111
-
112
- ```typescript
113
- class RequestSpyMiddleware extends BaseHelper implements IProvider<MiddlewareHandler> {
114
- static readonly REQUEST_ID_KEY = 'requestId';
115
- private isDebugMode: boolean;
116
- // ...
117
- }
118
- ```
119
-
120
- #### IProvider Pattern
121
-
122
- `RequestSpyMiddleware` implements the `IProvider<T>` interface from `@venizia/ignis-inversion`. This interface requires a single method:
48
+ - **Two middlewares, one component.** `binding()` registers Hono's own `requestId()` (from `hono/request-id`) first, then resolves `RequestSpyMiddleware` from the DI container and registers it - `requestId()` must run first so the spy can read the ID off the context.
49
+ - **IP resolution is best-effort, never fatal.** The middleware tries `getIncomingIp()` (runtime connection info), then `x-real-ip`, then `x-forwarded-for`; if none resolve it logs `'unknown'` instead of failing the request - this middleware observes traffic, it does not gate it.
50
+ - **Body logging is environment-gated.** `RequestSpyMiddleware` reads `NODE_ENV` once in its constructor: any value other than `'production'` logs the body; `'production'` logs query only. Query is always logged in every environment.
51
+ - **Body parsing follows Content-Type.** JSON, multipart, and URL-encoded bodies use Hono's own parsers; `application/octet-stream` returns the raw stream; everything else is read as text. A parse failure throws `'Malformed Body Payload'` (HTTP 400).
52
+ - **The middleware is an `IProvider`, not a plain function.** `RequestSpyMiddleware implements IProvider<MiddlewareHandler>` from `@venizia/ignis-inversion` - the container instantiates the class (so it can hold `isDebugMode` state) and calls `.value()` to obtain the actual Hono handler.
123
53
 
124
- ```typescript
125
- interface IProvider<T> {
126
- value(container: Container): T;
127
- }
128
- ```
129
-
130
- When the DI container resolves the binding (via `.toProvider(RequestSpyMiddleware)`), it instantiates the class and calls `value()` to obtain the actual `MiddlewareHandler`. This pattern allows the middleware to hold state (like `isDebugMode`) while producing a clean middleware function.
54
+ > [!TIP]
55
+ > The request ID is also available in the framework's error handlers (`notFoundHandler`, `AppErrorMiddleware`), making it easy to correlate error logs with the original request.
131
56
 
132
- #### Debug Mode Detection
57
+ ## Common tasks
133
58
 
134
- The constructor checks `process.env.NODE_ENV`:
59
+ ### Correlate logs with a request
60
+ Read the `requestId` context value inside your own handlers or middleware - the same ID appears in every `[SpyMW]` log line for that request.
135
61
 
136
62
  ```typescript
137
- constructor() {
138
- super({ scope: 'SpyMW' });
139
- const env = process.env.NODE_ENV?.toLowerCase();
140
- this.isDebugMode = env !== Environment.PRODUCTION;
141
- }
63
+ const requestId = context.get('requestId');
142
64
  ```
143
65
 
144
- When `isDebugMode` is `true` (any environment other than `'production'`), the incoming request log includes both `query` and `body`. When `false`, only `query` is logged.
145
-
146
- #### value() -- Middleware Handler
147
-
148
- The `value()` method returns a Hono middleware created via `createMiddleware()` from `hono/factory`. The middleware performs the following steps:
149
-
150
- 1. Starts a performance timer via `performance.now()`
151
- 2. Extracts the request ID from the Hono context (set by `requestId()` middleware)
152
- 3. Resolves the client IP using the priority chain (see IP Detection below)
153
- 4. Throws `'Malformed Connection Info'` (400) if both `incomingIp` and `forwardedIp` are `null`
154
- 5. Extracts method, path, and query from the request
155
- 6. Parses the request body via `parseBody()`
156
- 7. Logs the incoming request with `[=>]` direction marker
157
- 8. Calls `await next()` to proceed to the next middleware/handler
158
- 9. Calculates duration and logs the outgoing response with `[<=]` direction marker
159
-
160
- #### parseBody()
161
-
162
- A public method that parses the request body based on `Content-Type` and `Content-Length` headers.
66
+ ### Reuse `parseBody` for your own middleware
67
+ `parseBody` is a public method - reuse it wherever you need the same Content-Type-aware parsing.
163
68
 
164
69
  ```typescript
165
70
  async parseBody(opts: { req: TContext['req'] }): Promise<unknown>
166
71
  ```
167
72
 
168
- **Return conditions:**
73
+ ### Understand the client IP resolution order
74
+ | Priority | Source | Notes |
75
+ |----------|--------|-------|
76
+ | 1 | `getIncomingIp(context)` | Native connection info - `hono/bun` on Bun, `@hono/node-server/conninfo` on Node.js |
77
+ | 2 | `x-real-ip` header | Set by reverse proxies (e.g., Nginx `proxy_set_header X-Real-IP`) |
78
+ | 3 | `x-forwarded-for` header | Standard proxy header |
79
+ | 4 | `'unknown'` | Logged when none of the above resolve - the request still proceeds |
169
80
 
81
+ ### Understand body-parsing outcomes
170
82
  | Condition | Result |
171
83
  |-----------|--------|
172
- | No `Content-Type` header | Returns `null` |
173
- | No `Content-Length` header or value is `'0'` | Returns `null` |
174
- | `Content-Type` includes `application/json` | Calls `req.json()` |
175
- | `Content-Type` includes `multipart/form-data` | Calls `req.parseBody()` |
176
- | `Content-Type` includes `application/x-www-form-urlencoded` | Calls `req.parseBody()` |
177
- | Any other `Content-Type` (text, html, xml, etc.) | Calls `req.text()` |
178
- | Parsing fails for any content type | Throws `'Malformed Body Payload'` (HTTP 400) |
179
-
180
- ### getIncomingIp() Utility
181
-
182
- A utility function that attempts to extract the client IP address from the Hono context using runtime-specific connection info.
183
-
184
- ```typescript
185
- const getIncomingIp = (context: Context): string | null
186
- ```
187
-
188
- **Runtime detection:**
189
- - Uses `RuntimeModules.isBun()` from `@venizia/ignis-helpers` to detect the runtime
190
- - On **Bun**: imports `getConnInfo` from `hono/bun`
191
- - On **Node.js**: imports `getConnInfo` from `@hono/node-server/conninfo`
192
- - Returns `connInfo.remote.address` if available, `null` otherwise
193
- - Returns `null` if `getConnInfo` is unavailable or throws
194
-
195
- #### IP Detection Priority
196
-
197
- The middleware resolves the client IP using a three-step fallback chain:
198
-
199
- | Priority | Source | Description |
200
- |----------|--------|-------------|
201
- | 1 | `getIncomingIp(context)` | Direct connection info from the runtime (Bun or Node.js) |
202
- | 2 | `x-real-ip` header | Set by reverse proxies (e.g., Nginx `proxy_set_header X-Real-IP`) |
203
- | 3 | `x-forwarded-for` header | Standard proxy header with original client IP |
84
+ | No `Content-Type` header | `null` |
85
+ | `Content-Length` is `'0'`, or no body stream present | `null` |
86
+ | `Content-Type` includes `application/json` | `req.json()` |
87
+ | `Content-Type` includes `multipart/form-data` or `application/x-www-form-urlencoded` | `req.parseBody()` |
88
+ | `Content-Type` is `application/octet-stream` | Raw body stream |
89
+ | Any other `Content-Type` (text, html, xml, etc.) | `req.text()` |
90
+ | Parsing throws for any content type | `'Malformed Body Payload'` (HTTP 400) |
204
91
 
205
- The `clientIp` used in log output is resolved as `incomingIp ?? forwardedIp` -- meaning connection info takes precedence when available.
92
+ ## Reference
206
93
 
207
- If **all three** sources return `null`, the middleware throws a `'Malformed Connection Info'` error with HTTP 400 status.
208
-
209
- ## Binding Keys
94
+ ### Configuration
95
+ No user-configurable options - behavior is fully automatic.
210
96
 
97
+ ### Binding keys
211
98
  | Key | Constant | Type | Required | Default |
212
99
  |-----|----------|------|----------|---------|
213
- | `middlewares.RequestSpyMiddleware` | `RequestTrackerComponent.REQUEST_TRACKER_MW_BINDING_KEY` | `MiddlewareHandler` | Auto | Provided by component |
214
-
215
- The key is constructed from `BindingNamespaces.MIDDLEWARE` (`'middlewares'`) + `RequestSpyMiddleware.name` (`'RequestSpyMiddleware'`). The component binds `RequestSpyMiddleware` as a singleton provider at this key during construction.
216
-
217
- ## Troubleshooting
218
-
219
- ### "Invalid middleware to init request tracker | Please check again binding value"
100
+ | `middlewares.RequestSpyMiddleware` | `RequestTrackerComponent.REQUEST_TRACKER_MW_BINDING_KEY` | `MiddlewareHandler` | Auto | Singleton provider, registered by the constructor |
220
101
 
221
- **Cause:** The `RequestSpyMiddleware` binding could not be resolved from the DI container during the component's `binding()` phase. This typically means the binding was removed or overwritten before `binding()` executed.
102
+ The key is built as `BindingNamespaces.MIDDLEWARE` (`'middlewares'`) + `.` + `RequestSpyMiddleware.name`.
222
103
 
223
- **Fix:** Ensure no custom code unbinds or replaces the `middlewares.RequestSpyMiddleware` key. If you need to customize request logging, extend the component rather than removing the binding.
224
-
225
- ### "Malformed Body Payload"
226
-
227
- **Cause:** The `RequestSpyMiddleware` attempted to parse the request body based on the `Content-Type` header, but the body content was malformed (e.g., invalid JSON with `application/json` content type, or corrupt form data).
228
-
229
- **Fix:** Ensure clients send valid body content that matches the declared `Content-Type` header. This error returns HTTP 400 Bad Request.
104
+ ### RequestSpyMiddleware
105
+ ```typescript
106
+ class RequestSpyMiddleware extends BaseHelper implements IProvider<MiddlewareHandler> {
107
+ static readonly REQUEST_ID_KEY = 'requestId';
108
+ private isDebugMode: boolean;
109
+ // ...
110
+ }
111
+ ```
230
112
 
231
- ### "Malformed Connection Info"
113
+ - Extends `BaseHelper` with scope `'SpyMW'`
114
+ - Constructor sets `isDebugMode = process.env.NODE_ENV?.toLowerCase() !== Environment.PRODUCTION`
115
+ - `value()` returns a Hono middleware built via `createMiddleware()` from `hono/factory`
232
116
 
233
- **Cause:** The middleware could not determine the client IP address from any source. All three must have failed: (1) `getIncomingIp()` returned `null` (runtime connection info unavailable), (2) `x-real-ip` header was absent, and (3) `x-forwarded-for` header was absent. This error returns HTTP 400 Bad Request.
117
+ ### Component lifecycle
118
+ 1. **`constructor()`** - Receives `BaseApplication` via DI. Defines the middleware binding as a singleton provider.
119
+ 2. **`binding()`** - Registers `requestId()` on the server. Resolves the `RequestSpyMiddleware` binding, throwing if it cannot be resolved. Registers the resolved middleware on the server.
234
120
 
235
- **Fix:** Ensure your reverse proxy (e.g., Nginx, Caddy) forwards at least one of these headers:
236
- - `x-real-ip`
237
- - `x-forwarded-for`
121
+ ## Troubleshooting
238
122
 
239
- If running without a proxy, ensure the runtime provides connection info (Bun does this natively; Node.js requires `@hono/node-server`).
123
+ | Symptom | Cause | Fix |
124
+ |---------|-------|-----|
125
+ | `Invalid middleware to init request tracker \| Please check again binding value` | `RequestSpyMiddleware` binding was unbound or overwritten before `binding()` ran | Don't unbind or replace `middlewares.RequestSpyMiddleware`; extend the component instead of removing its binding |
126
+ | `Malformed Body Payload` (400) | Body content didn't match its declared `Content-Type` (e.g., invalid JSON with `application/json`) | Ensure clients send body content that matches the `Content-Type` header |
240
127
 
241
- ## See Also
128
+ ## See also
242
129
 
243
130
  - **Guides:**
244
131
  - [Components Overview](/guides/core-concepts/components) - Component system basics
@@ -253,3 +140,9 @@ If running without a proxy, ensure the runtime provides connection info (Bun doe
253
140
  - **Best Practices:**
254
141
  - [Troubleshooting Tips](/best-practices/troubleshooting-tips) - Debugging with request IDs
255
142
  - [Deployment Strategies](/best-practices/deployment-strategies) - Production logging
143
+
144
+ **Files:**
145
+
146
+ - [`packages/core/src/components/request-tracker/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/request-tracker/component.ts) - `RequestTrackerComponent`
147
+ - [`packages/core/src/base/middlewares/request-spy/request-spy.middleware.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/middlewares/request-spy/request-spy.middleware.ts) - `RequestSpyMiddleware`
148
+ - [`packages/core/src/utilities/network.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/utilities/network.utility.ts) - `getIncomingIp()`
@@ -159,6 +159,177 @@ async function createNodeSocketIOHelper(opts: {
159
159
  }
160
160
  ```
161
161
 
162
+ ## Configuration Reference
163
+
164
+ ### Default Server Options
165
+
166
+ The component applies these defaults if `SocketIOBindingKeys.SERVER_OPTIONS` is not bound or partially overridden:
167
+
168
+ | Option | Default | Description |
169
+ |--------|---------|-------------|
170
+ | `identifier` | `'SOCKET_IO_SERVER'` | Unique identifier for the helper instance |
171
+ | `path` | `'/io'` | URL path for Socket.IO handshake/polling |
172
+ | `cors.origin` | `'*'` | Allowed origins (restrict in production!) |
173
+ | `cors.methods` | `['GET', 'POST']` | Allowed HTTP methods for CORS preflight |
174
+ | `cors.preflightContinue` | `false` | Pass preflight to next handler |
175
+ | `cors.optionsSuccessStatus` | `204` | Status code for successful OPTIONS requests |
176
+ | `cors.credentials` | `true` | Allow cookies/auth headers |
177
+ | `perMessageDeflate.threshold` | `4096` | Minimum message size to compress (bytes) |
178
+ | `perMessageDeflate.concurrencyLimit` | `20` | Max concurrent compression operations |
179
+ | `perMessageDeflate.clientNoContextTakeover` | `true` | Client releases compression context after each message |
180
+ | `perMessageDeflate.serverNoContextTakeover` | `true` | Server releases compression context after each message |
181
+ | `perMessageDeflate.serverMaxWindowBits` | `10` | Server-side maximum window size (2^10 = 1KB) |
182
+
183
+ > [!WARNING]
184
+ > The default `cors.origin: '*'` is suitable for development only. In production, restrict this to your specific domains.
185
+
186
+ #### Full `DEFAULT_SERVER_OPTIONS`
187
+ ```typescript
188
+ const DEFAULT_SERVER_OPTIONS: Partial<IServerOptions> = {
189
+ identifier: 'SOCKET_IO_SERVER',
190
+ path: '/io',
191
+ cors: {
192
+ origin: '*',
193
+ methods: ['GET', 'POST'],
194
+ preflightContinue: false,
195
+ optionsSuccessStatus: 204,
196
+ credentials: true,
197
+ },
198
+ perMessageDeflate: {
199
+ threshold: 4096,
200
+ zlibDeflateOptions: { chunkSize: 10 * 1024 },
201
+ zlibInflateOptions: { windowBits: 12, memLevel: 8 },
202
+ clientNoContextTakeover: true,
203
+ serverNoContextTakeover: true,
204
+ serverMaxWindowBits: 10,
205
+ concurrencyLimit: 20,
206
+ },
207
+ };
208
+ ```
209
+
210
+ ### Custom Configuration
211
+
212
+ Bind custom server options before registering the component:
213
+
214
+ ```typescript
215
+ import { SocketIOBindingKeys } from '@venizia/ignis/socket-io';
216
+ import type { ServerOptions } from 'socket.io';
217
+
218
+ const customOptions: Partial<ServerOptions> = {
219
+ path: '/socket.io',
220
+ cors: {
221
+ origin: ['https://myapp.com', 'https://admin.myapp.com'],
222
+ methods: ['GET', 'POST'],
223
+ credentials: true,
224
+ },
225
+ pingTimeout: 60000,
226
+ pingInterval: 25000,
227
+ maxHttpBufferSize: 1e6, // 1MB
228
+ };
229
+
230
+ this.bind<Partial<ServerOptions>>({
231
+ key: SocketIOBindingKeys.SERVER_OPTIONS,
232
+ }).toValue(customOptions);
233
+
234
+ this.component(SocketIOComponent);
235
+ ```
236
+
237
+ > [!NOTE]
238
+ > `identifier` is part of the component's `IServerOptions` interface (which extends `ServerOptions`), not Socket.IO's native options. Set it by including it in the bound options object.
239
+
240
+ ### Binding Keys
241
+
242
+ All binding keys are available on `SocketIOBindingKeys`:
243
+
244
+ | Binding Key | Constant | Type | Required | Default |
245
+ |------------|----------|------|----------|---------|
246
+ | `@app/socket-io/server-options` | `SERVER_OPTIONS` | `Partial<ServerOptions>` | No | See defaults above |
247
+ | `@app/socket-io/redis-connection` | `REDIS_CONNECTION` | `IRedisHelper` (`RedisSingleHelper` / `RedisClusterHelper` / `RedisSentinelHelper`) | **Yes** | `null` |
248
+ | `@app/socket-io/authenticate-handler` | `AUTHENTICATE_HANDLER` | `TSocketIOAuthenticateFn` | **Yes** | `null` |
249
+ | `@app/socket-io/validate-room-handler` | `VALIDATE_ROOM_HANDLER` | `TSocketIOValidateRoomFn` | No | `null` |
250
+ | `@app/socket-io/client-connected-handler` | `CLIENT_CONNECTED_HANDLER` | `TSocketIOClientConnectedFn` | No | `null` |
251
+ | `@app/socket-io/instance` | `SOCKET_IO_INSTANCE` | `SocketIOServerHelper` | -- | *Set by component* |
252
+
253
+ > [!NOTE]
254
+ > `SOCKET_IO_INSTANCE` is **not** set by you -- the component creates and binds it automatically after the server starts. Inject it in services/controllers via a lazy getter (see [Usage & Examples](./usage#server-side-usage)).
255
+
256
+ ### System Events and Rooms
257
+
258
+ Constants are exported from `@venizia/ignis-helpers/socket-io` as `SocketIOConstants` and used internally by both the component and the helper.
259
+
260
+ | Constant | Value | Description |
261
+ |----------|-------|--------------|
262
+ | `EVENT_PING` | `'ping'` | Keep-alive ping emitted at `pingInterval` (default: 30s) |
263
+ | `EVENT_CONNECT` | `'connection'` | New client connected (server-side event) |
264
+ | `EVENT_DISCONNECT` | `'disconnect'` | Client disconnected |
265
+ | `EVENT_JOIN` | `'join'` | Client requests to join room(s) |
266
+ | `EVENT_LEAVE` | `'leave'` | Client requests to leave room(s) |
267
+ | `EVENT_AUTHENTICATE` | `'authenticate'` | Client sends auth credentials |
268
+ | `EVENT_AUTHENTICATED` | `'authenticated'` | Auth success response sent to client |
269
+ | `EVENT_UNAUTHENTICATE` | `'unauthenticated'` | Auth failure response sent to client |
270
+ | `ROOM_DEFAULT` | `'io-default'` | Default room all authenticated clients join |
271
+ | `ROOM_NOTIFICATION` | `'io-notification'` | Notification broadcast room |
272
+
273
+ > [!TIP]
274
+ > Override the default rooms via the `defaultRooms` option on `SocketIOServerHelper` -- the values above are only the fallback.
275
+
276
+ #### Internal Constants (Server Helper)
277
+
278
+ Defined at module scope in the server helper, not exported, but they govern default behavior:
279
+
280
+ | Constant | Value | Description |
281
+ |----------|-------|--------------|
282
+ | `CLIENT_AUTHENTICATE_TIMEOUT` | `10_000` (10s) | Time allowed for a client to authenticate before forced disconnect |
283
+ | `CLIENT_PING_INTERVAL` | `30_000` (30s) | Interval between server-to-client ping emissions |
284
+
285
+ Both are overridable via the `authenticateTimeout` and `pingInterval` constructor options on `SocketIOServerHelper`.
286
+
287
+ ### Client States
288
+
289
+ Each connected client tracks an authentication state that governs what actions are permitted:
290
+
291
+ | State | Constant | Description |
292
+ |-------|----------|--------------|
293
+ | `unauthorized` | `SocketIOClientStates.UNAUTHORIZED` | Initial state -- client must emit `authenticate` within the timeout (default: 10s) |
294
+ | `authenticating` | `SocketIOClientStates.AUTHENTICATING` | Auth in progress -- `authenticateFn` is executing |
295
+ | `authenticated` | `SocketIOClientStates.AUTHENTICATED` | Auth successful -- client can send/receive events and join rooms |
296
+
297
+ #### State Machine Diagram
298
+ ```
299
+ +------------------+
300
+ connect ---------->| unauthorized |
301
+ +--------+---------+
302
+ | emit('authenticate')
303
+ +--------v---------+
304
+ | authenticating |
305
+ +---+----------+---+
306
+ success | | failure
307
+ +---------v--+ +-------v-----------+
308
+ |authenticated| | unauthorized |--> disconnect
309
+ +-------------+ +------------------+
310
+ ^
311
+ timeout (10s)
312
+ ```
313
+
314
+ #### `SocketIOClientStates` Source
315
+ ```typescript
316
+ export class SocketIOClientStates {
317
+ static readonly UNAUTHORIZED = 'unauthorized';
318
+ static readonly AUTHENTICATING = 'authenticating';
319
+ static readonly AUTHENTICATED = 'authenticated';
320
+
321
+ static readonly SCHEME_SET = new Set([
322
+ this.UNAUTHORIZED,
323
+ this.AUTHENTICATING,
324
+ this.AUTHENTICATED,
325
+ ]);
326
+
327
+ static isValid(input: string): input is TConstValue<typeof SocketIOClientStates> {
328
+ return this.SCHEME_SET.has(input);
329
+ }
330
+ }
331
+ ```
332
+
162
333
  ## Server Helper API Reference
163
334
 
164
335
  ### `SocketIOServerHelper` Constructor
@@ -244,9 +415,7 @@ Returns the `@socket.io/bun-engine` instance. **Throws** if the runtime is Node.
244
415
  #### `getClients()`
245
416
 
246
417
  ```typescript
247
- // Overloaded:
248
- getClients(): Map<string, ISocketIOClient>
249
- getClients(opts: { id: string }): ISocketIOClient | undefined
418
+ getClients(opts?: { id?: string }): ISocketIOClient | Map<string, ISocketIOClient> | undefined
250
419
  ```
251
420
 
252
421
  When called without arguments, returns the full client map. When called with `{ id }`, returns the specific client entry or `undefined` if not found.
@@ -290,7 +459,7 @@ Disconnects a specific client and cleans up resources:
290
459
  3. Removes the client from the `clients` map
291
460
  4. Calls `socket.disconnect()` on the underlying Socket.IO socket
292
461
 
293
- If the socket is `undefined` or not tracked in the client map, the method still calls `socket.disconnect()` for safety.
462
+ If `socket` is falsy, the method returns immediately -- no cleanup, no `socket.disconnect()` call. If the socket exists but is not tracked in the `clients` map, steps 1-3 are skipped but `socket.disconnect()` is still called.
294
463
 
295
464
  #### `onClientConnect()`
296
465
 
@@ -338,7 +507,7 @@ send(opts: {
338
507
  data: any; // Event payload
339
508
  };
340
509
  doLog?: boolean; // Log the emission (default: false)
341
- cb?: () => void; // Callback executed via setImmediate after emit
510
+ callback?: () => void; // Executed via setImmediate after emit
342
511
  })
343
512
  ```
344
513
 
@@ -347,7 +516,7 @@ Key behaviors:
347
516
  - All messages are **compressed** via `emitter.compress(true)`
348
517
  - If `destination` is provided and non-empty, sends via `sender.to(destination).emit(topic, data)`
349
518
  - If `destination` is omitted/empty, broadcasts to **all** connected clients via `sender.emit(topic, data)`
350
- - Callback (`cb`) is executed asynchronously via `setImmediate()`, not after delivery confirmation
519
+ - `callback` is executed asynchronously via `setImmediate()`, not after delivery confirmation
351
520
  - Logging is opt-in (`doLog: true`) to avoid noise in high-throughput scenarios
352
521
 
353
522
  #### `send()` Silent Failure Behavior
@@ -575,8 +744,8 @@ Manually disconnects the socket. No-op with an info log if the client is not ini
575
744
  emit<T = unknown>(opts: {
576
745
  topic: string;
577
746
  data: T;
578
- doLog?: boolean; // default: false
579
- cb?: () => void;
747
+ doLog?: boolean; // default: false
748
+ callback?: () => void;
580
749
  }): void
581
750
  ```
582
751
 
@@ -586,7 +755,7 @@ Emits an event to the server.
586
755
  - The socket is not connected (`statusCode: 400`, message: `"Invalid socket client state to emit"`)
587
756
  - The `topic` is falsy (`statusCode: 400`, message: `"Topic is required to emit"`)
588
757
 
589
- If `cb` is provided, it is executed via `setImmediate()` (asynchronously, not after server acknowledgment). If `doLog` is `true`, logs the topic and data.
758
+ If `callback` is provided, it is executed via `setImmediate()` (asynchronously, not after server acknowledgment). If `doLog` is `true`, logs the topic and data.
590
759
 
591
760
  ### `joinRooms()`
592
761
 
@@ -796,7 +965,7 @@ interface ISocketIOClient {
796
965
  socket: IOSocket;
797
966
  state: TSocketIOClientState; // 'unauthorized' | 'authenticating' | 'authenticated'
798
967
  interval?: NodeJS.Timeout; // Ping interval (set after auth)
799
- authenticateTimeout: NodeJS.Timeout; // Auth deadline (cleared on success)
968
+ authenticateTimeout?: NodeJS.Timeout; // Auth deadline (undefined after it's cleared)
800
969
  }
801
970
  ```
802
971
 
@@ -853,7 +1022,7 @@ interface ISocketIOClient {
853
1022
  socket: IOSocket;
854
1023
  state: TSocketIOClientState;
855
1024
  interval?: NodeJS.Timeout;
856
- authenticateTimeout: NodeJS.Timeout;
1025
+ authenticateTimeout?: NodeJS.Timeout;
857
1026
  }
858
1027
 
859
1028
  // Redis client type alias
@@ -892,6 +1061,19 @@ type TSocketIOClientState = TConstValue<typeof SocketIOClientStates>;
892
1061
  ### Callback Types
893
1062
 
894
1063
  ```typescript
1064
+ // Handshake payload passed to the authenticate handler
1065
+ interface IHandshake {
1066
+ headers: IncomingHttpHeaders;
1067
+ time: string;
1068
+ address: string;
1069
+ xdomain: boolean;
1070
+ secure: boolean;
1071
+ issued: number;
1072
+ url: string;
1073
+ query: ParsedUrlQuery;
1074
+ auth: { [key: string]: any };
1075
+ }
1076
+
895
1077
  // Server authentication handler
896
1078
  type TSocketIOAuthenticateFn = (args: IHandshake) => ValueOrPromise<boolean>;
897
1079
 
@@ -1046,6 +1228,6 @@ clientHelper.shutdown()
1046
1228
 
1047
1229
  ## See Also
1048
1230
 
1049
- - [Setup & Configuration](./) -- Quick reference, installation, bindings, constants
1050
- - [Usage & Examples](./usage) -- Server-side usage, client helper, advanced patterns
1231
+ - [Setup & Configuration](./) -- Quick reference, required bindings, how it works
1232
+ - [Usage & Examples](./usage) -- Full setup steps, server-side usage, client helper, advanced patterns
1051
1233
  - [Error Reference](./errors) -- Error conditions and troubleshooting
@@ -116,6 +116,6 @@ socket.on('authenticated', (data) => {
116
116
 
117
117
  ## See Also
118
118
 
119
- - [Setup & Configuration](./) -- Quick reference, installation, bindings, constants
120
- - [Usage & Examples](./usage) -- Server-side usage, client helper, advanced patterns
121
- - [API Reference](./api) -- Architecture, method signatures, internals, types
119
+ - [Setup & Configuration](./) -- Quick reference, required bindings, how it works
120
+ - [Usage & Examples](./usage) -- Full setup steps, server-side usage, client helper, advanced patterns
121
+ - [API Reference](./api) -- Architecture, configuration reference, method signatures, internals, types