@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.
Files changed (180) hide show
  1. package/README.md +7 -7
  2. package/content/best-practices/api-usage-examples.md +15 -12
  3. package/content/best-practices/architectural-patterns.md +70 -78
  4. package/content/best-practices/architecture-decisions.md +91 -60
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +56 -44
  6. package/content/best-practices/code-style-standards/constants-configuration.md +11 -11
  7. package/content/best-practices/code-style-standards/control-flow.md +5 -2
  8. package/content/best-practices/code-style-standards/documentation.md +13 -13
  9. package/content/best-practices/code-style-standards/function-patterns.md +9 -10
  10. package/content/best-practices/code-style-standards/index.md +1 -1
  11. package/content/best-practices/code-style-standards/naming-conventions.md +10 -8
  12. package/content/best-practices/code-style-standards/route-definitions.md +30 -12
  13. package/content/best-practices/code-style-standards/tooling.md +8 -5
  14. package/content/best-practices/code-style-standards/type-safety.md +13 -12
  15. package/content/best-practices/common-pitfalls.md +56 -37
  16. package/content/best-practices/contribution-workflow.md +13 -14
  17. package/content/best-practices/data-modeling.md +46 -22
  18. package/content/best-practices/deployment-strategies.md +28 -27
  19. package/content/best-practices/error-handling.md +48 -24
  20. package/content/best-practices/index.md +5 -5
  21. package/content/best-practices/performance-optimization.md +40 -31
  22. package/content/best-practices/security-guidelines.md +52 -23
  23. package/content/best-practices/testing-strategies.md +65 -51
  24. package/content/best-practices/troubleshooting-tips.md +24 -24
  25. package/content/extensions/components/{swagger.md → api-reference.md} +40 -31
  26. package/content/extensions/components/authentication/api.md +19 -19
  27. package/content/extensions/components/authentication/errors.md +7 -7
  28. package/content/extensions/components/authentication/index.md +10 -8
  29. package/content/extensions/components/authentication/usage.md +101 -6
  30. package/content/extensions/components/authorization/api.md +45 -25
  31. package/content/extensions/components/authorization/errors.md +6 -6
  32. package/content/extensions/components/authorization/index.md +11 -10
  33. package/content/extensions/components/authorization/usage.md +21 -21
  34. package/content/extensions/components/health-check.md +1 -1
  35. package/content/extensions/components/index.md +5 -5
  36. package/content/extensions/components/mail/errors.md +15 -15
  37. package/content/extensions/components/mail/index.md +1 -2
  38. package/content/extensions/components/mail/usage.md +1 -1
  39. package/content/extensions/components/request-tracker.md +1 -1
  40. package/content/extensions/components/socket-io/api.md +9 -9
  41. package/content/extensions/components/socket-io/errors.md +5 -5
  42. package/content/extensions/components/socket-io/index.md +8 -8
  43. package/content/extensions/components/socket-io/usage.md +1 -1
  44. package/content/extensions/components/static-asset/api.md +17 -4
  45. package/content/extensions/components/static-asset/errors.md +4 -4
  46. package/content/extensions/components/static-asset/index.md +26 -28
  47. package/content/extensions/components/static-asset/usage.md +13 -12
  48. package/content/extensions/components/template/index.md +2 -2
  49. package/content/extensions/components/template/setup-page.md +1 -1
  50. package/content/extensions/components/websocket/api.md +3 -3
  51. package/content/extensions/components/websocket/errors.md +5 -5
  52. package/content/extensions/components/websocket/index.md +5 -5
  53. package/content/extensions/components/websocket/usage.md +3 -3
  54. package/content/extensions/helpers/cron/index.md +2 -2
  55. package/content/extensions/helpers/crypto/index.md +1 -1
  56. package/content/extensions/helpers/env/index.md +27 -12
  57. package/content/extensions/helpers/error/index.md +81 -25
  58. package/content/extensions/helpers/index.md +2 -3
  59. package/content/extensions/helpers/inversion/index.md +15 -7
  60. package/content/extensions/helpers/kafka/compile-binary.md +92 -0
  61. package/content/extensions/helpers/kafka/examples.md +1 -1
  62. package/content/extensions/helpers/kafka/index.md +3 -0
  63. package/content/extensions/helpers/logger/index.md +32 -2
  64. package/content/extensions/helpers/network/index.md +6 -0
  65. package/content/extensions/helpers/queue/index.md +14 -17
  66. package/content/extensions/helpers/redis/index.md +548 -323
  67. package/content/extensions/helpers/socket-io/index.md +14 -10
  68. package/content/extensions/helpers/storage/api.md +44 -8
  69. package/content/extensions/helpers/storage/index.md +43 -7
  70. package/content/extensions/helpers/template/index.md +6 -3
  71. package/content/extensions/helpers/types/index.md +11 -8
  72. package/content/extensions/helpers/websocket/api.md +9 -9
  73. package/content/extensions/helpers/websocket/index.md +7 -7
  74. package/content/extensions/helpers/worker-thread/index.md +2 -2
  75. package/content/extensions/index.md +3 -4
  76. package/content/extensions/src-details/mcp-server.md +18 -24
  77. package/content/guides/core-concepts/application/bootstrapping.md +11 -14
  78. package/content/guides/core-concepts/application/index.md +3 -3
  79. package/content/guides/core-concepts/components.md +19 -10
  80. package/content/guides/core-concepts/dependency-injection.md +6 -3
  81. package/content/guides/core-concepts/grpc-controllers.md +6 -5
  82. package/content/guides/core-concepts/persistent/datasources.md +42 -43
  83. package/content/guides/core-concepts/persistent/index.md +16 -7
  84. package/content/guides/core-concepts/persistent/models.md +24 -20
  85. package/content/guides/core-concepts/persistent/postgres-drivers.md +201 -0
  86. package/content/guides/core-concepts/persistent/repositories.md +40 -23
  87. package/content/guides/core-concepts/persistent/search-meilisearch.md +185 -0
  88. package/content/guides/core-concepts/persistent/search-typesense.md +431 -0
  89. package/content/guides/core-concepts/persistent/transactions.md +61 -25
  90. package/content/guides/core-concepts/rest-controllers.md +12 -9
  91. package/content/guides/core-concepts/services.md +330 -60
  92. package/content/guides/get-started/5-minute-quickstart.md +15 -15
  93. package/content/guides/get-started/philosophy.md +36 -36
  94. package/content/guides/get-started/setup.md +3 -3
  95. package/content/guides/index.md +3 -3
  96. package/content/guides/migrations/redis-helpers-migration.md +177 -0
  97. package/content/guides/migrations/scoped-rbac-migration.md +17 -17
  98. package/content/guides/migrations/unified-connectors-migration.md +113 -0
  99. package/content/guides/reference/glossary.md +19 -12
  100. package/content/guides/reference/mcp-docs-server.md +22 -18
  101. package/content/guides/tutorials/building-a-crud-api.md +37 -44
  102. package/content/guides/tutorials/complete-installation.md +17 -17
  103. package/content/guides/tutorials/ecommerce-api.md +163 -124
  104. package/content/guides/tutorials/realtime-chat.md +181 -135
  105. package/content/guides/tutorials/testing.md +65 -523
  106. package/content/index.md +2 -180
  107. package/content/public/apple-touch-icon.png +0 -0
  108. package/content/public/og-image.png +0 -0
  109. package/content/public/site.webmanifest +11 -0
  110. package/content/references/base/application.md +4 -5
  111. package/content/references/base/bootstrapping.md +18 -5
  112. package/content/references/base/components.md +149 -120
  113. package/content/references/base/connectors.md +178 -0
  114. package/content/references/base/controllers.md +41 -30
  115. package/content/references/base/datasources.md +163 -92
  116. package/content/references/base/dependency-injection.md +34 -22
  117. package/content/references/base/filter-system/application-usage.md +17 -14
  118. package/content/references/base/filter-system/array-operators.md +7 -2
  119. package/content/references/base/filter-system/comparison-operators.md +3 -0
  120. package/content/references/base/filter-system/default-filter.md +89 -71
  121. package/content/references/base/filter-system/fields-order-pagination.md +22 -22
  122. package/content/references/base/filter-system/index.md +6 -3
  123. package/content/references/base/filter-system/json-filtering.md +20 -1
  124. package/content/references/base/filter-system/list-operators.md +1 -1
  125. package/content/references/base/filter-system/logical-operators.md +33 -1
  126. package/content/references/base/filter-system/null-operators.md +30 -1
  127. package/content/references/base/filter-system/quick-reference.md +23 -4
  128. package/content/references/base/filter-system/tips.md +5 -5
  129. package/content/references/base/filter-system/use-cases.md +12 -12
  130. package/content/references/base/grpc-controllers.md +13 -13
  131. package/content/references/base/index.md +24 -12
  132. package/content/references/base/middlewares.md +265 -327
  133. package/content/references/base/models.md +63 -49
  134. package/content/references/base/providers.md +136 -130
  135. package/content/references/base/repositories/advanced.md +59 -58
  136. package/content/references/base/repositories/index.md +115 -91
  137. package/content/references/base/repositories/mixins.md +55 -291
  138. package/content/references/base/repositories/relations.md +54 -64
  139. package/content/references/base/repositories/soft-deletable.md +31 -30
  140. package/content/references/base/services.md +296 -93
  141. package/content/references/configuration/environment-variables.md +49 -31
  142. package/content/references/configuration/index.md +6 -6
  143. package/content/references/index.md +17 -12
  144. package/content/references/quick-reference.md +65 -106
  145. package/content/references/utilities/crypto.md +65 -23
  146. package/content/references/utilities/index.md +3 -3
  147. package/content/references/utilities/jsx.md +6 -4
  148. package/content/references/utilities/module.md +68 -20
  149. package/content/references/utilities/parse.md +4 -14
  150. package/content/references/utilities/promise.md +9 -7
  151. package/content/references/utilities/schema.md +5 -3
  152. package/dist/mcp-server/common/guards.d.ts +8 -0
  153. package/dist/mcp-server/common/guards.d.ts.map +1 -0
  154. package/dist/mcp-server/common/guards.js +14 -0
  155. package/dist/mcp-server/common/guards.js.map +1 -0
  156. package/dist/mcp-server/common/index.d.ts +1 -0
  157. package/dist/mcp-server/common/index.d.ts.map +1 -1
  158. package/dist/mcp-server/common/index.js +1 -0
  159. package/dist/mcp-server/common/index.js.map +1 -1
  160. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  161. package/dist/mcp-server/helpers/docs.helper.js +4 -2
  162. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  163. package/dist/mcp-server/helpers/github.helper.js +1 -1
  164. package/dist/mcp-server/index.js +7 -2
  165. package/dist/mcp-server/index.js.map +1 -1
  166. package/dist/mcp-server/tools/base.tool.d.ts +6 -2
  167. package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
  168. package/dist/mcp-server/tools/base.tool.js.map +1 -1
  169. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  171. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  172. package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
  173. package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
  174. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  175. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
  176. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
  177. package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
  178. package/package.json +9 -9
  179. package/content/extensions/helpers/testing/index.md +0 -510
  180. package/content/references/base/middleware.md +0 -347
