@venizia/ignis-docs 0.2.0 → 0.2.1-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +22 -11
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +6 -2
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +182 -93
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. package/package.json +8 -8
@@ -3,22 +3,22 @@
3
3
  IGNIS streamlines data modeling with Drizzle ORM by providing powerful helpers and "enrichers" that reduce boilerplate code for common schema patterns.
4
4
 
5
5
  > [!NOTE] Scope: PostgreSQL connector
6
- > This page covers Drizzle-backed models (`BasePostgresEntity`; the legacy `BaseEntity` export is a compatibility alias for the same class) and enrichers, which are specific to relational tables. Search documents (typesense connector) use `defineSearchCollection` instead - see [Search & Typesense](/guides/core-concepts/persistent/search-typesense). See [Connectors](/references/base/connectors) for how the engine-neutral `AbstractEntity` relates to each connector's concrete entity class.
6
+ > This page covers Drizzle-backed models (`BaseRelationalEntity`; the legacy `BasePostgresEntity` and `BaseEntity` exports are compatibility aliases for the same class) and enrichers, which are specific to relational tables. Search documents (typesense connector) use `defineSearchCollection` instead - see [Search & Typesense](/guides/core-concepts/persistent/search-typesense). See [Connectors](/references/base/connectors) for how the engine-neutral `AbstractEntity` relates to each connector's concrete entity class.
7
7
 
8
8
  ## 1. Base Entity
9
9
 
10
- All PostgreSQL entity models should extend `BasePostgresEntity`. This provides integration with the framework's repository layer and automatic schema generation support.
10
+ All PostgreSQL entity models should extend `BaseRelationalEntity`. This provides integration with the framework's repository layer and automatic schema generation support.
11
11
 
12
12
  The recommended pattern is to define the schema and relations as **static properties** on the class. This keeps the definition self-contained and enables powerful type inference.
13
13
 
14
14
  **Example (`src/models/entities/user.model.ts`):**
15
15
 
16
16
  ```typescript
17
- import { BasePostgresEntity, extraUserColumns, generateIdColumnDefs, model } from '@venizia/ignis';
17
+ import { BaseRelationalEntity, extraUserColumns, generateIdColumnDefs, model } from '@venizia/ignis';
18
18
  import { pgTable } from 'drizzle-orm/pg-core';
19
19
 
20
20
  @model({ type: 'entity' })
21
- export class User extends BasePostgresEntity<typeof User.schema> {
21
+ export class User extends BaseRelationalEntity<typeof User.schema> {
22
22
  // 1. Define schema as a static property
23
23
  static override schema = pgTable('User', {
24
24
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
@@ -40,10 +40,10 @@ Instead of manually defining common columns like primary keys, timestamps, or au
40
40
  |----------|-------------|---------------|
41
41
  | `generateIdColumnDefs` | Adds a Primary Key | `id` (text, number, or big-number) |
42
42
  | `generatePrincipalColumnDefs` | Adds polymorphic relation fields | `{discriminator}Id`, `{discriminator}Type` |
43
- | `generateTzColumnDefs` | Adds timestamps | `createdAt`, `modifiedAt` (auto-updating) |
43
+ | `generateTzColumnDefs` | Adds timestamps | `createdAt`, `modifiedAt` (auto-updating); `deletedAt` via `{ deleted: { enable: true, ... } }` |
44
44
  | `generateUserAuditColumnDefs` | Adds audit fields | `createdBy`, `modifiedBy` (supports `allowAnonymous` option) |
45
- | `generateDataTypeColumnDefs` | Adds generic value fields | `nValue` (number), `tValue` (text), `jValue` (json), etc. |
46
- | `extraUserColumns` | Comprehensive user fields | Combines audit, timestamps, status, and type fields |
45
+ | `generateDataTypeColumnDefs` | Adds generic value fields | `dataType`, `nValue` (number), `tValue` (text), `bValue` (bytea), `jValue` (jsonb), `boValue` (boolean) |
46
+ | `extraUserColumns` | Auth user fields | `realm`, `status`, `type`, `activatedAt`, `lastLoginAt`, `parentId` |
47
47
 
48
48
  **Usage Example:**
49
49
 
@@ -90,8 +90,8 @@ The `generateIdColumnDefs` enricher supports multiple ID strategies:
90
90
  |-----------|-----------------|-----------------|----------|
91
91
  | `string` | `TEXT` | `string` | UUIDs, custom IDs, distributed systems |
92
92
  | `number` | `INTEGER GENERATED ALWAYS AS IDENTITY` | `number` | Auto-increment, simple sequences |
93
- | `big-number` (mode: `number`) | `BIGINT GENERATED ALWAYS AS IDENTITY` | `number` | Large sequences (up to 2^53) |
94
- | `big-number` (mode: `bigint`) | `BIGINT GENERATED ALWAYS AS IDENTITY` | `bigint` | Very large sequences (up to 2^64) |
93
+ | `big-number` (`numberMode: 'number'`, default) | `BIGINT GENERATED ALWAYS AS IDENTITY` | `number` | Large sequences (up to 2^53) |
94
+ | `big-number` (`numberMode: 'bigint'`) | `BIGINT GENERATED ALWAYS AS IDENTITY` | `bigint` | Very large sequences (up to 2^64) |
95
95
 
96
96
  **Examples:**
97
97
 
@@ -189,11 +189,11 @@ Relations are defined using the `TRelationConfig` structure within the static `r
189
189
 
