@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.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +22 -11
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +6 -2
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +182 -93
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +107 -322
- package/content/extensions/components/authentication/api.md +454 -603
- package/content/extensions/components/authentication/errors.md +121 -498
- package/content/extensions/components/authentication/index.md +88 -801
- package/content/extensions/components/authentication/usage.md +207 -956
- package/content/extensions/components/authorization/api.md +736 -656
- package/content/extensions/components/authorization/errors.md +168 -206
- package/content/extensions/components/authorization/index.md +82 -797
- package/content/extensions/components/authorization/usage.md +194 -527
- package/content/extensions/components/health-check.md +71 -243
- package/content/extensions/components/mail/api.md +504 -287
- package/content/extensions/components/mail/errors.md +73 -61
- package/content/extensions/components/mail/index.md +96 -467
- package/content/extensions/components/mail/usage.md +130 -172
- package/content/extensions/components/request-tracker.md +66 -173
- package/content/extensions/components/socket-io/api.md +195 -13
- package/content/extensions/components/socket-io/errors.md +3 -3
- package/content/extensions/components/socket-io/index.md +50 -337
- package/content/extensions/components/socket-io/usage.md +143 -26
- package/content/extensions/components/static-asset/api.md +410 -142
- package/content/extensions/components/static-asset/errors.md +110 -53
- package/content/extensions/components/static-asset/index.md +79 -608
- package/content/extensions/components/static-asset/usage.md +180 -300
- package/content/extensions/components/websocket/api.md +275 -399
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +74 -407
- package/content/extensions/components/websocket/usage.md +110 -341
- package/content/extensions/helpers/cron/index.md +51 -160
- package/content/extensions/helpers/crypto/index.md +62 -483
- package/content/extensions/helpers/crypto/reference.md +456 -0
- package/content/extensions/helpers/env/index.md +60 -178
- package/content/extensions/helpers/error/index.md +221 -207
- package/content/extensions/helpers/inversion/index.md +65 -556
- package/content/extensions/helpers/inversion/reference.md +522 -0
- package/content/extensions/helpers/kafka/admin.md +20 -1
- package/content/extensions/helpers/kafka/compile-binary.md +41 -35
- package/content/extensions/helpers/kafka/consumer.md +54 -20
- package/content/extensions/helpers/kafka/examples.md +21 -16
- package/content/extensions/helpers/kafka/index.md +80 -610
- package/content/extensions/helpers/kafka/producer.md +134 -5
- package/content/extensions/helpers/kafka/schema-registry.md +45 -70
- package/content/extensions/helpers/logger/hf-logger.md +193 -0
- package/content/extensions/helpers/logger/index.md +64 -563
- package/content/extensions/helpers/logger/pino.md +85 -0
- package/content/extensions/helpers/logger/reference.md +746 -0
- package/content/extensions/helpers/network/api.md +241 -195
- package/content/extensions/helpers/network/index.md +72 -530
- package/content/extensions/helpers/queue/index.md +72 -900
- package/content/extensions/helpers/queue/reference.md +467 -0
- package/content/extensions/helpers/redis/index.md +73 -645
- package/content/extensions/helpers/redis/reference.md +727 -0
- package/content/extensions/helpers/secrets/index.md +66 -0
- package/content/extensions/helpers/socket-io/api.md +305 -203
- package/content/extensions/helpers/socket-io/index.md +66 -432
- package/content/extensions/helpers/storage/api.md +564 -462
- package/content/extensions/helpers/storage/index.md +77 -573
- package/content/extensions/helpers/types/index.md +66 -499
- package/content/extensions/helpers/types/reference.md +650 -0
- package/content/extensions/helpers/uid/index.md +58 -227
- package/content/extensions/helpers/websocket/api.md +329 -216
- package/content/extensions/helpers/websocket/index.md +65 -503
- package/content/extensions/helpers/worker-thread/index.md +58 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
- package/content/guides/core-concepts/persistent/transactions.md +1 -1
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/migrations/redis-helpers-migration.md +1 -1
- package/content/guides/migrations/unified-connectors-migration.md +2 -2
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/references/base/application.md +1 -1
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/datasources-reference.md +599 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +17 -39
- package/content/references/base/filter-system/application-usage.md +69 -121
- package/content/references/base/filter-system/array-operators.md +12 -0
- package/content/references/base/filter-system/comparison-operators.md +12 -0
- package/content/references/base/filter-system/default-filter.md +136 -348
- package/content/references/base/filter-system/fields-order-pagination.md +38 -16
- package/content/references/base/filter-system/index.md +106 -257
- package/content/references/base/filter-system/json-filtering.md +12 -2
- package/content/references/base/filter-system/list-operators.md +16 -2
- package/content/references/base/filter-system/logical-operators.md +13 -0
- package/content/references/base/filter-system/null-operators.md +13 -0
- package/content/references/base/filter-system/pattern-matching.md +12 -0
- package/content/references/base/filter-system/quick-reference.md +11 -2
- package/content/references/base/filter-system/range-operators.md +12 -0
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +156 -233
- package/content/references/base/middlewares.md +35 -21
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +80 -1452
- package/content/references/base/repositories/advanced.md +156 -192
- package/content/references/base/repositories/index.md +77 -650
- package/content/references/base/repositories/mixins.md +22 -18
- package/content/references/base/repositories/relations.md +123 -171
- package/content/references/base/repositories/soft-deletable.md +58 -56
- package/content/references/base/secrets.md +263 -0
- package/content/references/base/services.md +2 -2
- package/content/references/configuration/environment-variables.md +48 -4
- package/content/references/configuration/index.md +49 -31
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/index.md +1 -1
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +29 -62
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +57 -218
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/package.json +8 -8
|
@@ -20,17 +20,17 @@ APP_ENV_POSTGRES_PASSWORD=database_password_here
|
|
|
20
20
|
|
|
21
21
|
**Generate strong secrets:**
|
|
22
22
|
```bash
|
|
23
|
-
|
|
23
|
+
bun -e "console.log(Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString('base64'))"
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
### Redaction at Log Time
|
|
27
27
|
|
|
28
|
-
Never hard-coding a secret is not enough on its own
|
|
28
|
+
Never hard-coding a secret is not enough on its own - an options object holding one still ends up in a log line the moment it is passed to `logger.info('...: %s', opts)`. `@venizia/ignis-helpers` provides two primitives for exactly this:
|
|
29
29
|
|
|
30
30
|
| Function | Use For | Behavior |
|
|
31
31
|
|----------|---------|----------|
|
|
32
|
-
| `redactSecrets(value)` | Any object/array being logged | Recursively replaces every value whose key matches a secret-looking name
|
|
33
|
-
| `redactUrlCredentials(url)` | A connection/broker URL string | Strips the password out of a URL's authority section (`mqtts://user:hunter2@broker:8883` becomes `mqtts://user:[REDACTED]@broker:8883`); a value that doesn't parse as a URL is returned unchanged |
|
|
32
|
+
| `redactSecrets(value)` | Any object/array being logged | Recursively replaces every value whose key matches a secret-looking name with `'[REDACTED]'` |
|
|
33
|
+
| `redactUrlCredentials(url)` | A connection/broker URL string | Strips the password out of a URL's authority section (`mqtts://user:hunter2@broker:8883` becomes `mqtts://user:[REDACTED]@broker:8883`); a value that doesn't parse as a URL, or one with no password, is returned unchanged |
|
|
34
34
|
|
|
35
35
|
```typescript
|
|
36
36
|
import { redactSecrets, redactUrlCredentials } from '@venizia/ignis-helpers';
|
|
@@ -39,7 +39,19 @@ this.logger.info('[connect] Options: %s', redactSecrets(connectionOptions));
|
|
|
39
39
|
this.logger.info('[connect] Broker: %s', redactUrlCredentials(brokerUrl));
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
`redactSecrets` matches on the **key name**, case-insensitively, at any depth:
|
|
43
|
+
|
|
44
|
+
- Options-object spellings: `password`, `passphrase`, `secret`, `token`, `apiKey`, `privateKey`, `credentials`, `authorization`, `connectionString`, ...
|
|
45
|
+
- Any `*_token` / `*Token` key: `access_token`, `refresh_token`, `vaultToken`, ...
|
|
46
|
+
- Vault wire keys: `client_token`, `secret_id`, `role_id`
|
|
47
|
+
- HTTP header spellings: `x-api-key`, `x-vault-token`, `cookie`, `set-cookie`, `proxy-authorization`, `www-authenticate`
|
|
48
|
+
|
|
49
|
+
It also handles the shapes naive redaction breaks on: an `Error` is reprojected so its non-enumerable `message`/`stack` survive, cycles become `'[Circular]'`, and buffers/typed arrays are summarized as `[Binary N bytes]` instead of serialized.
|
|
50
|
+
|
|
51
|
+
These are the primitives the framework itself uses - outbound HTTP request configs (`NodeFetcher`/`AxiosFetcher`, see [Network Helper](/extensions/helpers/network/)) and MQTT broker URLs (`MQTTClientHelper`, see [Queue Helper](/extensions/helpers/queue/)) are both redacted this way before they reach a log line.
|
|
52
|
+
|
|
53
|
+
> [!WARNING]
|
|
54
|
+
> `APP_ENV_LOGGER_DO_REDACT=false` turns both functions into the identity function. It is a local-debugging kill-switch and must never be set in production. The check is fail-closed - only the literal string `false` disables redaction - and is read per call, so it can be flipped at runtime.
|
|
43
55
|
|
|
44
56
|
## 2. Input Validation
|
|
45
57
|
|
|
@@ -110,7 +122,7 @@ Configure model properties that should **never be returned** through repository
|
|
|
110
122
|
hiddenProperties: ['password', 'apiSecret', 'internalToken'],
|
|
111
123
|
},
|
|
112
124
|
})
|
|
113
|
-
export class User extends
|
|
125
|
+
export class User extends BaseRelationalEntity<typeof User.schema> {
|
|
114
126
|
static override schema = pgTable('User', {
|
|
115
127
|
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
116
128
|
email: text('email').notNull(),
|
|
@@ -131,7 +143,7 @@ export class User extends BasePostgresEntity<typeof User.schema> {
|
|
|
131
143
|
|
|
132
144
|
```typescript
|
|
133
145
|
// For authentication - access password via connector
|
|
134
|
-
const connector = userRepository.
|
|
146
|
+
const connector = userRepository.connector;
|
|
135
147
|
const [user] = await connector
|
|
136
148
|
.select({ id: User.schema.id, password: User.schema.password })
|
|
137
149
|
.from(User.schema)
|
|
@@ -339,7 +351,7 @@ const redisHelper = new RedisSingleHelper({
|
|
|
339
351
|
name: 'rate-limiter',
|
|
340
352
|
host: process.env.APP_ENV_REDIS_HOST ?? 'localhost',
|
|
341
353
|
port: process.env.APP_ENV_REDIS_PORT ?? '6379',
|
|
342
|
-
password: process.env.APP_ENV_REDIS_PASSWORD,
|
|
354
|
+
password: process.env.APP_ENV_REDIS_PASSWORD ?? '',
|
|
343
355
|
});
|
|
344
356
|
|
|
345
357
|
const distributedRateLimiter = async (opts: { key: string; max: number; windowSeconds: number }) => {
|
|
@@ -462,6 +474,18 @@ export class AuthService extends BaseService {
|
|
|
462
474
|
}
|
|
463
475
|
```
|
|
464
476
|
|
|
477
|
+
### Error Responses Are Hardened in Production
|
|
478
|
+
|
|
479
|
+
The global error handler decides what a client sees by environment, and it is **fail-closed**: an unset or unrecognized `NODE_ENV` is treated as production. Only `NODE_ENV` values in the development set relax it.
|
|
480
|
+
|
|
481
|
+
| In production | Behavior |
|
|
482
|
+
|---|---|
|
|
483
|
+
| Unexpected errors | Replaced with a generic `"Internal Server Error"` - raw text may carry SQL, schema names or connection details |
|
|
484
|
+
| Database constraint errors (400) | Base message only; the driver's `Detail:` (which echoes row values like emails), `Table:` and `Constraint:` are stripped |
|
|
485
|
+
| `details.stack` / `details.cause` | Omitted |
|
|
486
|
+
|
|
487
|
+
Deliberate `getError` messages are always returned verbatim, in every environment - so never put internal detail in one. Use `requestId` plus the server log to diagnose what the response no longer shows. See [Error Handling](./error-handling#_5-error-response-format).
|
|
488
|
+
|
|
465
489
|
**Events to Log:**
|
|
466
490
|
- Failed login attempts
|
|
467
491
|
- Successful logins
|
|
@@ -471,7 +495,7 @@ export class AuthService extends BaseService {
|
|
|
471
495
|
- Admin actions
|
|
472
496
|
|
|
473
497
|
> [!NOTE]
|
|
474
|
-
> When logging a request or connection config that might carry credentials, wrap it in `redactSecrets()` (or `redactUrlCredentials()` for a URL) rather than logging it raw
|
|
498
|
+
> When logging a request or connection config that might carry credentials, wrap it in `redactSecrets()` (or `redactUrlCredentials()` for a URL) rather than logging it raw - see [Redaction at Log Time](#redaction-at-log-time) above. This is what the framework's own outbound HTTP and MQTT logging already does automatically.
|
|
475
499
|
|
|
476
500
|
## Security Checklist
|
|
477
501
|
|
|
@@ -489,6 +513,8 @@ Before deploying to production, verify:
|
|
|
489
513
|
| **Dependencies** | No known vulnerabilities (`bun audit`) |
|
|
490
514
|
| **HTTPS** | TLS configured for production |
|
|
491
515
|
| **Hidden Data** | Sensitive fields use `hiddenProperties` |
|
|
516
|
+
| **Redaction** | `APP_ENV_LOGGER_DO_REDACT` is unset (never `false`) |
|
|
517
|
+
| **Error Leakage** | `NODE_ENV` set to a production value so responses are sanitized |
|
|
492
518
|
|
|
493
519
|
## See Also
|
|
494
520
|
|
|
@@ -102,8 +102,8 @@ bun test
|
|
|
102
102
|
# Run specific test file
|
|
103
103
|
bun test src/__tests__/user.service.test.ts
|
|
104
104
|
|
|
105
|
-
# Run tests
|
|
106
|
-
bun test
|
|
105
|
+
# Run tests whose name matches a pattern
|
|
106
|
+
bun test -t "UserService"
|
|
107
107
|
|
|
108
108
|
# Watch mode
|
|
109
109
|
bun test --watch
|
|
@@ -410,6 +410,11 @@ describe('UserController E2E', () => {
|
|
|
410
410
|
});
|
|
411
411
|
|
|
412
412
|
expect(response.status).toBe(409);
|
|
413
|
+
|
|
414
|
+
// Error responses carry one message shape: { text, code, args }.
|
|
415
|
+
// There is no flat top-level messageCode - branch on normalized.code.
|
|
416
|
+
const body = await response.json();
|
|
417
|
+
expect(body.normalized.code).toBeDefined();
|
|
413
418
|
});
|
|
414
419
|
});
|
|
415
420
|
|
|
@@ -50,11 +50,9 @@ This prints all registered routes on startup.
|
|
|
50
50
|
|
|
51
51
|
**Debug bindings:**
|
|
52
52
|
```typescript
|
|
53
|
-
// In
|
|
54
|
-
async postConfigure(): Promise<void> {
|
|
55
|
-
this.logger.info('Available bindings: %
|
|
56
|
-
Array.from(this.bindings.keys())
|
|
57
|
-
);
|
|
53
|
+
// In your Application class - `bindings` is protected on the container it extends
|
|
54
|
+
override async postConfigure(): Promise<void> {
|
|
55
|
+
this.logger.info('[postConfigure] Available bindings: %j', Array.from(this.bindings.keys()));
|
|
58
56
|
}
|
|
59
57
|
```
|
|
60
58
|
|
|
@@ -77,16 +75,21 @@ curl -H "Authorization: Bearer YOUR_TOKEN" \
|
|
|
77
75
|
|
|
78
76
|
**Enable detailed logging:**
|
|
79
77
|
```bash
|
|
80
|
-
#
|
|
78
|
+
# `debug` is the only gated level - it is dropped unless DEBUG is set
|
|
81
79
|
DEBUG=true
|
|
80
|
+
|
|
81
|
+
# File logging is OPT-IN. Without this, logs go to console/dgram only
|
|
82
|
+
APP_ENV_LOGGER_FOLDER_PATH=./logs
|
|
82
83
|
```
|
|
83
84
|
|
|
84
85
|
**Use method-scoped logging with `.for()`:**
|
|
85
86
|
```typescript
|
|
87
|
+
import { ApplicationLogger, type ILogger } from '@venizia/ignis-helpers';
|
|
88
|
+
|
|
86
89
|
class UserService {
|
|
87
|
-
private logger =
|
|
90
|
+
private logger: ILogger = ApplicationLogger.get('UserService');
|
|
88
91
|
|
|
89
|
-
async createUser(data:
|
|
92
|
+
async createUser(data: TCreateUserRequest) {
|
|
90
93
|
this.logger.for('createUser').info('Creating user: %j', data);
|
|
91
94
|
// Output: [UserService-createUser] Creating user: {...}
|
|
92
95
|
|
|
@@ -102,16 +105,19 @@ class UserService {
|
|
|
102
105
|
}
|
|
103
106
|
```
|
|
104
107
|
|
|
108
|
+
Annotate against `ILogger`, never a concrete provider class. Acquire a logger via `BaseHelper.logger` (inside any helper, service, controller or repository), `ApplicationLogger.get('Scope')`, or `LoggerFactory.getLogger(['A', 'B'])`.
|
|
109
|
+
|
|
105
110
|
**Common debugging commands:**
|
|
106
111
|
```bash
|
|
107
|
-
# View application logs
|
|
108
|
-
|
|
112
|
+
# View application logs - the rotating files are <APP_ENV_APPLICATION_NAME>-info-<date>.log
|
|
113
|
+
# and -error-<date>.log inside APP_ENV_LOGGER_FOLDER_PATH
|
|
114
|
+
tail -f "$APP_ENV_LOGGER_FOLDER_PATH"/*-info-*.log
|
|
109
115
|
|
|
110
|
-
# Check TypeScript compilation errors
|
|
111
|
-
|
|
116
|
+
# Check TypeScript compilation errors (from the repo root)
|
|
117
|
+
make core
|
|
112
118
|
|
|
113
119
|
# Validate environment variables
|
|
114
|
-
|
|
120
|
+
grep APP_ENV .env
|
|
115
121
|
```
|
|
116
122
|
|
|
117
123
|
**Useful debugging patterns:**
|
|
@@ -144,10 +150,10 @@ async getUser(c: Context) {
|
|
|
144
150
|
**Filtering logs by request:**
|
|
145
151
|
```bash
|
|
146
152
|
# Find all logs for a specific request
|
|
147
|
-
grep "abc123"
|
|
153
|
+
grep "abc123" "$APP_ENV_LOGGER_FOLDER_PATH"/*-info-*.log
|
|
148
154
|
|
|
149
155
|
# Extract request timing
|
|
150
|
-
grep "\[abc123\]"
|
|
156
|
+
grep "\[abc123\]" "$APP_ENV_LOGGER_FOLDER_PATH"/*-info-*.log | grep "Took:"
|
|
151
157
|
```
|
|
152
158
|
|
|
153
159
|
**Why this matters:**
|
|
@@ -159,12 +165,16 @@ grep "\[abc123\]" logs/app.log | grep "Took:"
|
|
|
159
165
|
|
|
160
166
|
When Zod validation fails, IGNIS returns a structured error response. Understanding this format helps debug client-side issues.
|
|
161
167
|
|
|
162
|
-
**Error response structure** (
|
|
168
|
+
**Error response structure** (`message`/`normalized.code` come from the first issue; the fallback message is `ValidationError`):
|
|
163
169
|
```json
|
|
164
170
|
{
|
|
165
171
|
"statusCode": 422,
|
|
166
172
|
"message": "Invalid email address",
|
|
167
|
-
"
|
|
173
|
+
"normalized": {
|
|
174
|
+
"text": "Invalid email address",
|
|
175
|
+
"code": "invalid_format",
|
|
176
|
+
"args": {}
|
|
177
|
+
},
|
|
168
178
|
"requestId": "abc123",
|
|
169
179
|
"details": {
|
|
170
180
|
"url": "http://localhost:3000/api/users",
|