@venizia/ignis-docs 0.0.8-3 → 0.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.
- package/README.md +7 -7
- package/{wiki → content}/best-practices/api-usage-examples.md +15 -12
- package/{wiki → content}/best-practices/architectural-patterns.md +70 -78
- package/{wiki → content}/best-practices/architecture-decisions.md +91 -60
- package/{wiki → content}/best-practices/code-style-standards/advanced-patterns.md +56 -44
- package/{wiki → content}/best-practices/code-style-standards/constants-configuration.md +11 -11
- package/{wiki → content}/best-practices/code-style-standards/control-flow.md +5 -2
- package/{wiki → content}/best-practices/code-style-standards/documentation.md +13 -13
- package/{wiki → content}/best-practices/code-style-standards/function-patterns.md +9 -10
- package/{wiki → content}/best-practices/code-style-standards/index.md +1 -1
- package/{wiki → content}/best-practices/code-style-standards/naming-conventions.md +10 -8
- package/{wiki → content}/best-practices/code-style-standards/route-definitions.md +30 -12
- package/{wiki → content}/best-practices/code-style-standards/tooling.md +8 -5
- package/{wiki → content}/best-practices/code-style-standards/type-safety.md +13 -12
- package/{wiki → content}/best-practices/common-pitfalls.md +56 -37
- package/{wiki → content}/best-practices/contribution-workflow.md +13 -14
- package/{wiki → content}/best-practices/data-modeling.md +44 -20
- package/{wiki → content}/best-practices/deployment-strategies.md +28 -27
- package/{wiki → content}/best-practices/error-handling.md +48 -24
- package/{wiki → content}/best-practices/index.md +5 -5
- package/{wiki → content}/best-practices/performance-optimization.md +36 -28
- package/{wiki → content}/best-practices/security-guidelines.md +52 -23
- package/{wiki → content}/best-practices/testing-strategies.md +65 -51
- package/{wiki → content}/best-practices/troubleshooting-tips.md +24 -24
- package/{wiki/extensions/components/swagger.md → content/extensions/components/api-reference.md} +40 -31
- package/{wiki → content}/extensions/components/authentication/api.md +19 -19
- package/{wiki → content}/extensions/components/authentication/errors.md +7 -7
- package/{wiki → content}/extensions/components/authentication/index.md +10 -8
- package/{wiki → content}/extensions/components/authentication/usage.md +101 -6
- package/{wiki → content}/extensions/components/authorization/api.md +45 -25
- package/{wiki → content}/extensions/components/authorization/errors.md +6 -6
- package/{wiki → content}/extensions/components/authorization/index.md +11 -10
- package/{wiki → content}/extensions/components/authorization/usage.md +21 -21
- package/{wiki → content}/extensions/components/health-check.md +1 -1
- package/{wiki → content}/extensions/components/index.md +5 -5
- package/{wiki → content}/extensions/components/mail/errors.md +15 -15
- package/{wiki → content}/extensions/components/mail/index.md +1 -2
- package/{wiki → content}/extensions/components/mail/usage.md +1 -1
- package/{wiki → content}/extensions/components/request-tracker.md +1 -1
- package/{wiki → content}/extensions/components/socket-io/api.md +9 -9
- package/{wiki → content}/extensions/components/socket-io/errors.md +5 -5
- package/{wiki → content}/extensions/components/socket-io/index.md +8 -8
- package/{wiki → content}/extensions/components/socket-io/usage.md +1 -1
- package/{wiki → content}/extensions/components/static-asset/api.md +17 -4
- package/{wiki → content}/extensions/components/static-asset/errors.md +4 -4
- package/{wiki → content}/extensions/components/static-asset/index.md +26 -28
- package/{wiki → content}/extensions/components/static-asset/usage.md +13 -12
- package/{wiki → content}/extensions/components/template/index.md +2 -2
- package/{wiki → content}/extensions/components/template/setup-page.md +1 -1
- package/{wiki → content}/extensions/components/websocket/api.md +3 -3
- package/{wiki → content}/extensions/components/websocket/errors.md +5 -5
- package/{wiki → content}/extensions/components/websocket/index.md +5 -5
- package/{wiki → content}/extensions/components/websocket/usage.md +3 -3
- package/{wiki → content}/extensions/helpers/cron/index.md +2 -2
- package/{wiki → content}/extensions/helpers/crypto/index.md +1 -1
- package/{wiki → content}/extensions/helpers/env/index.md +27 -12
- package/content/extensions/helpers/error/index.md +283 -0
- package/{wiki → content}/extensions/helpers/index.md +2 -3
- package/{wiki → content}/extensions/helpers/inversion/index.md +15 -7
- package/{wiki → content}/extensions/helpers/kafka/examples.md +1 -1
- package/{wiki → content}/extensions/helpers/logger/index.md +32 -2
- package/{wiki → content}/extensions/helpers/network/index.md +6 -0
- package/{wiki → content}/extensions/helpers/queue/index.md +14 -17
- package/content/extensions/helpers/redis/index.md +713 -0
- package/{wiki → content}/extensions/helpers/socket-io/index.md +14 -10
- package/{wiki → content}/extensions/helpers/storage/api.md +44 -8
- package/{wiki → content}/extensions/helpers/storage/index.md +43 -7
- package/{wiki → content}/extensions/helpers/template/index.md +6 -3
- package/{wiki → content}/extensions/helpers/types/index.md +11 -8
- package/{wiki → content}/extensions/helpers/websocket/api.md +9 -9
- package/{wiki → content}/extensions/helpers/websocket/index.md +7 -7
- package/{wiki → content}/extensions/helpers/worker-thread/index.md +2 -2
- package/{wiki → content}/extensions/index.md +3 -4
- package/{wiki → content}/extensions/src-details/mcp-server.md +18 -24
- package/{wiki → content}/guides/core-concepts/application/bootstrapping.md +11 -14
- package/{wiki → content}/guides/core-concepts/application/index.md +3 -3
- package/{wiki → content}/guides/core-concepts/components.md +19 -10
- package/{wiki → content}/guides/core-concepts/dependency-injection.md +6 -3
- package/{wiki → content}/guides/core-concepts/grpc-controllers.md +6 -5
- package/{wiki → content}/guides/core-concepts/persistent/datasources.md +33 -27
- package/{wiki → content}/guides/core-concepts/persistent/index.md +16 -5
- package/{wiki → content}/guides/core-concepts/persistent/models.md +24 -20
- package/content/guides/core-concepts/persistent/postgres-drivers.md +167 -0
- package/{wiki → content}/guides/core-concepts/persistent/repositories.md +40 -23
- package/content/guides/core-concepts/persistent/search-meilisearch.md +183 -0
- package/content/guides/core-concepts/persistent/search-typesense.md +429 -0
- package/{wiki → content}/guides/core-concepts/persistent/transactions.md +61 -25
- package/{wiki → content}/guides/core-concepts/rest-controllers.md +12 -9
- package/content/guides/core-concepts/services.md +389 -0
- package/{wiki → content}/guides/get-started/5-minute-quickstart.md +19 -19
- package/{wiki → content}/guides/get-started/philosophy.md +36 -36
- package/{wiki → content}/guides/get-started/setup.md +3 -3
- package/{wiki → content}/guides/index.md +3 -3
- package/content/guides/migrations/redis-helpers-migration.md +177 -0
- package/{wiki → content}/guides/migrations/scoped-rbac-migration.md +17 -17
- package/content/guides/migrations/unified-connectors-migration.md +113 -0
- package/{wiki → content}/guides/reference/glossary.md +19 -12
- package/{wiki → content}/guides/reference/mcp-docs-server.md +22 -18
- package/{wiki → content}/guides/tutorials/building-a-crud-api.md +30 -33
- package/{wiki → content}/guides/tutorials/complete-installation.md +17 -17
- package/{wiki → content}/guides/tutorials/ecommerce-api.md +158 -119
- package/{wiki → content}/guides/tutorials/realtime-chat.md +176 -130
- package/content/guides/tutorials/testing.md +264 -0
- package/content/index.md +5 -0
- package/content/public/apple-touch-icon.png +0 -0
- package/content/public/og-image.png +0 -0
- package/content/public/site.webmanifest +11 -0
- package/{wiki → content}/references/base/application.md +4 -5
- package/{wiki → content}/references/base/bootstrapping.md +18 -5
- package/{wiki → content}/references/base/components.md +149 -120
- package/content/references/base/connectors.md +178 -0
- package/{wiki → content}/references/base/controllers.md +41 -30
- package/content/references/base/datasources.md +527 -0
- package/{wiki → content}/references/base/dependency-injection.md +34 -22
- package/{wiki → content}/references/base/filter-system/application-usage.md +17 -14
- package/{wiki → content}/references/base/filter-system/array-operators.md +7 -2
- package/{wiki → content}/references/base/filter-system/comparison-operators.md +3 -0
- package/{wiki → content}/references/base/filter-system/default-filter.md +89 -71
- package/{wiki → content}/references/base/filter-system/fields-order-pagination.md +22 -22
- package/{wiki → content}/references/base/filter-system/index.md +6 -3
- package/{wiki → content}/references/base/filter-system/json-filtering.md +20 -1
- package/{wiki → content}/references/base/filter-system/list-operators.md +1 -1
- package/{wiki → content}/references/base/filter-system/logical-operators.md +33 -1
- package/{wiki → content}/references/base/filter-system/null-operators.md +30 -1
- package/{wiki → content}/references/base/filter-system/quick-reference.md +23 -4
- package/{wiki → content}/references/base/filter-system/tips.md +5 -5
- package/{wiki → content}/references/base/filter-system/use-cases.md +12 -12
- package/{wiki → content}/references/base/grpc-controllers.md +13 -13
- package/{wiki → content}/references/base/index.md +24 -12
- package/{wiki/references/base/middleware.md → content/references/base/middlewares.md} +205 -24
- package/{wiki → content}/references/base/models.md +63 -49
- package/{wiki → content}/references/base/providers.md +136 -130
- package/{wiki → content}/references/base/repositories/advanced.md +59 -58
- package/{wiki → content}/references/base/repositories/index.md +115 -91
- package/content/references/base/repositories/mixins.md +99 -0
- package/{wiki → content}/references/base/repositories/relations.md +54 -64
- package/{wiki → content}/references/base/repositories/soft-deletable.md +31 -30
- package/content/references/base/services.md +404 -0
- package/{wiki → content}/references/configuration/environment-variables.md +46 -30
- package/{wiki → content}/references/configuration/index.md +6 -6
- package/{wiki → content}/references/index.md +17 -12
- package/{wiki → content}/references/quick-reference.md +65 -106
- package/content/references/utilities/crypto.md +98 -0
- package/{wiki → content}/references/utilities/index.md +3 -3
- package/{wiki → content}/references/utilities/jsx.md +6 -4
- package/content/references/utilities/module.md +90 -0
- package/{wiki → content}/references/utilities/parse.md +4 -14
- package/{wiki → content}/references/utilities/promise.md +9 -7
- package/{wiki → content}/references/utilities/schema.md +5 -3
- package/dist/mcp-server/common/guards.d.ts +8 -0
- package/dist/mcp-server/common/guards.d.ts.map +1 -0
- package/dist/mcp-server/common/guards.js +14 -0
- package/dist/mcp-server/common/guards.js.map +1 -0
- package/dist/mcp-server/common/index.d.ts +1 -0
- package/dist/mcp-server/common/index.d.ts.map +1 -1
- package/dist/mcp-server/common/index.js +1 -0
- package/dist/mcp-server/common/index.js.map +1 -1
- package/dist/mcp-server/common/paths.d.ts.map +1 -1
- package/dist/mcp-server/common/paths.js +2 -2
- package/dist/mcp-server/common/paths.js.map +1 -1
- package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
- package/dist/mcp-server/helpers/docs.helper.js +4 -2
- package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
- package/dist/mcp-server/helpers/github.helper.js +1 -1
- package/dist/mcp-server/index.js +7 -2
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/mcp-server/tools/base.tool.d.ts +6 -2
- package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/base.tool.js.map +1 -1
- package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
- package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
- package/package.json +12 -12
- package/wiki/extensions/helpers/error/index.md +0 -227
- package/wiki/extensions/helpers/redis/index.md +0 -488
- package/wiki/extensions/helpers/testing/index.md +0 -510
- package/wiki/guides/core-concepts/services.md +0 -119
- package/wiki/guides/tutorials/testing.md +0 -722
- package/wiki/index.md +0 -183
- package/wiki/references/base/datasources.md +0 -454
- package/wiki/references/base/middlewares.md +0 -590
- package/wiki/references/base/repositories/mixins.md +0 -335
- package/wiki/references/base/services.md +0 -201
- package/wiki/references/utilities/crypto.md +0 -56
- package/wiki/references/utilities/module.md +0 -42
- /package/{wiki → content}/extensions/components/mail/api.md +0 -0
- /package/{wiki → content}/extensions/components/template/api-page.md +0 -0
- /package/{wiki → content}/extensions/components/template/errors-page.md +0 -0
- /package/{wiki → content}/extensions/components/template/single-page.md +0 -0
- /package/{wiki → content}/extensions/components/template/usage-page.md +0 -0
- /package/{wiki → content}/extensions/helpers/kafka/admin.md +0 -0
- /package/{wiki → content}/extensions/helpers/kafka/consumer.md +0 -0
- /package/{wiki → content}/extensions/helpers/kafka/index.md +0 -0
- /package/{wiki → content}/extensions/helpers/kafka/producer.md +0 -0
- /package/{wiki → content}/extensions/helpers/kafka/schema-registry.md +0 -0
- /package/{wiki → content}/extensions/helpers/network/api.md +0 -0
- /package/{wiki → content}/extensions/helpers/socket-io/api.md +0 -0
- /package/{wiki → content}/extensions/helpers/template/single-page.md +0 -0
- /package/{wiki → content}/extensions/helpers/uid/index.md +0 -0
- /package/{wiki → content}/guides/core-concepts/components-guide.md +0 -0
- /package/{wiki → content}/public/logo.svg +0 -0
- /package/{wiki → content}/references/base/filter-system/pattern-matching.md +0 -0
- /package/{wiki → content}/references/base/filter-system/range-operators.md +0 -0
- /package/{wiki → content}/references/utilities/date.md +0 -0
- /package/{wiki → content}/references/utilities/performance.md +0 -0
- /package/{wiki → content}/references/utilities/request.md +0 -0
- /package/{wiki → content}/references/utilities/statuses.md +0 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# Error
|
|
2
|
+
|
|
3
|
+
Standardized error class and factory for throwing HTTP-aware errors with machine-readable codes across the application.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
| Item | Value |
|
|
8
|
+
|------|-------|
|
|
9
|
+
| **Package** | `@venizia/ignis-helpers` |
|
|
10
|
+
| **Class** | `ApplicationError` |
|
|
11
|
+
| **Extends** | `Error` (native) |
|
|
12
|
+
| **Runtimes** | Both |
|
|
13
|
+
|
|
14
|
+
#### Import Paths
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { ApplicationError, getError, isApplicationError } from '@venizia/ignis-helpers';
|
|
18
|
+
import { ErrorSchema } from '@venizia/ignis-helpers';
|
|
19
|
+
import type { TError } from '@venizia/ignis-helpers';
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Creating an Instance
|
|
23
|
+
|
|
24
|
+
The canonical way to raise an error in IGNIS is the standalone `getError()` factory - use it everywhere (house rule: `getError`, never `new Error`). `ApplicationError` extends the native `Error` class with an HTTP `statusCode` and an optional `messageCode` for machine-readable error identification; `getError()` constructs one for you.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { getError, HTTP } from '@venizia/ignis-helpers';
|
|
28
|
+
|
|
29
|
+
throw getError({
|
|
30
|
+
message: 'User not found',
|
|
31
|
+
statusCode: HTTP.ResultCodes.RS_4.NotFound,
|
|
32
|
+
messageCode: 'core.user.not_found',
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### Options (`TError`)
|
|
37
|
+
|
|
38
|
+
The same options apply to `getError()`, the `ApplicationError` constructor, and the static factory:
|
|
39
|
+
|
|
40
|
+
| Option | Type | Default | Description |
|
|
41
|
+
|--------|------|---------|-------------|
|
|
42
|
+
| `message` | `string` | -- (required) | Human-readable error message |
|
|
43
|
+
| `statusCode` | `number` | `400` | HTTP status code |
|
|
44
|
+
| `messageCode` | `string` | `MessageCode.DEFAULT` (`'core.system_error'`) | Machine-readable error code for client-side handling. Always resolved through `MessageCode.resolve()` -- never left `undefined`, and lower-cased regardless of what was passed in |
|
|
45
|
+
| `name` | `string` | `undefined` | Accepted by the schema but discarded by the constructor (the native `Error` name is kept) |
|
|
46
|
+
|
|
47
|
+
> [!TIP]
|
|
48
|
+
> The `TError` type is derived from `ErrorSchema` (a Zod schema) and uses `.catchall(z.any())`, so you can pass additional arbitrary properties beyond the four listed above. Extra properties are collected into the `extra` field on the resulting `ApplicationError` instance.
|
|
49
|
+
|
|
50
|
+
#### `ApplicationError` Constructor and Static Factory
|
|
51
|
+
|
|
52
|
+
`getError()` is the canonical form. The class constructor and the static `ApplicationError.getError()` are legal equivalents - use them only when a direct class reference reads better:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { ApplicationError, HTTP } from '@venizia/ignis-helpers';
|
|
56
|
+
|
|
57
|
+
// Class constructor (equivalent to getError)
|
|
58
|
+
const error = new ApplicationError({
|
|
59
|
+
message: 'Configuration missing',
|
|
60
|
+
statusCode: HTTP.ResultCodes.RS_5.InternalServerError,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Static factory (equivalent to getError)
|
|
64
|
+
throw ApplicationError.getError({
|
|
65
|
+
message: 'Invalid credentials',
|
|
66
|
+
statusCode: HTTP.ResultCodes.RS_4.Unauthorized,
|
|
67
|
+
messageCode: 'core.auth.invalid_credentials',
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### MessageCode
|
|
72
|
+
|
|
73
|
+
`MessageCode` builds and normalizes the machine-readable codes carried on `ApplicationError.messageCode`. Every code an application throws should be constructed with `MessageCode.build()` rather than typed as a raw string literal -- a malformed code fails at module load (import time) instead of shipping dead into production.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { MessageCode } from '@venizia/ignis-helpers';
|
|
77
|
+
|
|
78
|
+
export class UserErrorCodes {
|
|
79
|
+
static readonly NOT_FOUND = MessageCode.build({ parts: ['core', 'user', 'not_found'] });
|
|
80
|
+
static readonly DUPLICATE_EMAIL = MessageCode.build({ parts: ['core', 'user', 'duplicate_email'] });
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Member | Type | Value / Signature | Description |
|
|
85
|
+
|--------|------|--------------------|-------------|
|
|
86
|
+
| `DEFAULT` | `string` | `'core.system_error'` | The code `ApplicationError` falls back to when no `messageCode` is supplied |
|
|
87
|
+
| `SEPARATOR` | `string` | `'.'` | Joins segments into a dotted code (e.g. `core.mail.send_failed`) |
|
|
88
|
+
| `SEGMENT_PATTERN` | `RegExp` | `/^[a-z0-9]+(_[a-z0-9]+)*$/` | Each segment must be lower snake_case -- `a-z`, `0-9`, `_` |
|
|
89
|
+
| `MIN_SEGMENTS` | `number` | `2` | A code needs at least a namespace and a reason (e.g. `core.not_found` is valid, `not_found` alone is not) |
|
|
90
|
+
| `build(opts: { parts: Array<string> })` | `string` | -- | Joins `parts` with `SEPARATOR` and lower-cases the result. Throws (via `getError`) if `parts.length < MIN_SEGMENTS`, or if any segment fails `SEGMENT_PATTERN` |
|
|
91
|
+
| `isValid(code: string)` | `boolean` | -- | Cheap structural check for a code arriving from outside the process |
|
|
92
|
+
| `resolve(code?: string)` | `string` | -- | Normalizes an absent or empty code to `DEFAULT`; otherwise lower-cases `code` |
|
|
93
|
+
|
|
94
|
+
> [!IMPORTANT]
|
|
95
|
+
> `ApplicationError`'s constructor always calls `MessageCode.resolve(messageCode)` -- so `error.messageCode` is **never** `undefined`, and it is **always lower-cased** regardless of the casing passed to `getError()`. A comparison like `error.messageCode === 'DUPLICATE_EMAIL'` is always false; compare against `'duplicate_email'` (or the exact string returned by `MessageCode.build()`).
|
|
96
|
+
|
|
97
|
+
## Checking for an Application Error
|
|
98
|
+
|
|
99
|
+
Use `isApplicationError(error)` to recognize an application error by **shape** - an `Error` instance carrying a numeric `statusCode` - rather than by class identity:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { isApplicationError } from '@venizia/ignis-helpers';
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
await someOperation();
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (isApplicationError(error)) {
|
|
108
|
+
// already shaped (has a statusCode) - surface as-is
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
// an unknown failure - sanitize before rethrowing
|
|
112
|
+
throw getError({ message: 'Operation failed', statusCode: HTTP.ResultCodes.RS_5.InternalServerError });
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
> [!WARNING]
|
|
117
|
+
> Never compare `ApplicationError` with `instanceof` across a package boundary. `inversion` ships **dual CJS + ESM** builds (its DI powers frontend libraries), so its error class deliberately has more than one runtime identity, and `helpers` keeps its own `ApplicationError` for the backend stack. Two objects that are both "an application error" can be instances of different classes, so `instanceof` gives false negatives. `isApplicationError` checks the shape and works regardless of which package threw. The search connectors use it to decide what is already shaped versus what must be sanitized as a `503`.
|
|
118
|
+
|
|
119
|
+
## Usage
|
|
120
|
+
|
|
121
|
+
### Throwing Errors in Services
|
|
122
|
+
|
|
123
|
+
The most common pattern is throwing `ApplicationError` from service methods to signal HTTP-level failures. The framework's error handling middleware catches these and formats the response automatically.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { getError, HTTP } from '@venizia/ignis-helpers';
|
|
127
|
+
|
|
128
|
+
class AuthenticationService {
|
|
129
|
+
async signUp(opts: { username: string; credential: string }) {
|
|
130
|
+
const existingUser = await this.userRepository.findByUsername(opts.username);
|
|
131
|
+
if (existingUser) {
|
|
132
|
+
throw getError({
|
|
133
|
+
statusCode: HTTP.ResultCodes.RS_4.Conflict,
|
|
134
|
+
message: 'Username already exists',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// ...
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Using `messageCode` for Client-Side Handling
|
|
143
|
+
|
|
144
|
+
The `messageCode` field allows frontend applications to map errors to localized messages or specific UI behaviors without parsing the human-readable `message` string.
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
throw getError({
|
|
148
|
+
message: 'Email verification required before login',
|
|
149
|
+
statusCode: HTTP.ResultCodes.RS_4.Forbidden,
|
|
150
|
+
messageCode: 'auth.email_not_verified',
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Error Response Format
|
|
155
|
+
|
|
156
|
+
The built-in `appErrorHandler` middleware (from `@venizia/ignis`) catches `ApplicationError` instances and formats them into consistent JSON responses. The response shape differs by environment.
|
|
157
|
+
|
|
158
|
+
#### Sanitized (Production-Class) Response
|
|
159
|
+
|
|
160
|
+
The handler is **fail-closed**: it exposes internals only when `NODE_ENV` names a development environment - one of `local`, `debug`, `development`, `dev`, `sit` (`Environment.DEVELOPMENT_ENVS`). Everything else is sanitized as production: `production`, and also `alpha`, `beta`, `uat`, `staging`, an unrecognized name, and an unset `NODE_ENV`.
|
|
161
|
+
|
|
162
|
+
In a sanitized response, `stack` and `cause` are omitted from `details`. For unexpected errors without a `statusCode` (i.e., not thrown via `getError`), the raw message is replaced with a generic `"Internal Server Error"`, and a database error keeps only its base message - no SQL detail, no table or constraint name.
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"message": "User not found",
|
|
167
|
+
"messageCode": "core.user.not_found",
|
|
168
|
+
"statusCode": 404,
|
|
169
|
+
"requestId": "abc-123-def",
|
|
170
|
+
"details": {
|
|
171
|
+
"url": "http://localhost:3000/api/users/123",
|
|
172
|
+
"path": "/api/users/123"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### Development Response
|
|
178
|
+
|
|
179
|
+
In a development environment (see the list above), `details` additionally includes debugging fields:
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"message": "User not found",
|
|
184
|
+
"messageCode": "core.user.not_found",
|
|
185
|
+
"statusCode": 404,
|
|
186
|
+
"requestId": "abc-123-def",
|
|
187
|
+
"details": {
|
|
188
|
+
"url": "http://localhost:3000/api/users/123",
|
|
189
|
+
"path": "/api/users/123",
|
|
190
|
+
"stack": "Error: User not found\n at ...",
|
|
191
|
+
"cause": "..."
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### ErrorSchema (Zod)
|
|
197
|
+
|
|
198
|
+
`ErrorSchema` is a Zod object schema used for OpenAPI response documentation. It is typically referenced in route definitions to describe error responses.
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { ErrorSchema, HTTP } from '@venizia/ignis-helpers';
|
|
202
|
+
|
|
203
|
+
// In route definition responses
|
|
204
|
+
const responses = {
|
|
205
|
+
[HTTP.ResultCodes.RS_4.NotFound]: {
|
|
206
|
+
description: 'Resource not found',
|
|
207
|
+
content: {
|
|
208
|
+
'application/json': { schema: ErrorSchema },
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The schema shape:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
const ErrorSchema = z
|
|
218
|
+
.object({
|
|
219
|
+
name: z.string().optional(),
|
|
220
|
+
statusCode: z.number().optional(),
|
|
221
|
+
messageCode: z.string().optional(),
|
|
222
|
+
message: z.string(),
|
|
223
|
+
})
|
|
224
|
+
.catchall(z.any());
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Common Status Code Patterns
|
|
228
|
+
|
|
229
|
+
| Scenario | Status Code | `HTTP.ResultCodes` Path |
|
|
230
|
+
|----------|-------------|-------------------------|
|
|
231
|
+
| Invalid input / bad request | 400 | `RS_4.BadRequest` |
|
|
232
|
+
| Missing or invalid auth | 401 | `RS_4.Unauthorized` |
|
|
233
|
+
| Insufficient permissions | 403 | `RS_4.Forbidden` |
|
|
234
|
+
| Resource not found | 404 | `RS_4.NotFound` |
|
|
235
|
+
| Duplicate resource | 409 | `RS_4.Conflict` |
|
|
236
|
+
| Validation error | 422 | `RS_4.UnprocessableEntity` |
|
|
237
|
+
| Server failure | 500 | `RS_5.InternalServerError` |
|
|
238
|
+
|
|
239
|
+
## Troubleshooting
|
|
240
|
+
|
|
241
|
+
### `statusCode` defaults to 400
|
|
242
|
+
|
|
243
|
+
**Cause:** `getError()` was called without specifying a `statusCode`. The `ApplicationError` constructor defaults to `400` (Bad Request).
|
|
244
|
+
|
|
245
|
+
**Fix:** Always provide an explicit status code using `HTTP.ResultCodes`:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
throw getError({
|
|
249
|
+
message: 'Resource not found',
|
|
250
|
+
statusCode: HTTP.ResultCodes.RS_4.NotFound,
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Error response missing `stack` and `cause`
|
|
255
|
+
|
|
256
|
+
**Cause:** `NODE_ENV` is not one of the development environments, so the handler sanitized the response (`url` and `path` are always included). Note this is what happens on `alpha`/`staging`, on a typo'd name, and when `NODE_ENV` is unset - not only on `production`.
|
|
257
|
+
|
|
258
|
+
**Fix:** Set `NODE_ENV` to a development name - `development`, `dev`, `local`, `debug` or `sit`. Anything else stays sanitized by design.
|
|
259
|
+
|
|
260
|
+
### Errors returning 500 instead of expected status code
|
|
261
|
+
|
|
262
|
+
**Cause:** A plain `Error` (not `ApplicationError`) was thrown. The `appErrorHandler` middleware only reads `statusCode` from errors that have that property. Native `Error` instances default to `500`.
|
|
263
|
+
|
|
264
|
+
**Fix:** Use `getError()` or `new ApplicationError()` instead of `new Error()`:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
// Incorrect -- will return 500
|
|
268
|
+
throw new Error('Not found');
|
|
269
|
+
|
|
270
|
+
// Correct -- will return 404
|
|
271
|
+
throw getError({
|
|
272
|
+
message: 'Not found',
|
|
273
|
+
statusCode: HTTP.ResultCodes.RS_4.NotFound,
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## See Also
|
|
278
|
+
|
|
279
|
+
- [Controllers](/references/base/controllers) -- Throwing errors in route handlers
|
|
280
|
+
- [Services](/references/base/services) -- Error handling in business logic
|
|
281
|
+
- [Middlewares](/references/base/middlewares) -- The `appErrorHandler` middleware
|
|
282
|
+
- [Helpers Index](/extensions/helpers/) -- All available helpers
|
|
283
|
+
- [Logger Helper](/extensions/helpers/logger/) -- Logging errors
|
|
@@ -11,7 +11,7 @@ Reusable classes and functions providing common functionality - designed for eas
|
|
|
11
11
|
| [Crypto](./crypto/) | Cryptographic operations | AES/RSA/ECDH encryption, key exchange, hashing |
|
|
12
12
|
| [Environment](./env/) | Environment variables | Centralized config access |
|
|
13
13
|
| [Error](./error/) | Error handling | `ApplicationError`, consistent responses |
|
|
14
|
-
| [Inversion](./inversion/) | Dependency injection |
|
|
14
|
+
| [Inversion](./inversion/) | Dependency injection | IoC container - separate package `@venizia/ignis-inversion` |
|
|
15
15
|
| [Logger](./logger/) | Logging | Winston-based, multiple transports, scopes |
|
|
16
16
|
| [Network](./network/) | Network requests | HTTP, TCP, UDP helpers |
|
|
17
17
|
| [Kafka](./kafka/) | Event streaming | Apache Kafka producer/consumer/admin/schema registry |
|
|
@@ -19,8 +19,7 @@ Reusable classes and functions providing common functionality - designed for eas
|
|
|
19
19
|
| [Redis](./redis/) | Redis operations | Single/cluster, key-value, hashes, JSON, pub/sub |
|
|
20
20
|
| [Socket.IO](./socket-io/) | Real-time communication | Socket.IO client/server helpers |
|
|
21
21
|
| [WebSocket](./websocket/) | Real-time communication | Bun native WebSocket server/emitter, Redis scaling |
|
|
22
|
-
| [Storage](./storage/) | File storage | In-memory,
|
|
23
|
-
| [Testing](./testing/) | Test utilities | Test plan runner, base test classes |
|
|
22
|
+
| [Storage](./storage/) | File storage | In-memory, disk, MinIO, Bun S3 object storage |
|
|
24
23
|
| [UID](./uid/) | Unique ID generation | Snowflake IDs, Base62 encoding |
|
|
25
24
|
| [Worker Thread](./worker-thread/) | Worker threads | Node.js worker management |
|
|
26
25
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Inversion (DI)
|
|
2
2
|
|
|
3
|
-
Standalone IoC container with decorator-based injection, fluent binding API, and singleton/transient scoping -- the foundation layer for all
|
|
3
|
+
Standalone IoC container with decorator-based injection, fluent binding API, and singleton/transient scoping -- the foundation layer for all IGNIS packages.
|
|
4
4
|
|
|
5
5
|
## Quick Reference
|
|
6
6
|
|
|
@@ -50,7 +50,7 @@ import type {
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
> [!NOTE]
|
|
53
|
-
> The framework package `@venizia/ignis` re-exports DI-specific symbols (`Binding`, `BindingKeys`, `BindingScopes`, `BindingValueTypes`, `IProvider`, `isClass`, `isClassProvider`, `
|
|
53
|
+
> The framework package `@venizia/ignis` re-exports DI-specific symbols (`Binding`, `BindingKeys`, `BindingScopes`, `BindingValueTypes`, `IProvider`, `isClass`, `isClassProvider`, `TBindingScope`, `TBindingValueType`, `IBindingTag`) from `@venizia/ignis-inversion` and adds higher-level helpers (`app.controller()`, `app.service()`, etc.). All types from inversion are also available via type-only re-exports.
|
|
54
54
|
|
|
55
55
|
## Creating an Instance
|
|
56
56
|
|
|
@@ -160,7 +160,7 @@ import { inject, injectable, BindingScopes } from '@venizia/ignis-inversion';
|
|
|
160
160
|
class UserService {
|
|
161
161
|
constructor(
|
|
162
162
|
@inject({ key: 'repositories.UserRepository' })
|
|
163
|
-
private
|
|
163
|
+
private userRepository: UserRepository,
|
|
164
164
|
|
|
165
165
|
@inject({ key: 'services.Logger', isOptional: true })
|
|
166
166
|
private logger?: Logger,
|
|
@@ -168,7 +168,10 @@ class UserService {
|
|
|
168
168
|
}
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
The container reads `@inject` metadata during `instantiate()
|
|
171
|
+
The container reads `@inject` metadata during `instantiate()` by constructor index (the `Reflect`-stored array is already index-keyed - no sort is performed), resolves each dependency, and passes them as constructor arguments.
|
|
172
|
+
|
|
173
|
+
> [!IMPORTANT]
|
|
174
|
+
> Every constructor parameter of a container-instantiated class must carry `@inject`. Mixing decorated and undecorated parameters is refused: an undecorated parameter leaves a hole in the index-keyed metadata array, and there is no channel through which the container could supply it anyway. `instantiate()` throws `[ClassName] Constructor parameter N has no @inject | Every parameter of a container-instantiated class must be decorated - the container cannot supply an undecorated one`.
|
|
172
175
|
|
|
173
176
|
### Property Injection
|
|
174
177
|
|
|
@@ -178,7 +181,7 @@ import { inject, injectable } from '@venizia/ignis-inversion';
|
|
|
178
181
|
@injectable({})
|
|
179
182
|
class UserService {
|
|
180
183
|
@inject({ key: 'repositories.UserRepository' })
|
|
181
|
-
private
|
|
184
|
+
private userRepository: UserRepository;
|
|
182
185
|
|
|
183
186
|
@inject({ key: 'services.Logger', isOptional: true })
|
|
184
187
|
private logger?: Logger;
|
|
@@ -189,7 +192,7 @@ class UserService {
|
|
|
189
192
|
> Property-injected classes must be instantiated through the container (`container.resolve()` or `container.instantiate()`). Using `new MyClass()` directly will leave `@inject` properties as `undefined`.
|
|
190
193
|
|
|
191
194
|
The instantiation algorithm is two-phase:
|
|
192
|
-
1. **Constructor injection** -- reads `@inject` metadata on the constructor
|
|
195
|
+
1. **Constructor injection** -- reads `@inject` metadata on the constructor by parameter index (no sort - the metadata is already index-keyed), resolves from container
|
|
193
196
|
2. **Property injection** -- reads property metadata, resolves and assigns each dependency to the instance
|
|
194
197
|
|
|
195
198
|
### Scopes (Singleton / Transient)
|
|
@@ -511,7 +514,6 @@ interface IInjectableMetadata {
|
|
|
511
514
|
// Type guards
|
|
512
515
|
function isClass<T>(target: any): target is TClass<T>;
|
|
513
516
|
function isClassProvider<T>(target: any): target is TClass<IProvider<T>>;
|
|
514
|
-
function isClassConstructor(fn: Function): boolean;
|
|
515
517
|
```
|
|
516
518
|
|
|
517
519
|
## Troubleshooting
|
|
@@ -555,6 +557,12 @@ function isClassConstructor(fn: Function): boolean;
|
|
|
555
557
|
|
|
556
558
|
**Fix:** Provide a non-empty `key` string: `BindingKeys.build({ namespace: 'services', key: 'UserService' })`.
|
|
557
559
|
|
|
560
|
+
### "[ClassName] Constructor parameter N has no @inject"
|
|
561
|
+
|
|
562
|
+
**Cause:** A container-instantiated class has a constructor with a mix of decorated and undecorated parameters. `@inject` stores its metadata at the parameter's index, so an undecorated parameter leaves a hole in that index-keyed array; there is no channel through which the container could supply it anyway. `instantiate()` refuses the shape by name and index rather than passing `undefined`.
|
|
563
|
+
|
|
564
|
+
**Fix:** Decorate every constructor parameter of the class with `@inject`. There is no partial-injection escape hatch - if a value does not come from the container (e.g. a plain `scope: string`), pass it through a factory/provider instead of a bare constructor parameter, or have the subclass forward it via its own `@inject`-decorated parameter.
|
|
565
|
+
|
|
558
566
|
### "@inject decorator can only be used on class properties or constructor parameters"
|
|
559
567
|
|
|
560
568
|
**Cause:** The `@inject` decorator was applied to something other than a class property or constructor parameter.
|
|
@@ -189,6 +189,34 @@ class UserService {
|
|
|
189
189
|
}
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
+
### Logging Errors: `%s`, Never `%j`
|
|
193
|
+
|
|
194
|
+
`message` and `stack` are non-enumerable properties on a native `Error`. `%j` formats via `JSON.stringify`, which only visits enumerable own properties, so `logger.error('Failed: %j', error)` silently drops both `message` and `stack` -- the two fields the log line exists to capture. Always pair an `Error` argument with `%s`; reserve `%j`/`%o` for plain data objects.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// ✅ Good - %s prints message + stack
|
|
198
|
+
logger.error('Failed to create user: %s', error);
|
|
199
|
+
|
|
200
|
+
// ❌ Bad - %j drops message and stack (non-enumerable on Error)
|
|
201
|
+
logger.error('Failed to create user: %j', error);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Object Inspection Depth for `%s`
|
|
205
|
+
|
|
206
|
+
Node hard-codes `depth: 0` for `%s` in `util.format` -- an object passed to `%s` collapses to `[Object]`, hiding the nested `extra` or `cause` a wrapped error carries. IGNIS's formatter (`formatLogMessage`/`deepSplat`) pre-inspects any object bound to a `%s` placeholder before handing the message to Winston, widening that depth so nested fields print instead of collapsing.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
logger.error('Failed: %s', error); // nested `error.cause` is now visible, not `[Object]`
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The inspection depth defaults to `5` and is configurable via `APP_ENV_LOGGER_INSPECT_DEPTH`:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
APP_ENV_LOGGER_INSPECT_DEPTH=8
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The value must be a non-negative integer. An absent, empty, negative, or unparseable value falls back to the default of `5` -- there is no "unlimited" setting.
|
|
219
|
+
|
|
192
220
|
### Log Formats
|
|
193
221
|
|
|
194
222
|
The logger supports two output formats, controlled by the `APP_ENV_LOGGER_FORMAT` environment variable (default: `text`).
|
|
@@ -396,7 +424,7 @@ Debug logs require **both** conditions to be met:
|
|
|
396
424
|
1. `DEBUG=true` environment variable is set (parsed via `toBoolean`)
|
|
397
425
|
2. `NODE_ENV` is either unset **or** is present in the `Environment.COMMON_ENVS` set
|
|
398
426
|
|
|
399
|
-
The `COMMON_ENVS` set includes: `local`, `debug`, `development`, `alpha`, `beta`, `staging`, `production`. You can extend this set with `APP_ENV_EXTRA_LOG_ENVS`:
|
|
427
|
+
The `COMMON_ENVS` set includes: `local`, `debug`, `development`, `dev`, `sit`, `uat`, `alpha`, `beta`, `staging`, `production`. You can extend this set with `APP_ENV_EXTRA_LOG_ENVS`:
|
|
400
428
|
|
|
401
429
|
```bash
|
|
402
430
|
DEBUG=true
|
|
@@ -473,6 +501,7 @@ The buffer wraps around at 65,536 entries using bitwise AND masking (`writeIndex
|
|
|
473
501
|
| `APP_ENV_EXTRA_LOG_ENVS` | _(empty)_ | Comma-separated additional environments to allow debug |
|
|
474
502
|
| `APP_ENV_LOGGER_FORMAT` | `text` | Output format (`json` or `text`) |
|
|
475
503
|
| `APP_ENV_LOGGER_FOLDER_PATH` | `./` | Log files directory |
|
|
504
|
+
| `APP_ENV_LOGGER_INSPECT_DEPTH` | `5` | Object inspection depth for `%s` placeholders. Non-negative integer only; invalid or absent falls back to `5` |
|
|
476
505
|
|
|
477
506
|
#### File Rotation
|
|
478
507
|
|
|
@@ -502,6 +531,7 @@ APP_ENV_APPLICATION_NAME=my-service
|
|
|
502
531
|
DEBUG=true
|
|
503
532
|
APP_ENV_LOGGER_FORMAT=json
|
|
504
533
|
APP_ENV_LOGGER_FOLDER_PATH=./app_data/logs
|
|
534
|
+
APP_ENV_LOGGER_INSPECT_DEPTH=5
|
|
505
535
|
|
|
506
536
|
# File rotation
|
|
507
537
|
APP_ENV_LOGGER_FILE_FREQUENCY=24h
|
|
@@ -547,7 +577,7 @@ APP_ENV_LOGGER_DGRAM_LEVELS=error,warn,info
|
|
|
547
577
|
|
|
548
578
|
**Fix:**
|
|
549
579
|
1. Verify `DEBUG=true` is set in your environment.
|
|
550
|
-
2. Verify `NODE_ENV` is set to one of: `local`, `debug`, `development`, `alpha`, `beta`, `staging`, `production` -- or is unset entirely.
|
|
580
|
+
2. Verify `NODE_ENV` is set to one of: `local`, `debug`, `development`, `dev`, `sit`, `uat`, `alpha`, `beta`, `staging`, `production` -- or is unset entirely.
|
|
551
581
|
3. If you use a custom environment name (e.g. `qa`), add it to `APP_ENV_EXTRA_LOG_ENVS=qa`.
|
|
552
582
|
|
|
553
583
|
```bash
|
|
@@ -332,6 +332,12 @@ class PaymentGateway extends AxiosNetworkRequest {
|
|
|
332
332
|
}
|
|
333
333
|
```
|
|
334
334
|
|
|
335
|
+
#### Request Logging & Redaction
|
|
336
|
+
|
|
337
|
+
Both `NodeFetcher` and `AxiosFetcher` log every outbound request at `info` level (`URL: %s | Props: %s | ...`), including the request config -- headers, body, and any other options passed to `send()`. Before that log line is written, the request config is run through `redactSecrets()`, so values under secret-looking keys never reach the log.
|
|
338
|
+
|
|
339
|
+
Redaction matches by key name, case-insensitively, at any depth -- both options-object spellings (`apiKey`, `token`, `password`, `authorization`, ...) and HTTP header spellings (`x-api-key`, `x-csrf-token`, `cookie`, `set-cookie`, `proxy-authorization`, `www-authenticate`, ...). A header like `'X-API-Key': process.env.PAYMENT_API_KEY` or `'Authorization': 'Bearer my-token'` (as in the examples above) is logged as `'[REDACTED]'`, not the real value. This redaction happens automatically -- there is nothing to configure.
|
|
340
|
+
|
|
335
341
|
#### Convenience Methods
|
|
336
342
|
|
|
337
343
|
```typescript
|
|
@@ -75,7 +75,7 @@ All queue helpers extend `BaseHelper` (Kafka helpers via `BaseKafkaHelper`), pro
|
|
|
75
75
|
The `BullMQHelper` wraps the BullMQ library for Redis-backed job queuing. It operates in one of two roles: `'queue'` (producer) or `'worker'` (consumer). The role is set at construction time and determines which BullMQ primitives are initialized.
|
|
76
76
|
|
|
77
77
|
```typescript
|
|
78
|
-
import {
|
|
78
|
+
import { RedisSingleHelper } from '@venizia/ignis-helpers';
|
|
79
79
|
import { BullMQHelper } from '@venizia/ignis-helpers/bullmq';
|
|
80
80
|
|
|
81
81
|
const worker = new BullMQHelper({
|
|
@@ -107,7 +107,7 @@ const worker = new BullMQHelper({
|
|
|
107
107
|
| `queueName` | `string` | -- | Name of the BullMQ queue. Must be non-empty. |
|
|
108
108
|
| `identifier` | `string` | -- | Unique identifier used for scoped logging. |
|
|
109
109
|
| `role` | `TBullQueueRole` | -- | `'queue'` (producer) or `'worker'` (consumer). |
|
|
110
|
-
| `redisConnection` | `
|
|
110
|
+
| `redisConnection` | `IRedisHelper` | -- | Redis helper instance. The helper calls `duplicateClient()` internally. |
|
|
111
111
|
| `numberOfWorker` | `number` | `1` | Worker concurrency (number of jobs processed in parallel). |
|
|
112
112
|
| `lockDuration` | `number` | `5400000` | Job lock duration in milliseconds (default: 90 minutes). |
|
|
113
113
|
| `onWorkerData` | `(job: Job<TQueueElement, TQueueResult>) => Promise<any>` | `undefined` | Job processing callback. If omitted, the worker logs job details. |
|
|
@@ -115,7 +115,7 @@ const worker = new BullMQHelper({
|
|
|
115
115
|
| `onWorkerDataFail` | `(job: Job<TQueueElement, TQueueResult> \| undefined, error: Error) => Promise<void>` | `undefined` | Callback fired when a job fails. |
|
|
116
116
|
|
|
117
117
|
> [!IMPORTANT]
|
|
118
|
-
> Pass
|
|
118
|
+
> Pass an `IRedisHelper` instance to `redisConnection`, **not** the raw ioredis client. The helper internally calls `redisConnection.duplicateClient()` to create dedicated connections for the queue and worker.
|
|
119
119
|
|
|
120
120
|
### MQTTClientHelper
|
|
121
121
|
|
|
@@ -162,6 +162,9 @@ const mqttClient = new MQTTClientHelper({
|
|
|
162
162
|
| `onError` | `(error: Error) => void` | `undefined` | Callback fired on client errors. |
|
|
163
163
|
| `onClose` | `(error?: Error) => void` | `undefined` | Callback fired when the connection is closed. |
|
|
164
164
|
|
|
165
|
+
> [!NOTE]
|
|
166
|
+
> At connect time, `MQTTClientHelper` logs the broker `url` through `redactUrlCredentials()` and the `options` object through `redactSecrets()`. If `url` embeds a password (e.g. `mqtts://user:hunter2@broker:8883`), the password never reaches the log -- only `mqtts://user:[REDACTED]@broker:8883` does.
|
|
167
|
+
|
|
165
168
|
### QueueHelper
|
|
166
169
|
|
|
167
170
|
The `QueueHelper` is a generator-based, in-memory queue with a built-in state machine. It processes enqueued items one at a time, making it suitable for sequential task processing within a single process.
|
|
@@ -442,34 +445,28 @@ If `onWorkerData` is not provided, the worker logs the job's `id`, `name`, and `
|
|
|
442
445
|
|
|
443
446
|
### BullMQ -- Redis Cluster
|
|
444
447
|
|
|
445
|
-
When using Redis Cluster with BullMQ,
|
|
448
|
+
When using Redis Cluster with BullMQ, use `RedisClusterHelper`. Set `maxRetriesPerRequest: null` inside `clusterOptions.redisOptions` -- this is **required** by BullMQ.
|
|
446
449
|
|
|
447
450
|
```typescript
|
|
448
|
-
import {
|
|
449
|
-
import { DefaultRedisHelper } from '@venizia/ignis-helpers';
|
|
451
|
+
import { RedisClusterHelper } from '@venizia/ignis-helpers';
|
|
450
452
|
import { BullMQHelper } from '@venizia/ignis-helpers/bullmq';
|
|
451
453
|
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
+
const redisHelper = new RedisClusterHelper({
|
|
455
|
+
name: 'cluster-redis',
|
|
456
|
+
nodes: [
|
|
454
457
|
{ host: 'node1.redis.example.com', port: 6379 },
|
|
455
458
|
{ host: 'node2.redis.example.com', port: 6379 },
|
|
456
459
|
{ host: 'node3.redis.example.com', port: 6379 },
|
|
457
460
|
],
|
|
458
|
-
{
|
|
459
|
-
maxRetriesPerRequest: null, // Required by BullMQ
|
|
461
|
+
clusterOptions: {
|
|
460
462
|
enableReadyCheck: true,
|
|
461
463
|
scaleReads: 'slave',
|
|
462
464
|
redisOptions: {
|
|
463
465
|
password: 'your-password',
|
|
464
466
|
tls: {},
|
|
467
|
+
maxRetriesPerRequest: null, // Required by BullMQ
|
|
465
468
|
},
|
|
466
|
-
}
|
|
467
|
-
);
|
|
468
|
-
|
|
469
|
-
const redisHelper = new DefaultRedisHelper({
|
|
470
|
-
scope: 'BullMQ',
|
|
471
|
-
identifier: 'cluster-redis',
|
|
472
|
-
client: cluster,
|
|
469
|
+
},
|
|
473
470
|
});
|
|
474
471
|
|
|
475
472
|
const worker = BullMQHelper.newInstance({
|