190
190
  **One-to-One (belongsTo):**
191
191
  ```typescript
192
- import { BasePostgresEntity, model, RelationTypes, TRelationConfig } from '@venizia/ignis';
192
+ import { BaseRelationalEntity, model, RelationTypes, TRelationConfig } from '@venizia/ignis';
193
193
  import { User } from './user.model';
194
194
 
195
195
  @model({ type: 'entity' })
196
- export class Configuration extends BasePostgresEntity<typeof Configuration.schema> {
196
+ export class Configuration extends BaseRelationalEntity<typeof Configuration.schema> {
197
197
  static override schema = pgTable('Configuration', {
198
198
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
199
199
  createdBy: text('created_by'),
@@ -216,32 +216,45 @@ export class Configuration extends BasePostgresEntity<typeof Configuration.schem
216
216
  ```
217
217
 
218
218
  **One-to-Many (hasMany):**
219
+
220
+ The foreign key lives on the **`ONE` side only**. A `MANY` relation carries no `fields`/`references` - Drizzle's `many()` takes just `relationName`, pointing back at the `ONE` relation that owns the key.
221
+
219
222
  ```typescript
223
+ // Owning side: Post declares the FK
220
224
  @model({ type: 'entity' })
221
- export class User extends BasePostgresEntity<typeof User.schema> {
222
- static override schema = pgTable('User', {
225
+ export class Post extends BaseRelationalEntity<typeof Post.schema> {
226
+ static override schema = pgTable('Post', {
223
227
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
224
- name: text('name').notNull(),
228
+ authorId: text('author_id').notNull(),
225
229
  });
226
230
 
227
231
  static override relations = (): TRelationConfig[] => [
228
232
  {
229
- name: 'posts', // User.posts
230
- type: RelationTypes.MANY, // One User → Many Posts
231
- schema: Post.schema,
233
+ name: 'author',
234
+ type: RelationTypes.ONE,
235
+ schema: User.schema,
232
236
  metadata: {
233
- fields: [User.schema.id],
234
- references: [Post.schema.authorId],
237
+ fields: [Post.schema.authorId],
238
+ references: [User.schema.id],
235
239
  },
236
240
  },
241
+ ];
242
+ }
243
+
244
+ // Inverse side: User just names the relation back
245
+ @model({ type: 'entity' })
246
+ export class User extends BaseRelationalEntity<typeof User.schema> {
247
+ static override schema = pgTable('User', {
248
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
249
+ name: text('name').notNull(),
250
+ });
251
+
252
+ static override relations = (): TRelationConfig[] => [
237
253
  {
238
- name: 'comments', // User.comments
239
- type: RelationTypes.MANY,
240
- schema: Comment.schema,
241
- metadata: {
242
- fields: [User.schema.id],
243
- references: [Comment.schema.userId],
244
- },
254
+ name: 'posts', // User.posts
255
+ type: RelationTypes.MANY, // One User -> Many Posts
256
+ schema: Post.schema,
257
+ metadata: { relationName: 'author' }, // The ONE relation on Post
245
258
  },
246
259
  ];
247
260
  }
@@ -295,7 +308,7 @@ DataSources automatically discover their schema from the repositories that bind
295
308
  ```typescript
