@venizia/ignis-docs 0.0.8 → 0.2.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/content/best-practices/api-usage-examples.md +15 -12
- package/content/best-practices/architectural-patterns.md +70 -78
- package/content/best-practices/architecture-decisions.md +91 -60
- package/content/best-practices/code-style-standards/advanced-patterns.md +56 -44
- package/content/best-practices/code-style-standards/constants-configuration.md +11 -11
- package/content/best-practices/code-style-standards/control-flow.md +5 -2
- package/content/best-practices/code-style-standards/documentation.md +13 -13
- package/content/best-practices/code-style-standards/function-patterns.md +9 -10
- package/content/best-practices/code-style-standards/index.md +1 -1
- package/content/best-practices/code-style-standards/naming-conventions.md +10 -8
- package/content/best-practices/code-style-standards/route-definitions.md +30 -12
- package/content/best-practices/code-style-standards/tooling.md +8 -5
- package/content/best-practices/code-style-standards/type-safety.md +13 -12
- package/content/best-practices/common-pitfalls.md +56 -37
- package/content/best-practices/contribution-workflow.md +13 -14
- package/content/best-practices/data-modeling.md +46 -22
- package/content/best-practices/deployment-strategies.md +28 -27
- package/content/best-practices/error-handling.md +48 -24
- package/content/best-practices/index.md +5 -5
- package/content/best-practices/performance-optimization.md +40 -31
- package/content/best-practices/security-guidelines.md +52 -23
- package/content/best-practices/testing-strategies.md +65 -51
- package/content/best-practices/troubleshooting-tips.md +24 -24
- package/content/extensions/components/{swagger.md → api-reference.md} +40 -31
- package/content/extensions/components/authentication/api.md +19 -19
- package/content/extensions/components/authentication/errors.md +7 -7
- package/content/extensions/components/authentication/index.md +10 -8
- package/content/extensions/components/authentication/usage.md +101 -6
- package/content/extensions/components/authorization/api.md +45 -25
- package/content/extensions/components/authorization/errors.md +6 -6
- package/content/extensions/components/authorization/index.md +11 -10
- package/content/extensions/components/authorization/usage.md +21 -21
- package/content/extensions/components/health-check.md +1 -1
- package/content/extensions/components/index.md +5 -5
- package/content/extensions/components/mail/errors.md +15 -15
- package/content/extensions/components/mail/index.md +1 -2
- package/content/extensions/components/mail/usage.md +1 -1
- package/content/extensions/components/request-tracker.md +1 -1
- package/content/extensions/components/socket-io/api.md +9 -9
- package/content/extensions/components/socket-io/errors.md +5 -5
- package/content/extensions/components/socket-io/index.md +8 -8
- package/content/extensions/components/socket-io/usage.md +1 -1
- package/content/extensions/components/static-asset/api.md +17 -4
- package/content/extensions/components/static-asset/errors.md +4 -4
- package/content/extensions/components/static-asset/index.md +26 -28
- package/content/extensions/components/static-asset/usage.md +13 -12
- package/content/extensions/components/template/index.md +2 -2
- package/content/extensions/components/template/setup-page.md +1 -1
- package/content/extensions/components/websocket/api.md +3 -3
- package/content/extensions/components/websocket/errors.md +5 -5
- package/content/extensions/components/websocket/index.md +5 -5
- package/content/extensions/components/websocket/usage.md +3 -3
- package/content/extensions/helpers/cron/index.md +2 -2
- package/content/extensions/helpers/crypto/index.md +1 -1
- package/content/extensions/helpers/env/index.md +27 -12
- package/content/extensions/helpers/error/index.md +81 -25
- package/content/extensions/helpers/index.md +2 -3
- package/content/extensions/helpers/inversion/index.md +15 -7
- package/content/extensions/helpers/kafka/compile-binary.md +92 -0
- package/content/extensions/helpers/kafka/examples.md +1 -1
- package/content/extensions/helpers/kafka/index.md +3 -0
- package/content/extensions/helpers/logger/index.md +32 -2
- package/content/extensions/helpers/network/index.md +6 -0
- package/content/extensions/helpers/queue/index.md +14 -17
- package/content/extensions/helpers/redis/index.md +548 -323
- package/content/extensions/helpers/socket-io/index.md +14 -10
- package/content/extensions/helpers/storage/api.md +44 -8
- package/content/extensions/helpers/storage/index.md +43 -7
- package/content/extensions/helpers/template/index.md +6 -3
- package/content/extensions/helpers/types/index.md +11 -8
- package/content/extensions/helpers/websocket/api.md +9 -9
- package/content/extensions/helpers/websocket/index.md +7 -7
- package/content/extensions/helpers/worker-thread/index.md +2 -2
- package/content/extensions/index.md +3 -4
- package/content/extensions/src-details/mcp-server.md +18 -24
- package/content/guides/core-concepts/application/bootstrapping.md +11 -14
- package/content/guides/core-concepts/application/index.md +3 -3
- package/content/guides/core-concepts/components.md +19 -10
- package/content/guides/core-concepts/dependency-injection.md +6 -3
- package/content/guides/core-concepts/grpc-controllers.md +6 -5
- package/content/guides/core-concepts/persistent/datasources.md +42 -43
- package/content/guides/core-concepts/persistent/index.md +16 -7
- package/content/guides/core-concepts/persistent/models.md +24 -20
- package/content/guides/core-concepts/persistent/postgres-drivers.md +201 -0
- package/content/guides/core-concepts/persistent/repositories.md +40 -23
- package/content/guides/core-concepts/persistent/search-meilisearch.md +185 -0
- package/content/guides/core-concepts/persistent/search-typesense.md +431 -0
- package/content/guides/core-concepts/persistent/transactions.md +61 -25
- package/content/guides/core-concepts/rest-controllers.md +12 -9
- package/content/guides/core-concepts/services.md +330 -60
- package/content/guides/get-started/5-minute-quickstart.md +15 -15
- package/content/guides/get-started/philosophy.md +36 -36
- package/content/guides/get-started/setup.md +3 -3
- package/content/guides/index.md +3 -3
- package/content/guides/migrations/redis-helpers-migration.md +177 -0
- package/content/guides/migrations/scoped-rbac-migration.md +17 -17
- package/content/guides/migrations/unified-connectors-migration.md +113 -0
- package/content/guides/reference/glossary.md +19 -12
- package/content/guides/reference/mcp-docs-server.md +22 -18
- package/content/guides/tutorials/building-a-crud-api.md +37 -44
- package/content/guides/tutorials/complete-installation.md +17 -17
- package/content/guides/tutorials/ecommerce-api.md +163 -124
- package/content/guides/tutorials/realtime-chat.md +181 -135
- package/content/guides/tutorials/testing.md +65 -523
- package/content/index.md +2 -180
- 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/content/references/base/application.md +4 -5
- package/content/references/base/bootstrapping.md +18 -5
- package/content/references/base/components.md +149 -120
- package/content/references/base/connectors.md +178 -0
- package/content/references/base/controllers.md +41 -30
- package/content/references/base/datasources.md +163 -92
- package/content/references/base/dependency-injection.md +34 -22
- package/content/references/base/filter-system/application-usage.md +17 -14
- package/content/references/base/filter-system/array-operators.md +7 -2
- package/content/references/base/filter-system/comparison-operators.md +3 -0
- package/content/references/base/filter-system/default-filter.md +89 -71
- package/content/references/base/filter-system/fields-order-pagination.md +22 -22
- package/content/references/base/filter-system/index.md +6 -3
- package/content/references/base/filter-system/json-filtering.md +20 -1
- package/content/references/base/filter-system/list-operators.md +1 -1
- package/content/references/base/filter-system/logical-operators.md +33 -1
- package/content/references/base/filter-system/null-operators.md +30 -1
- package/content/references/base/filter-system/quick-reference.md +23 -4
- package/content/references/base/filter-system/tips.md +5 -5
- package/content/references/base/filter-system/use-cases.md +12 -12
- package/content/references/base/grpc-controllers.md +13 -13
- package/content/references/base/index.md +24 -12
- package/content/references/base/middlewares.md +265 -327
- package/content/references/base/models.md +63 -49
- package/content/references/base/providers.md +136 -130
- package/content/references/base/repositories/advanced.md +59 -58
- package/content/references/base/repositories/index.md +115 -91
- package/content/references/base/repositories/mixins.md +55 -291
- package/content/references/base/repositories/relations.md +54 -64
- package/content/references/base/repositories/soft-deletable.md +31 -30
- package/content/references/base/services.md +296 -93
- package/content/references/configuration/environment-variables.md +49 -31
- package/content/references/configuration/index.md +6 -6
- package/content/references/index.md +17 -12
- package/content/references/quick-reference.md +65 -106
- package/content/references/utilities/crypto.md +65 -23
- package/content/references/utilities/index.md +3 -3
- package/content/references/utilities/jsx.md +6 -4
- package/content/references/utilities/module.md +68 -20
- package/content/references/utilities/parse.md +4 -14
- package/content/references/utilities/promise.md +9 -7
- package/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/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 +9 -9
- package/content/extensions/helpers/testing/index.md +0 -510
- package/content/references/base/middleware.md +0 -347
|
@@ -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.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Compiling to a Single Binary
|
|
2
|
+
|
|
3
|
+
`bun build --compile` produces a standalone executable that crashes on startup when the application
|
|
4
|
+
imports any Kafka helper, unless the build registers `platformaticWasmPlugin`.
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
ENOENT: no such file or directory, open '/$bunfs/dist/native.wasm'
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The failure happens while the module graph is still loading -- before the IGNIS application boots, so
|
|
11
|
+
no log line, no lifecycle hook, and no error handler of yours ever runs.
|
|
12
|
+
|
|
13
|
+
## Why it happens
|
|
14
|
+
|
|
15
|
+
`@platformatic/kafka` computes Kafka's CRC32C checksums and lz4/snappy compression in WebAssembly,
|
|
16
|
+
through `@platformatic/wasm-utils`. The default entrypoint of that package reads the wasm payload
|
|
17
|
+
from disk at module load time:
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
// @platformatic/wasm-utils/dist/index.js
|
|
21
|
+
const wasm = readFileSync(new URL('../dist/native.wasm', import.meta.url));
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`bun build --compile` embeds JavaScript modules only -- assets such as `native.wasm` are not carried
|
|
25
|
+
into the executable. Inside the binary, `import.meta.url` resolves against the virtual `/$bunfs`
|
|
26
|
+
filesystem, the file is not there, and the read throws.
|
|
27
|
+
|
|
28
|
+
Running from source (`bun run`, `bun .`) is unaffected: `node_modules` is on disk, so the read
|
|
29
|
+
succeeds. The bug only exists in compiled binaries.
|
|
30
|
+
|
|
31
|
+
## The fix
|
|
32
|
+
|
|
33
|
+
`@platformatic/wasm-utils` ships a second entrypoint, `@platformatic/wasm-utils/bundled`, exposing
|
|
34
|
+
the same API with the wasm payload inlined as base64 -- no filesystem read. `platformaticWasmPlugin`
|
|
35
|
+
swaps one entrypoint for the other at bundle time.
|
|
36
|
+
|
|
37
|
+
Compile through a Bun build script instead of the `bun build --compile` CLI, which cannot register
|
|
38
|
+
plugins:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// scripts/compile.ts
|
|
42
|
+
import { platformaticWasmPlugin } from '@venizia/ignis-helpers/kafka';
|
|
43
|
+
|
|
44
|
+
const built = await Bun.build({
|
|
45
|
+
entrypoints: ['./dist/index.js'],
|
|
46
|
+
target: 'bun',
|
|
47
|
+
minify: { whitespace: true, syntax: true },
|
|
48
|
+
sourcemap: 'linked',
|
|
49
|
+
compile: {
|
|
50
|
+
target: process.env.BUN_TARGET ?? 'bun-linux-x64',
|
|
51
|
+
outfile: './dist/bin',
|
|
52
|
+
},
|
|
53
|
+
plugins: [platformaticWasmPlugin()],
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (!built.success) {
|
|
57
|
+
console.error(built.logs);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"scripts": {
|
|
65
|
+
"compile": "bun run ./scripts/compile.ts"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The plugin resolves `@platformatic/wasm-utils/bundled` from the importing module's own directory, so
|
|
71
|
+
it works with hoisted and isolated `node_modules` layouts alike, and pins no package version.
|
|
72
|
+
|
|
73
|
+
## Verifying
|
|
74
|
+
|
|
75
|
+
A correctly built binary contains no reference to the wasm file on disk:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
grep -c 'native.wasm' ./dist/bin # 0 -- the payload is inlined
|
|
79
|
+
./dist/bin # boots instead of throwing ENOENT
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The binary grows by roughly 76 KB, the base64 form of the 57 KB wasm module.
|
|
83
|
+
|
|
84
|
+
## Notes
|
|
85
|
+
|
|
86
|
+
- Upgrading `@platformatic/kafka` does not remove the need for the plugin: every release to date,
|
|
87
|
+
including 2.6.1, imports the default `@platformatic/wasm-utils` entrypoint.
|
|
88
|
+
- Applications that never import a Kafka helper need no plugin -- nothing pulls in
|
|
89
|
+
`@platformatic/wasm-utils`, and the plugin's resolver never fires.
|
|
90
|
+
- Patching `node_modules` during the build achieves the same result, but mutates a dependency in
|
|
91
|
+
place, pins the store path to one version, and leaves the tree dirty when a build fails. The
|
|
92
|
+
plugin needs neither.
|
|
@@ -88,6 +88,9 @@ import type {
|
|
|
88
88
|
> [!NOTE]
|
|
89
89
|
> Kafka helpers are **not** re-exported from the main `@venizia/ignis-helpers` entry point. You must use the `@venizia/ignis-helpers/kafka` subpath import. This keeps the optional `@platformatic/kafka` peer dependency isolated for tree-shaking.
|
|
90
90
|
|
|
91
|
+
> [!WARNING]
|
|
92
|
+
> Compiling an application that uses these helpers with `bun build --compile` produces a binary that dies on startup with `ENOENT: /$bunfs/dist/native.wasm`. The build must register `platformaticWasmPlugin` from `@venizia/ignis-helpers/kafka` -- see [Compiling to a Single Binary](./compile-binary.md).
|
|
93
|
+
|
|
91
94
|
### Installation
|
|
92
95
|
|
|
93
96
|
```bash
|
|
@@ -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({
|