@@ -1,21 +1,24 @@
1
1
  # Data Modeling
2
2
 
3
- Ignis streamlines data modeling with Drizzle ORM by providing powerful helpers and "enrichers" that reduce boilerplate code for common schema patterns.
3
+ IGNIS streamlines data modeling with Drizzle ORM by providing powerful helpers and "enrichers" that reduce boilerplate code for common schema patterns.
4
+
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.
4
7
 
5
8
  ## 1. Base Entity
6
9
 
7
- All entity models should extend `BaseEntity`. This provides integration with the framework's repository layer and automatic schema generation support.
10
+ All PostgreSQL entity models should extend `BasePostgresEntity`. This provides integration with the framework's repository layer and automatic schema generation support.
8
11
 
9
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.
10
13
 
11
14
  **Example (`src/models/entities/user.model.ts`):**
12
15
 
13
16
  ```typescript
14
- import { BaseEntity, extraUserColumns, generateIdColumnDefs, model } from '@venizia/ignis';
17
+ import { BasePostgresEntity, extraUserColumns, generateIdColumnDefs, model } from '@venizia/ignis';
15
18
  import { pgTable } from 'drizzle-orm/pg-core';
16
19
 
17
20
  @model({ type: 'entity' })
18
- export class User extends BaseEntity<typeof User.schema> {
21
+ export class User extends BasePostgresEntity<typeof User.schema> {
19
22
  // 1. Define schema as a static property
20
23
  static override schema = pgTable('User', {
21
24
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
@@ -29,7 +32,7 @@ export class User extends BaseEntity<typeof User.schema> {
29
32
 
30
33
  ## 2. Schema Enrichers
31
34
 
32
- Instead of manually defining common columns like primary keys, timestamps, or audit fields in every table, use Ignis "enrichers".
35
+ Instead of manually defining common columns like primary keys, timestamps, or audit fields in every table, use IGNIS "enrichers".
33
36
 
34
37
  **Available Enrichers:**
35
38
 
@@ -50,7 +53,7 @@ import {
50
53
  generateTzColumnDefs,
51
54
  generateUserAuditColumnDefs,
52
55
  } from '@venizia/ignis';
53
- import { pgTable, text } from 'drizzle-orm/pg-core';
56
+ import { pgTable, text, unique } from 'drizzle-orm/pg-core';
54
57
 
55
58
  export const configurationTable = pgTable(
56
59
  'Configuration',
@@ -151,7 +154,7 @@ export const commentTable = pgTable('Comment', {
151
154
  **Querying polymorphic relations:**
152
155
  ```typescript