296
309
  // src/datasources/postgres.datasource.ts
297
310
  import { datasource, ValueOrPromise } from '@venizia/ignis';
298
- import { BasePostgresDataSource } from '@venizia/ignis/postgres';
311
+ import { BaseRelationalDataSource } from '@venizia/ignis/postgres';
299
312
  import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
300
313
  import { Pool } from 'pg';
301
314
 
@@ -308,7 +321,7 @@ interface IDataSourceConfigs {
308
321
  }
309
322
 
310
323
  @datasource({ driver: NodePostgresDriver })
311
- export class PostgresDataSource extends BasePostgresDataSource<IDataSourceConfigs> {
324
+ export class PostgresDataSource extends BaseRelationalDataSource<IDataSourceConfigs> {
312
325
  constructor() {
313
326
  super({
314
327
  name: PostgresDataSource.name,
@@ -343,7 +356,7 @@ For most repositories, you don't need a constructor. The DataSource is automatic
343
356
 
344
357
  ```typescript
345
358
  @repository({ model: Configuration, dataSource: PostgresDataSource })
346
- export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {
359
+ export class ConfigurationRepository extends DefaultRelationalRepository<typeof Configuration.schema> {
347
360
  // No constructor needed!
348
361
  }
349
362
  ```
@@ -380,7 +393,7 @@ Protect sensitive data by configuring properties that are excluded at the SQL le
380
393
  hiddenProperties: ['password', 'secret'],
381
394
  },
382
395
  })
383
- export class User extends BasePostgresEntity<typeof User.schema> {
396
+ export class User extends BaseRelationalEntity<typeof User.schema> {
384
397
  static override schema = pgTable('User', {
385
398
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
386
399
  email: text('email').notNull(),
@@ -411,7 +424,7 @@ Declare your model's authorization principal in `@model` settings to make the mo
411
424
  hiddenProperties: ['password'],
412
425
  },
413
426
  })
414
- export class User extends BasePostgresEntity<typeof User.schema> {
427
+ export class User extends BaseRelationalEntity<typeof User.schema> {
415
428
  static override schema = pgTable('User', {
416
429
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
417
430
  email: text('email').notNull(),
@@ -437,17 +450,27 @@ Drizzle Kit handles schema migrations. Follow these best practices for safe migr
437
450
 
438
451
  ### Generate Migrations
439
452
 
440
- ```bash
441
- # Generate migration from schema changes
442
- bun run db:generate
453
+ Wire these as scripts in your app's `package.json`, pointing at your Drizzle config:
443
454
 
444
- # Apply migrations to database
445
- bun run db:migrate
455
+ ```json
456
+ {
457
+ "scripts": {
458
+ "migrate:generate": "drizzle-kit generate --config=src/migration.ts",
459
+ "migrate:dev": "drizzle-kit migrate --config=src/migration.ts",
460
+ "migrate:push": "drizzle-kit push --config=src/migration.ts"
461
+ }
462
+ }
463
+ ```
446
464
 
447
- # Push schema directly (development only)
448
- bun run db:push
465
+ ```bash
466
+ bun run migrate:generate # Generate migration from schema changes
467
+ bun run migrate:dev # Apply pending migrations
468
+ bun run migrate:push # Push schema directly (development only)
449
469
  ```
450
470
 
471
+ > [!NOTE]
472
+ > Boot does nothing to your schema. Migrations are an explicit step you run - never a side effect of starting the application.
473
+
451
474
  ### Migration Best Practices
452
475
 
453
476
  | Practice | Description |
@@ -484,33 +507,26 @@ ALTER TABLE "User" RENAME COLUMN "old_name" TO "new_name";
484
507
 
485
508
  ### Custom Migration SQL
486
509
 
487
- For complex migrations, use custom SQL:
510
+ Drizzle Kit emits plain `.sql` files. For anything it cannot infer - backfills, concurrent indexes, data reshaping - edit the generated file or add your own:
488
511
 
489
- ```typescript
490
- // drizzle/migrations/0005_custom_migration.ts
491
- import { sql } from 'drizzle-orm';
492
-
493
- export async function up(db: DrizzleDB) {
494
- // Add index for performance
495
- await db.execute(sql`
496
- CREATE INDEX CONCURRENTLY idx_user_email
497
- ON "User" (email)
498
- WHERE status = 'ACTIVE'
499
- `);
500
-
501
- // Backfill data
502
- await db.execute(sql`
503
- UPDATE "User"
504
- SET normalized_email = LOWER(email)
505
- WHERE normalized_email IS NULL
506
- `);
507
- }
508
-
509
- export async function down(db: DrizzleDB) {
510
- await db.execute(sql`DROP INDEX IF EXISTS idx_user_email`);
511
- }
512
+ ```sql
513
+ -- drizzle/migrations/0005_custom_migration.sql
514
+
515
+ -- Backfill before enforcing the constraint
516
+ UPDATE "User"
517
+ SET normalized_email = LOWER(email)
518
+ WHERE normalized_email IS NULL;
519
+ --> statement-breakpoint
520
+
521
+ -- CONCURRENTLY cannot run inside a transaction - keep it in its own migration
522
+ CREATE INDEX CONCURRENTLY idx_user_email
523
+ ON "User" (email)
524
+ WHERE status = 'ACTIVE';
512
525
  ```
513
526
 
527
+ > [!WARNING]
528
+ > Drizzle wraps each migration in a transaction. `CREATE INDEX CONCURRENTLY` is rejected inside one - isolate it in its own migration and apply it out of band.
529
+
514
530
  ### Migration Checklist
515
531
 
516
532
  | Step | Action |
@@ -23,12 +23,17 @@ Use environment variables for all configuration - never hard-code.
23
23
  **Production Environment Variables:**
24
24
  | Variable | Value | Purpose |
25
25
  |----------|-------|---------|
26
- | `NODE_ENV` | `production` | Enables performance optimizations |
26
+ | `NODE_ENV` | `production` | Enables performance optimizations. Error responses are fail-closed: an unset or unrecognized value is treated as production |
27
27
  | `APP_ENV_APPLICATION_SECRET` | Strong random string | Application secret |
28
28
  | `APP_ENV_JWT_SECRET` | Strong random string | JWT signing key |
29
- | `APP_ENV_POSTGRES_*` | Production DB credentials | Database connection |
29
+ | `APP_ENV_POSTGRES_HOST` / `_PORT` / `_USERNAME` / `_PASSWORD` / `_DATABASE` | Production DB credentials | Database connection |
30
30
  | `APP_ENV_SERVER_HOST` | `0.0.0.0` | Accept connections from any IP |
31
- | `APP_ENV_SERVER_PORT` | `3000` or cloud-assigned | Server port |
31
+ | `APP_ENV_SERVER_PORT` | `3000` or cloud-assigned | Server port (`PORT` is also honoured) |
32
+ | `APP_ENV_LOGGER_LEVEL` | `info` | Logger floor - `debug` \| `info` \| `warn` \| `error` \| `emerg` |
33
+ | `APP_ENV_LOGGER_FOLDER_PATH` | Writable path | Opt-in rotating file output; unset means console only |
34
+
35
+ > [!WARNING]
36
+ > Leave `APP_ENV_LOGGER_DO_REDACT` unset in production. Setting it to `false` disables secret-key redaction in logs - a local-debugging switch only.
32
37
 
33
38
  **Where to store:**
34
39
  - **Docker:** Use environment variables in `docker-compose.yml` or secrets
@@ -40,27 +45,33 @@ Use environment variables for all configuration - never hard-code.
40
45
  ### Docker Deployment (Recommended)
41
46
 
42
47
  **Dockerfile:**
48
+ Two stages: the build needs devDependencies (`typescript`, `tsc-alias`), the runtime does not.
49
+
43
50
  ```dockerfile
44
- FROM oven/bun:1-slim
51
+ # --- Build stage ---
52
+ FROM oven/bun:1-slim AS builder
45
53
 
46
54
  WORKDIR /usr/src/app
47
55
 
48
- # Copy dependency files
49
56
  COPY package.json bun.lock ./
57
+ # All deps, including dev - `--production` would omit the compiler and the build would fail
58
+ RUN bun install --frozen-lockfile
50
59
 
51
- # Install production dependencies
52
- RUN bun install --production --frozen-lockfile
53
-
54
- # Copy source code
55
60
  COPY . .
56
-
57
- # Build TypeScript
58
61
  RUN bun run build
59
62
 
60
- # Expose port
63
+ # --- Runtime stage ---
64
+ FROM oven/bun:1-slim
65
+
66
+ WORKDIR /usr/src/app
67
+
68
+ COPY package.json bun.lock ./
69
+ RUN bun install --production --frozen-lockfile
70
+
71
+ COPY --from=builder /usr/src/app/dist ./dist
72
+
61
73
  EXPOSE 3000
62
74
 
63
- # Start server
64
75
  CMD [ "bun", "run", "server:prod" ]
65
76
  ```
66
77
 
@@ -161,13 +172,27 @@ docker-compose up -d --scale app=3
161
172
  Compile your app into a single standalone binary:
162
173
 
163
174
  ```bash
164
- bun build --compile --minify --target=bun-linux-x64 \
175
+ bun build --compile --minify --sourcemap --target=bun-linux-x64 \
165
176
  ./src/index.ts --outfile ./dist/my-app
166
177
  ```
167
178
 
168
179
  **Pros:** No dependencies needed on server, simple deployment
169
180
  **Cons:** Platform-specific, newer technology (test thoroughly)
170
181
 
182
+ > [!IMPORTANT]
183
+ > **A compiled binary MUST register a logger provider explicitly.** Logger providers are sub-path exports and `winston` is an optional peer, so the default provider is reached through a runtime `require` that the bundler cannot see. Without this line the binary throws on its first log call.
184
+ >
185
+ > ```typescript
186
+ > // src/index.ts - before the application boots
187
+ > import { LoggerFactory } from '@venizia/ignis-helpers';
188
+ > import { WinstonLogger } from '@venizia/ignis-helpers/winston';
189
+ > // or: import { PinoLogger } from '@venizia/ignis-helpers/pino';
190
+ >
191
+ > LoggerFactory.use({ provider: WinstonLogger });
192
+ > ```
193
+ >
194
+ > The same rule applies to any optional peer: only a value import of the class carries it into the bundle. Naming a driver class in `@datasource({ driver: NodePostgresDriver })` is what pulls `pg` in.
195
+
171
196
  **Deploy:**
172
197
  ```bash
173
198
  # Copy executable and .env to server
@@ -652,16 +677,28 @@ this.component(HealthCheckComponent);
652
677
  Configure structured logging:
653
678
 
654
679
  ```typescript
655
- import { LoggerFactory } from '@venizia/ignis-helpers';
680
+ import type { ILogger } from '@venizia/ignis-helpers';
681
+ import { ApplicationLogger, LoggerFactory } from '@venizia/ignis-helpers';
682
+ import { PinoLogger } from '@venizia/ignis-helpers/pino';
656
683
 
657
- // Debug-level logs are gated by the DEBUG environment variable (DEBUG=true)
658
- // and the current NODE_ENV. File output is configured via APP_ENV_LOGGER_*
659
- // variables (e.g. APP_ENV_LOGGER_FOLDER_PATH, APP_ENV_LOGGER_FORMAT).
684
+ // One line at the entrypoint selects the provider. Omit it and winston is loaded on
685
+ // first use - which only works when winston is installed and the app is not compiled.
686
+ LoggerFactory.use({ provider: PinoLogger });
660
687
 
661
- const logger = LoggerFactory.getLogger(['MyService']);
688
+ const logger: ILogger = ApplicationLogger.get('MyService');
662
689
  logger.info('Service started | port: %d | env: %s', 3000, 'production');
690
+
691
+ // Method-scoped child logger
692
+ logger.for('start').warn('Slow boot | took: %d ms', 1420);
663
693
  ```
664
694
 
695
+ **Operational notes:**
696
+ - Five levels, each a direct method: `debug`, `info`, `warn`, `error`, `emerg`
697
+ - `debug()` is compiled out at module load unless `DEBUG` is truthy and `NODE_ENV` is a development env - restart to change it
698
+ - Log an `Error` with `%s`; `%j` swallows the stack
699
+ - Secret-looking keys redact to `[REDACTED]` automatically
700
+ - File output is opt-in: without `APP_ENV_LOGGER_FOLDER_PATH` nothing is written to disk
701
+
665
702
  ### Metrics Collection
666
703
 
667
704
  Add Prometheus metrics endpoint: