@venizia/ignis-docs 0.2.0 → 0.2.1-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +24 -13
- 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 +27 -3
- 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 +8 -4
- 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 +247 -153
- 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 +109 -323
- package/content/extensions/components/authentication/api.md +489 -602
- package/content/extensions/components/authentication/errors.md +135 -498
- package/content/extensions/components/authentication/index.md +89 -801
- package/content/extensions/components/authentication/usage.md +231 -955
- package/content/extensions/components/authorization/api.md +991 -644
- package/content/extensions/components/authorization/errors.md +204 -208
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +88 -795
- package/content/extensions/components/authorization/usage.md +219 -528
- package/content/extensions/components/health-check.md +77 -243
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +553 -285
- package/content/extensions/components/mail/errors.md +76 -62
- package/content/extensions/components/mail/index.md +111 -463
- package/content/extensions/components/mail/usage.md +139 -173
- package/content/extensions/components/request-tracker.md +70 -173
- package/content/extensions/components/socket-io/api.md +462 -785
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +69 -372
- package/content/extensions/components/socket-io/usage.md +212 -105
- package/content/extensions/components/static-asset/api.md +461 -141
- package/content/extensions/components/static-asset/errors.md +121 -53
- package/content/extensions/components/static-asset/index.md +84 -606
- package/content/extensions/components/static-asset/usage.md +184 -299
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +311 -404
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +75 -407
- package/content/extensions/components/websocket/usage.md +120 -338
- package/content/extensions/helpers/cron/index.md +52 -160
- package/content/extensions/helpers/crypto/index.md +67 -480
- package/content/extensions/helpers/crypto/reference.md +528 -0
- package/content/extensions/helpers/env/index.md +64 -178
- package/content/extensions/helpers/error/index.md +286 -196
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +76 -550
- package/content/extensions/helpers/inversion/reference.md +530 -0
- package/content/extensions/helpers/kafka/admin.md +23 -3
- package/content/extensions/helpers/kafka/compile-binary.md +83 -52
- package/content/extensions/helpers/kafka/consumer.md +65 -28
- package/content/extensions/helpers/kafka/examples.md +46 -253
- package/content/extensions/helpers/kafka/index.md +55 -617
- package/content/extensions/helpers/kafka/producer.md +156 -22
- package/content/extensions/helpers/kafka/schema-registry.md +59 -79
- package/content/extensions/helpers/logger/hf-logger.md +220 -0
- package/content/extensions/helpers/logger/index.md +78 -552
- package/content/extensions/helpers/logger/pino.md +105 -0
- package/content/extensions/helpers/logger/reference.md +937 -0
- package/content/extensions/helpers/network/api.md +276 -195
- package/content/extensions/helpers/network/index.md +87 -524
- package/content/extensions/helpers/queue/index.md +80 -897
- package/content/extensions/helpers/queue/reference.md +494 -0
- package/content/extensions/helpers/redis/index.md +86 -640
- package/content/extensions/helpers/redis/reference.md +784 -0
- package/content/extensions/helpers/secrets/index.md +136 -0
- package/content/extensions/helpers/socket-io/api.md +325 -204
- package/content/extensions/helpers/socket-io/index.md +79 -429
- package/content/extensions/helpers/storage/api.md +584 -464
- package/content/extensions/helpers/storage/index.md +80 -573
- package/content/extensions/helpers/types/index.md +75 -495
- package/content/extensions/helpers/types/reference.md +689 -0
- package/content/extensions/helpers/uid/index.md +176 -189
- package/content/extensions/helpers/websocket/api.md +366 -214
- package/content/extensions/helpers/websocket/index.md +68 -503
- package/content/extensions/helpers/worker-thread/index.md +62 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/extensions/index.md +38 -39
- package/content/extensions/src-details/mcp-server.md +96 -548
- package/content/guides/core-concepts/persistent/datasources.md +2 -2
- package/content/guides/core-concepts/persistent/index.md +5 -1
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/pglite.md +218 -0
- package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
- package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
- package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/get-started/5-minute-quickstart.md +59 -206
- package/content/guides/get-started/philosophy.md +135 -670
- package/content/guides/get-started/setup.md +53 -74
- package/content/guides/migrations/redis-helpers-migration.md +5 -4
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +8 -7
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +64 -22
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +600 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +25 -46
- package/content/references/base/filter-system/application-usage.md +95 -123
- package/content/references/base/filter-system/array-operators.md +33 -43
- package/content/references/base/filter-system/comparison-operators.md +55 -63
- package/content/references/base/filter-system/default-filter.md +144 -350
- package/content/references/base/filter-system/fields-order-pagination.md +116 -148
- package/content/references/base/filter-system/index.md +114 -253
- package/content/references/base/filter-system/json-filtering.md +52 -181
- package/content/references/base/filter-system/list-operators.md +28 -44
- package/content/references/base/filter-system/logical-operators.md +69 -114
- package/content/references/base/filter-system/null-operators.md +41 -98
- package/content/references/base/filter-system/pattern-matching.md +48 -51
- package/content/references/base/filter-system/quick-reference.md +93 -196
- package/content/references/base/filter-system/range-operators.md +22 -38
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +173 -232
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +43 -28
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +81 -1452
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +281 -419
- package/content/references/base/repositories/index.md +84 -644
- package/content/references/base/repositories/mixins.md +25 -21
- package/content/references/base/repositories/relations.md +194 -375
- package/content/references/base/repositories/soft-deletable.md +67 -55
- package/content/references/base/secrets.md +267 -0
- package/content/references/base/services.md +8 -6
- package/content/references/configuration/environment-variables.md +73 -21
- package/content/references/configuration/index.md +51 -31
- package/content/references/index.md +1 -1
- 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/duration.md +85 -0
- package/content/references/utilities/index.md +6 -2
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +81 -61
- 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 +58 -218
- package/content/references/utilities/retry.md +139 -0
- 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/dist/mcp-server/index.js +0 -0
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
- package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
- package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
- package/package.json +24 -23
|
@@ -102,6 +102,9 @@ Controller → Service → Repository → DataSource
|
|
|
102
102
|
// ✅ Error Handling
|
|
103
103
|
throw getError({ statusCode: 404, message: 'User not found' });
|
|
104
104
|
|
|
105
|
+
// ✅ Scoped Logging (ILogger - never names a provider)
|
|
106
|
+
this.logger.for('createUser').info('User created | id: %s', user.id);
|
|
107
|
+
|
|
105
108
|
// ✅ Input Validation
|
|
106
109
|
request: { body: jsonContent({ schema: z.object({ email: z.string().email() }) }) }
|
|
107
110
|
```
|
|
@@ -120,6 +123,9 @@ async getUser(c: Context) {
|
|
|
120
123
|
// ❌ Catching all errors silently
|
|
121
124
|
try { await riskyOperation(); } catch (e) { /* swallowed */ }
|
|
122
125
|
|
|
126
|
+
// ❌ Raw Error - loses statusCode, messageCode and the normalized response shape
|
|
127
|
+
throw new Error('User not found'); // Use getError()
|
|
128
|
+
|
|
123
129
|
// ❌ Using `any` type
|
|
124
130
|
const data: any = await fetchData(); // Use proper types!
|
|
125
131
|
```
|
|
@@ -9,6 +9,7 @@ Identify bottlenecks before optimizing:
|
|
|
9
9
|
```typescript
|
|
10
10
|
import { executeWithPerformanceMeasure } from '@venizia/ignis-helpers';
|
|
11
11
|
|
|
12
|
+
// `logger` is optional and typed `ILogger` - it falls back to `console` when omitted.
|
|
12
13
|
await executeWithPerformanceMeasure({
|
|
13
14
|
logger: this.logger,
|
|
14
15
|
scope: 'DataProcessing',
|
|
@@ -148,13 +149,22 @@ if (!cached) {
|
|
|
148
149
|
|
|
149
150
|
| Setting | Value | Why |
|
|
150
151
|
|---------|-------|-----|
|
|
151
|
-
| `NODE_ENV` | `production` | Enables library optimizations |
|
|
152
|
-
|
|
|
153
|
-
|
|
|
152
|
+
| `NODE_ENV` | `production` | Enables library optimizations; also gates error-detail leakage (unset is treated as production) |
|
|
153
|
+
| `APP_ENV_LOGGER_LEVEL` | `info` or `warn` | Keep hot-path logging off the critical path |
|
|
154
|
+
| Process Manager | systemd, Docker, Kubernetes | Auto-restart, supervision |
|
|
155
|
+
| Horizontal Scaling | One process per CPU core | Utilize all CPUs |
|
|
156
|
+
|
|
157
|
+
Bun has no PM2-style cluster mode, and `Bun.serve` is started without `reusePort` - two processes cannot share a port. Scale out with one process per port behind a reverse proxy, or with container replicas:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# Each replica binds its own port; nginx / a load balancer fans out across them
|
|
161
|
+
APP_ENV_SERVER_PORT=3000 NODE_ENV=production bun run dist/index.js &
|
|
162
|
+
APP_ENV_SERVER_PORT=3001 NODE_ENV=production bun run dist/index.js &
|
|
163
|
+
```
|
|
154
164
|
|
|
155
|
-
**PM2 Cluster Mode:**
|
|
156
165
|
```bash
|
|
157
|
-
|
|
166
|
+
# Or let the orchestrator do it
|
|
167
|
+
docker compose up -d --scale app=4
|
|
158
168
|
```
|
|
159
169
|
|
|
160
170
|
## 6. Transaction Support
|
|
@@ -208,12 +218,12 @@ Connection pooling significantly improves performance by reusing database connec
|
|
|
208
218
|
```typescript
|
|
209
219
|
import { Pool } from 'pg';
|
|
210
220
|
import { datasource } from '@venizia/ignis';
|
|
211
|
-
import {
|
|
221
|
+
import { BaseRelationalDataSource } from '@venizia/ignis/postgres';
|
|
212
222
|
import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
|
|
213
223
|
|
|
214
224
|
// IDataSourceConfigs: your settings interface (host/port/user/password/database)
|
|
215
225
|
@datasource({ driver: NodePostgresDriver })
|
|
216
|
-
export class PostgresDataSource extends
|
|
226
|
+
export class PostgresDataSource extends BaseRelationalDataSource<IDataSourceConfigs> {
|
|
217
227
|
override configure(): void {
|
|
218
228
|
// Keep the pool on `this.client` - NodePostgresDriver above wires the driver and Drizzle
|
|
219
229
|
// connector from it lazily, on first getConnector()/beginTransaction()
|
|
@@ -385,9 +395,9 @@ const logger = HfLogger.get('OrderEngine');
|
|
|
385
395
|
const MSG_ORDER_SENT = HfLogger.encodeMessage('Order sent');
|
|
386
396
|
const MSG_ORDER_FILLED = HfLogger.encodeMessage('Order filled');
|
|
387
397
|
|
|
388
|
-
// Start background flusher
|
|
398
|
+
// Start background flusher (writes to stdout by default; pass { filePath } or { sink })
|
|
389
399
|
const flusher = new HfLogFlusher();
|
|
390
|
-
flusher.start(100); // Flush
|
|
400
|
+
flusher.start(100); // Flush interval in ms - 100 is also the default
|
|
391
401
|
|
|
392
402
|
// In hot path (~100-300ns, zero allocation):
|
|
393
403
|
logger.log('info', MSG_ORDER_SENT);
|
|
@@ -395,11 +405,14 @@ logger.log('info', MSG_ORDER_FILLED);
|
|
|
395
405
|
```
|
|
396
406
|
|
|
397
407
|
**Key points:**
|
|
408
|
+
- `log(level, bytes)` is the zero-allocation path. Passing a string, or any `...args`, falls back to formatting
|
|
409
|
+
- Levels are the same five as `ILogger`: `debug`, `info`, `warn`, `error`, `emerg`
|
|
398
410
|
- Pre-encode messages at initialization, not in hot path
|
|
399
|
-
-
|
|
400
|
-
-
|
|
411
|
+
- HfLogger uses a lock-free ring buffer (64K entries x 256 bytes = 16MB), allocated lazily on first use
|
|
412
|
+
- Scope truncates at 32 UTF-8 bytes, message at 213 - silently
|
|
413
|
+
- For the standard logger, narrow output with `APP_ENV_LOGGER_LEVEL` in production and keep debug lines behind the `DEBUG` gate (pre-computed at module load, near-zero cost when off)
|
|
401
414
|
|
|
402
|
-
> **Deep Dive:** See [
|
|
415
|
+
> **Deep Dive:** See [HfLogger](../extensions/helpers/logger/hf-logger.md) for the complete API.
|
|
403
416
|
|
|
404
417
|
## Performance Checklist
|
|
405
418
|
|
|
@@ -414,7 +427,7 @@ logger.log('info', MSG_ORDER_FILLED);
|
|
|
414
427
|
| **Memory** | Large datasets processed in batches | High |
|
|
415
428
|
| **Caching** | Expensive queries cached | High |
|
|
416
429
|
| **Workers** | CPU-intensive tasks offloaded | High |
|
|
417
|
-
| **Logging** | HfLogger for hot paths (HFT) | High |
|
|
430
|
+
| **Logging** | HfLogger for hot paths (HFT); `APP_ENV_LOGGER_LEVEL` in production | High |
|
|
418
431
|
| **Monitoring** | Performance logging enabled | Low |
|
|
419
432
|
|
|
420
433
|
## See Also
|
|
@@ -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
|
|
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",
|