153
156
  // Find all comments on a specific post
154
- const comments = await commentRepo.find({
157
+ const comments = await commentRepository.find({
155
158
  filter: {
156
159
  where: {
157
160
  commentableType: 'Post',
@@ -161,7 +164,7 @@ const comments = await commentRepo.find({
161
164
  });
162
165
 
163
166
  // Find all comments on a product
164
- const productComments = await commentRepo.find({
167
+ const productComments = await commentRepository.find({
165
168
  filter: {
166
169
  where: {
167
170
  commentableType: 'Product',
@@ -186,11 +189,11 @@ Relations are defined using the `TRelationConfig` structure within the static `r
186
189
 
187
190
  **One-to-One (belongsTo):**
188
191
  ```typescript
189
- import { BaseEntity, model, RelationTypes, TRelationConfig } from '@venizia/ignis';
192
+ import { BasePostgresEntity, model, RelationTypes, TRelationConfig } from '@venizia/ignis';
190
193
  import { User } from './user.model';
191
194
 
192
195
  @model({ type: 'entity' })
193
- export class Configuration extends BaseEntity<typeof Configuration.schema> {
196
+ export class Configuration extends BasePostgresEntity<typeof Configuration.schema> {
194
197
  static override schema = pgTable('Configuration', {
195
198
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
196
199
  createdBy: text('created_by'),
@@ -215,7 +218,7 @@ export class Configuration extends BaseEntity<typeof Configuration.schema> {
215
218
  **One-to-Many (hasMany):**
216
219
  ```typescript
217
220
  @model({ type: 'entity' })
218
- export class User extends BaseEntity<typeof User.schema> {
221
+ export class User extends BasePostgresEntity<typeof User.schema> {
219
222
  static override schema = pgTable('User', {
220
223
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
221
224
  name: text('name').notNull(),
@@ -248,7 +251,7 @@ export class User extends BaseEntity<typeof User.schema> {
248
251
 
249
252
  ```typescript
250
253
  // Eager load single relation
251
- const configs = await configRepo.find({
254
+ const configs = await configurationRepository.find({
252
255
  filter: {
253
256
  include: [{ relation: 'creator' }],
254
257
  },
@@ -256,7 +259,7 @@ const configs = await configRepo.find({
256
259
  // Result: [{ id, code, ..., creator: { id, name, email } }]
257
260
 
258
261
  // Eager load multiple relations
259
- const users = await userRepo.find({
262
+ const users = await userRepository.find({
260
263
  filter: {
261
264
  include: [
262
265
  { relation: 'posts' },
@@ -266,7 +269,7 @@ const users = await userRepo.find({
266
269
  });
267
270
 
268
271
  // Nested relations (up to 2 levels recommended)
269
- const users = await userRepo.find({
272
+ const users = await userRepository.find({
270
273
  filter: {
271
274
  include: [{
272
275
  relation: 'posts',
@@ -283,7 +286,7 @@ const users = await userRepo.find({
283
286
 
284
287
  ## 4. Repositories and Auto-Discovery
285
288
 
286
- Ignis simplifies the connection between models, repositories, and datasources.
289
+ IGNIS simplifies the connection between models, repositories, and datasources.
287
290
 
288
291
  ### DataSource Auto-Discovery
289
292
 
@@ -291,8 +294,21 @@ DataSources automatically discover their schema from the repositories that bind
291
294
 
292
295
  ```typescript
293
296
  // src/datasources/postgres.datasource.ts
294
- @datasource({ driver: 'node-postgres' })
295
- export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
297
+ import { datasource, ValueOrPromise } from '@venizia/ignis';
298
+ import { BasePostgresDataSource } from '@venizia/ignis/postgres';
299
+ import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
300
+ import { Pool } from 'pg';
301
+
302
+ interface IDataSourceConfigs {
303
+ host: string;
304
+ port: number;
305
+ database: string;
306
+ user: string;
307
+ password: string;
308
+ }
309
+
310
+ @datasource({ driver: NodePostgresDriver })
311
+ export class PostgresDataSource extends BasePostgresDataSource<IDataSourceConfigs> {
296
312
  constructor() {
297
313
  super({
298
314
  name: PostgresDataSource.name,
@@ -302,9 +318,17 @@ export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, I
302
318
  }
303
319
 
304
320
  override configure(): ValueOrPromise<void> {
305
- // This method automatically collects all schemas from bound repositories
306
- const schema = this.getSchema();
307
- this.connector = drizzle({ client: new Pool(this.settings), schema });
321
+ // getSchema() automatically collects all schemas from bound repositories
322
+ this.logger.debug('[configure] Auto-discovered schema | Keys: %o', Object.keys(this.getSchema()));
323
+
324
+ // Keep the pool on this.client - naming NodePostgresDriver above is what wires the driver
325
+ // and Drizzle connector; beginTransaction() resolves its driver from this.client lazily.
326
+ this.client = new Pool(this.settings);
327
+ }
328
+
329
+ override getConnectionString(): ValueOrPromise<string> {
330
+ const { host, port, user, password, database } = this.settings;
331
+ return `postgresql://${user}:${password}@${host}:${port}/${database}`;
308
332
  }
309
333
  }
310
334
  ```
@@ -356,7 +380,7 @@ Protect sensitive data by configuring properties that are excluded at the SQL le
356
380
  hiddenProperties: ['password', 'secret'],
357
381
  },
358
382
  })
359
- export class User extends BaseEntity<typeof User.schema> {
383
+ export class User extends BasePostgresEntity<typeof User.schema> {
360
384
  static override schema = pgTable('User', {
361
385
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
362
386
  email: text('email').notNull(),
@@ -387,7 +411,7 @@ Declare your model's authorization principal in `@model` settings to make the mo
387
411
  hiddenProperties: ['password'],
388
412
  },
389
413
  })
390
- export class User extends BaseEntity<typeof User.schema> {
414
+ export class User extends BasePostgresEntity<typeof User.schema> {
391
415
  static override schema = pgTable('User', {
392
416
  ...generateIdColumnDefs({ id: { dataType: 'string' } }),
393
417
  email: text('email').notNull(),
@@ -1,6 +1,6 @@
1
1
  # Deployment Strategies
2
2
 
3
- Deploy your Ignis application reliably, securely, and efficiently.
3
+ Deploy your IGNIS application reliably, securely, and efficiently.
4
4
 
5
5
  ## 1. Building for Production
6
6
 
@@ -46,7 +46,7 @@ FROM oven/bun:1-slim
46
46
  WORKDIR /usr/src/app
47
47
 
48
48
  # Copy dependency files
49
- COPY package.json bun.lockb ./
49
+ COPY package.json bun.lock ./
50
50
 
51
51
  # Install production dependencies
52
52
  RUN bun install --production --frozen-lockfile
@@ -109,7 +109,7 @@ services:
109
109
  condition: service_started
110
110
  restart: unless-stopped
111
111
  healthcheck:
112
- test: ["CMD", "curl", "-f", "http://localhost:3000/health-check"]
112
+ test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
113
113
  interval: 30s
114
114
  timeout: 10s
115
115
  retries: 3
@@ -188,7 +188,7 @@ scp dist/my-app .env user@server:/app/
188
188
  ```ini
189
189
  # /etc/systemd/system/my-app.service
190
190
  [Unit]
191
- Description=My Ignis Application
191
+ Description=My IGNIS Application
192
192
  After=network.target
193
193
 
194
194
  [Service]
@@ -225,7 +225,7 @@ Add health check endpoint for load balancers:
225
225
  this.component(HealthCheckComponent);
226
226
  ```
227
227
 
228
- Access at `/health-check` for liveness/readiness probes.
228
+ Access at `/health` (default path, configurable) for liveness/readiness probes.
229
229
 
230
230
  ## 5. Build Debugging
231
231
 
@@ -273,30 +273,30 @@ Total time: 8.47s
273
273
 
274
274
  ### Force Update Strategy
275
275
 
276
- Keep dependencies in sync with the NPM registry using the force-update script:
276
+ Keep dependencies in sync with the NPM registry using each package's force-update script (`packages/<name>/scripts/force-update.sh`, exposed as the `force-update` script in that package):
277
277
 
278
278
  ```bash
279
- # Update to latest stable versions
280
- ./scripts/force-update.sh latest
279
+ # Update to latest stable versions (from a package directory)
280
+ bun run force-update latest
281
281
 
282
282
  # Update to pre-release versions (for testing new features)
283
- ./scripts/force-update.sh next
283
+ bun run force-update next
284
284
  ```
285
285
 
286
286
  **What it does:**
287
- 1. Queries NPM registry for the specified tag (`latest` or `next`)
288
- 2. Updates `package.json` with exact versions
289
- 3. Applies to all `@venizia/*` packages
287
+ 1. Queries NPM registry for the specified tag (`latest`, `next`, or `highest`)
288
+ 2. Updates the package's `@venizia/*` dependencies in `package.json` with exact versions
290
289
 
291
290
  **When to use:**
292
291
  | Tag | Use Case |
293
292
  |-----|----------|
294
293
  | `latest` | Production deployments, stable releases |
295
294
  | `next` | Testing new features, pre-release validation |
295
+ | `highest` | Highest released version by semver sort |
296
296
 
297
- **Makefile shortcuts:**
297
+ **Makefile shortcuts (repo root):**
298
298
  ```bash
299
- make update # Force update all packages (latest)
299
+ make update # Reinstall deps (postinstall runs force-update)
300
300
  make update-core # Update only @venizia/ignis
301
301
  make update-helpers # Update only @venizia/ignis-helpers
302
302
  ```
@@ -447,7 +447,7 @@ Railway provides simple deployments with automatic builds:
447
447
  },
448
448
  "deploy": {
449
449
  "startCommand": "bun run server:prod",
450
- "healthcheckPath": "/health-check",
450
+ "healthcheckPath": "/health",
451
451
  "healthcheckTimeout": 30,
452
452
  "restartPolicyType": "ON_FAILURE",
453
453
  "restartPolicyMaxRetries": 3
@@ -495,7 +495,7 @@ primary_region = "sjc"
495
495
  interval = 10000
496
496
  grace_period = "5s"
497
497
  method = "get"
498
- path = "/health-check"
498
+ path = "/health"
499
499
  protocol = "http"
500
500
  timeout = 2000
501
501
  ```
@@ -524,7 +524,7 @@ services:
524
524
  generateValue: true
525
525
  - key: APP_ENV_JWT_SECRET
526
526
  generateValue: true
527
- healthCheckPath: /health-check
527
+ healthCheckPath: /health
528
528
  autoDeploy: true
529
529
 
530
530
  databases:
@@ -579,13 +579,13 @@ spec:
579
579
  key: db-host
580
580
  livenessProbe:
581
581
  httpGet:
582
- path: /health-check
582
+ path: /health
583
583
  port: 3000
584
584
  initialDelaySeconds: 10
585
585
  periodSeconds: 30
586
586
  readinessProbe:
587
587
  httpGet:
588
- path: /health-check
588
+ path: /health
589
589
  port: 3000
590
590
  initialDelaySeconds: 5
591
591
  periodSeconds: 10
@@ -634,7 +634,7 @@ kubectl logs -l app=ignis -f
634
634
 
635
635
  ### Health Check Endpoints
636
636
 
637
- Ignis provides built-in health checks:
637
+ IGNIS provides built-in health checks:
638
638
 
639
639
  ```typescript
640
640
  // In application.ts
@@ -643,9 +643,9 @@ import { HealthCheckComponent } from '@venizia/ignis';
643
643
  this.component(HealthCheckComponent);
644
644
  ```
645
645
 
646
- **Endpoints:**
647
- - `GET /health-check` - Basic liveness check
648
- - `GET /health-check/ready` - Readiness (includes DB connection)
646
+ **Endpoints (default path `/health`, configurable via component options):**
647
+ - `GET /health` - Basic liveness check
648
+ - `POST /health/ping` - Echo/ping check (send `{ "message": "..." }`, get a PONG back)
649
649
 
650
650
  ### Logging in Production
651
651
 
@@ -654,11 +654,12 @@ Configure structured logging:
654
654
  ```typescript
655
655
  import { LoggerFactory } from '@venizia/ignis-helpers';
656
656
 
657
- // Set log level via environment
658
- // APP_ENV_LOG_LEVEL=info (debug, info, warn, error)
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).
659
660
 
660
661
  const logger = LoggerFactory.getLogger(['MyService']);
661
- logger.info('Service started', { port: 3000, env: 'production' });
662
+ logger.info('Service started | port: %d | env: %s', 3000, 'production');
662
663
  ```
663
664
 
664
665
  ### Metrics Collection
@@ -669,7 +670,7 @@ Add Prometheus metrics endpoint:
669
670
  // src/controllers/metrics.controller.ts
670
671
  @controller({ path: '/metrics' })
671
672
  export class MetricsController extends BaseRestController {
672
- @get({ configs: { path: '/' } })
673
+ @get({ configs: { path: '/', responses: { 200: { description: 'Prometheus metrics' } } } })
673
674
  getMetrics(c: Context) {
674
675
  return c.text(`
675
676
  # HELP http_requests_total Total HTTP requests
@@ -1,6 +1,6 @@
1
1
  # Error Handling
2
2
 
3
- Comprehensive guide to handling errors gracefully in Ignis applications.
3
+ Comprehensive guide to handling errors gracefully in IGNIS applications.
4
4
 
5
5
  ## Error Handling Philosophy
6
6
 
@@ -13,7 +13,7 @@ Comprehensive guide to handling errors gracefully in Ignis applications.
13
13
 
14
14
  ## 1. Using `getError` Helper
15
15
 
16
- Ignis provides `getError` for creating consistent, structured errors.
16
+ IGNIS provides `getError` for creating consistent, structured errors.
17
17
 
18
18
  ```typescript
19
19
  import { getError, HTTP } from '@venizia/ignis-helpers';
@@ -52,7 +52,7 @@ Use the correct status code for each error type:
52
52
  | 401 | `RS_4.Unauthorized` | Missing or invalid authentication |
53
53
  | 403 | `RS_4.Forbidden` | Authenticated but insufficient permissions |
54
54
  | 404 | `RS_4.NotFound` | Resource does not exist |
55
- | 409 | `RS_4.Conflict` | Resource already exists (custom duplicate handling) |
55
+ | 409 | `RS_4.Conflict` | Resource already exists (custom duplicate handling); transient DB conflicts (deadlock / serialization failure, auto-handled) |
56
56
  | 422 | `RS_4.UnprocessableEntity` | Validation failed (Zod errors) |
57
57
  | 429 | `RS_4.TooManyRequests` | Rate limit exceeded |
58
58
  | 500 | `RS_5.InternalServerError` | Unexpected server error |
@@ -60,7 +60,7 @@ Use the correct status code for each error type:
60
60
  | 503 | `RS_5.ServiceUnavailable` | Service temporarily down |
61
61
 
62
62
  :::tip Automatic Database Error Handling
63
- Database errors in SQLSTATE class `22` (data exception) and `23` (integrity constraint unique, foreign key, not null, check, exclusion) are automatically converted to HTTP 400 by the global error middleware. You don't need to catch these manually. Other classes (e.g. syntax / undefined column) stay 500, and production responses are sanitized see [Repository Layer Errors](#repository-layer-errors).
63
+ Database errors in SQLSTATE classes `22` (data exception), `23` (integrity constraint - unique, foreign key, not null, check, exclusion), and `44` (WITH CHECK OPTION violation) are automatically converted to HTTP 400 by the global error middleware. Transient conflicts (`40001` serialization failure, `40P01` deadlock) become HTTP 409 with a retryable message. You don't need to catch these manually. Other classes (e.g. syntax / undefined column) stay 500, and production responses are sanitized - see [Repository Layer Errors](#repository-layer-errors).
64
64
  :::
65
65
 
66
66
  ## 3. Error Handling Patterns
@@ -73,12 +73,12 @@ import { getError, HTTP } from '@venizia/ignis-helpers';
73
73
 
74
74
  export class UserService extends BaseService {
75
75
  async createUser(data: TCreateUserRequest): Promise<TUser> {
76
- // Validate business rules
77
- const existingUser = await this.userRepo.findOne({
76
+ // Validate business rules (findOne returns the record or null)
77
+ const existingUser = await this.userRepository.findOne({
78
78
  filter: { where: { email: data.email } },
79
79
  });
80
80
 
81
- if (existingUser.data) {
81
+ if (existingUser) {
82
82
  throw getError({
83
83
  statusCode: HTTP.ResultCodes.RS_4.Conflict,
84
84
  message: 'Email already registered',
@@ -95,13 +95,16 @@ export class UserService extends BaseService {
95
95
  data.email, error.message);
96
96
  }
97
97
 
98
- return this.userRepo.create({ data });
98
+ // create returns { count, data }
99
+ const created = await this.userRepository.create({ data });
100
+ return created.data;
99
101
  }
100
102
 
101
103
  async getUserOrFail(id: string): Promise<TUser> {
102
- const user = await this.userRepo.findById({ id });
104
+ // findById returns the record or null (no wrapper object)
105
+ const user = await this.userRepository.findById({ id });
103
106
 
104
- if (!user.data) {
107
+ if (!user) {
105
108
  throw getError({
106
109
  statusCode: HTTP.ResultCodes.RS_4.NotFound,
107
110
  message: 'User not found',
@@ -109,7 +112,7 @@ export class UserService extends BaseService {
109
112
  });
110
113
  }
111
114
 
112
- return user.data;
115
+ return user;
113
116
  }
114
117
  }
115
118
  ```
@@ -148,33 +151,34 @@ export class UserController extends BaseRestController {
148
151
 
149
152
  ### Repository Layer Errors
150
153
 
151
- Database errors in SQLSTATE class `22` (data exception) and `23` (integrity constraint unique, foreign key, not null, check, exclusion) are **automatically handled** by the global error middleware and return HTTP 400. Codes outside those classes (e.g. class `42` undefined column an application/SQL bug) correctly stay 500.
154
+ Database errors in SQLSTATE classes `22` (data exception), `23` (integrity constraint - unique, foreign key, not null, check, exclusion), and `44` (WITH CHECK OPTION violation) are **automatically handled** by the global error middleware and return HTTP 400. Transient conflicts (`40001` serialization failure, `40P01` deadlock) return HTTP 409 with a generic retryable message. Codes outside those classes (e.g. class `42` undefined column - an application/SQL bug) correctly stay 500.
152
155
 
153
156
  **Non-production** returns the full driver context for debugging:
154
157
 
155
158
  ```json
156
159
  {
157
160
  "message": "Unique constraint violation\nDetail: Key (email)=(test@example.com) already exists.\nTable: User\nConstraint: UQ_User_email",
161
+ "messageCode": "core.system_error",
158
162
  "statusCode": 400,
159
163
  "requestId": "abc123"
160
164
  }
161
165
  ```
162
166
 
163
167
  :::warning Production sanitizes database internals
164
- In production the message is the **base message only** `Detail:` (which echoes row values like emails), `Table:`, and `Constraint:` are stripped, and `details.stack`/`details.cause` are omitted. Unexpected (non-client) database errors and connection failures return a generic `"Internal Server Error"`, so SQL, schema names, and connection host/port never leak. Use `requestId` + server logs to diagnose.
168
+ In production the message is the **base message only** - `Detail:` (which echoes row values like emails), `Table:`, and `Constraint:` are stripped, and `details.stack`/`details.cause` are omitted. Unexpected (non-client) database errors and connection failures return a generic `"Internal Server Error"`, so SQL, schema names, and connection host/port never leak. Use `requestId` + server logs to diagnose.
165
169
 
166
170
  ```json
167
- { "message": "Unique constraint violation", "statusCode": 400, "requestId": "abc123" }
171
+ { "message": "Unique constraint violation", "messageCode": "core.system_error", "statusCode": 400, "requestId": "abc123" }
168
172
  ```
169
173
  :::
170
174
 
171
175
  You don't need to wrap repository calls in try-catch for constraint errors. If you need custom error messages, you can still handle them explicitly:
172
176
 
173
177
  ```typescript
174
- import { BaseRepository } from '@venizia/ignis';
178
+ import { DefaultCRUDRepository } from '@venizia/ignis';
175
179
  import { getError, HTTP } from '@venizia/ignis-helpers';
176
180
 
177
- export class UserRepository extends BaseRepository<typeof User.schema> {
181
+ export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
178
182
  async createWithCustomError(data: TCreateUser): Promise<TCreateResult<TUser>> {
179
183
  try {
180
184
  return await this.create({ data });
@@ -194,11 +198,11 @@ export class UserRepository extends BaseRepository<typeof User.schema> {
194
198
 
195
199
  ## 4. Global Error Handler
196
200
 
197
- Ignis includes a built-in error handler. Customize behavior in your application:
201
+ IGNIS includes a built-in error handler. Customize behavior in your application:
198
202
 
199
203
  ```typescript
200
204
  import { BaseApplication } from '@venizia/ignis';
201
- import { ApplicationError } from '@venizia/ignis-helpers';
205
+ import { ApplicationError, MessageCode } from '@venizia/ignis-helpers';
202
206
 
203
207
  export class Application extends BaseApplication {
204
208
  override setupMiddlewares(): void {
@@ -216,6 +220,7 @@ export class Application extends BaseApplication {
216
220
  return c.json({
217
221
  statusCode: error.statusCode,
218
222
  message: error.message,
223
+ messageCode: error.messageCode, // already lower-cased, never undefined
219
224
  details: error.details,
220
225
  requestId,
221
226
  }, error.statusCode as StatusCode);
@@ -226,6 +231,7 @@ export class Application extends BaseApplication {
226
231
  return c.json({
227
232
  statusCode: 422,
228
233
  message: 'Validation failed',
234
+ messageCode: MessageCode.DEFAULT,
229
235
  details: { cause: error.errors },
230
236
  requestId,
231
237
  }, 422);
@@ -235,6 +241,7 @@ export class Application extends BaseApplication {
235
241
  return c.json({
236
242
  statusCode: 500,
237
243
  message: 'Internal server error',
244
+ messageCode: MessageCode.DEFAULT,
238
245
  requestId,
239
246
  }, 500);
240
247
  });
@@ -271,19 +278,23 @@ interface ErrorResponse {
271
278
  {
272
279
  "statusCode": 400,
273
280
  "message": "Invalid request body",
281
+ "messageCode": "core.system_error",
274
282
  "requestId": "abc123"
275
283
  }
276
284
 
277
285
  // 404 Not Found
286
+ // Extra keys passed to getError(...) (e.g. `details`) surface under `extra`;
287
+ // the top-level `details` object is reserved for middleware context (url, path, stack, cause).
278
288
  {
279
289
  "statusCode": 404,
280
290
  "message": "User not found",
291
+ "messageCode": "core.system_error",
281
292
  "requestId": "abc123",
282
- "details": { "id": "user-uuid" }
293
+ "extra": { "details": { "id": "user-uuid" } }
283
294
  }
284
295
 
285
296
  // 422 Validation Error
286
- // `message`/`messageCode` come from the first failing issue its `params.code` if the schema set
297
+ // `message`/`messageCode` come from the first failing issue - its `params.code` if the schema set
287
298
  // one, otherwise the raw Zod code (e.g. `invalid_type`, `too_small`). The full list stays in `details.cause`.
288
299
  {
289
300
  "statusCode": 422,
@@ -305,12 +316,25 @@ interface ErrorResponse {
305
316
  {
306
317
  "statusCode": 500,
307
318
  "message": "Internal server error",
319
+ "messageCode": "core.system_error",
308
320
  "requestId": "abc123"
309
321
  }
310
322
  ```
311
323
 
312
324
  ## 6. Logging Errors
313
325
 
326
+ ### `%s`, Never `%j`, for an `Error`
327
+
328
+ `message` and `stack` are non-enumerable properties on a native `Error` (and on `ApplicationError`, which extends it). `%j` serializes via `JSON.stringify`, which only visits enumerable own properties - so `logger.error('... | error: %j', error)` silently drops both `message` and `stack`, logging little more than `{}`. Always use `%s` to log an `Error` instance; reserve `%j`/`%o` for plain data objects.
329
+
330
+ ```typescript
331
+ // ✅ Good - %s prints message + stack
332
+ this.logger.error('[createOrder] Failed | error: %s', error);
333
+
334
+ // ❌ Bad - %j drops message and stack (non-enumerable)
335
+ this.logger.error('[createOrder] Failed | error: %j', error);
336
+ ```
337
+
314
338
  ### What to Log
315
339
 
316
340
  ```typescript
@@ -358,7 +382,7 @@ this.logger.debug('[query] Executing | sql: %s | params: %j', sql, params);
358
382
  ```typescript
359
383
  // ✅ Good - Errors propagate naturally with async/await
360
384
  async function processOrder(orderId: string) {
361
- const order = await orderRepo.findById({ id: orderId }); // Throws if fails
385
+ const order = await orderRepository.findById({ id: orderId }); // Throws if fails
362
386
  const payment = await paymentService.charge(order); // Throws if fails
363
387
  return payment;
364
388
  }
@@ -402,11 +426,11 @@ this.sendNotification(userId); // If this rejects, crash!
402
426
 
403
427
  ```typescript
404
428
  async function transferFunds(from: string, to: string, amount: number) {
405
- const tx = await accountRepo.beginTransaction();
429
+ const tx = await accountRepository.beginTransaction();
406
430
 
407
431
  try {
408
- await accountRepo.debit({ id: from, amount }, { transaction: tx });
409
- await accountRepo.credit({ id: to, amount }, { transaction: tx });
432
+ await accountRepository.debit({ id: from, amount, options: { transaction: tx } });
433
+ await accountRepository.credit({ id: to, amount, options: { transaction: tx } });
410
434
 
411
435
  await tx.commit();
412
436
  return { success: true };
@@ -1,6 +1,6 @@
1
1
  # Best Practices
2
2
 
3
- Production-ready patterns, security guidelines, and optimization strategies for building robust Ignis applications. These best practices are distilled from real-world experience building enterprise applications.
3
+ Production-ready patterns, security guidelines, and optimization strategies for building robust IGNIS applications. These best practices are distilled from real-world experience building enterprise applications.
4
4
 
5
5
  <div class="guide-cards">
6
6
 
@@ -112,7 +112,7 @@ request: { body: jsonContent({ schema: z.object({ email: z.string().email() }) }
112
112
  // ❌ Business logic in controllers
113
113
  @get({ configs: RouteConfigs.GET_USER })
114
114
  async getUser(c: Context) {
115
- const user = await this.userRepo.findById(id);
115
+ const user = await this.userRepository.findById({ id });
116
116
  if (user.lastLogin < cutoff) await this.sendReminder(user); // Move to service!
117
117
  return c.json(user);
118
118
  }
@@ -130,7 +130,7 @@ const data: any = await fetchData(); // Use proper types!
130
130
  |-------|--------|
131
131
  | Secrets | Store in environment variables, never in code |
132
132
  | Input | Validate with Zod schemas at API boundaries |
133
- | Auth | Protect routes with `authStrategies: [Authentication.STRATEGY_JWT]` |
133
+ | Auth | Protect routes with `authenticate: { strategies: [Authentication.STRATEGY_JWT] }` |
134
134
  | Sensitive data | Use `hiddenProperties` in model settings |
135
135
  | File uploads | Use `sanitizeFilename()` for all user-provided filenames |
136
136
  | CORS | Configure allowed origins explicitly |
@@ -186,7 +186,7 @@ const data: any = await fetchData(); // Use proper types!
186
186
  |-------|-------------|
187
187
  | [Contribution Workflow](./contribution-workflow) | Git workflow, PR guidelines, code review |
188
188
 
189
- > [!TIP] New to Ignis?
189
+ > [!TIP] New to IGNIS?
190
190
  > Start with the [Getting Started Guide](/guides/) for tutorials, then return here for production-ready patterns.
191
191
 
192
192
  > [!WARNING] Production Deployment?
@@ -194,7 +194,7 @@ const data: any = await fetchData(); // Use proper types!
194
194
 
195
195
  ## See Also
196
196
 
197
- - [Getting Started](/guides/) - New to Ignis? Start here
197
+ - [Getting Started](/guides/) - New to IGNIS? Start here
198
198
  - [API Reference](/references/) - Detailed API documentation
199
199
  - [Core Concepts](/guides/core-concepts/application/) - Deep dive into architecture
200
200
  - [Changelogs](/changelogs/) - Version history and updates