@venizia/ignis-docs 0.0.3 → 0.0.4-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.
Files changed (131) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -2
  3. package/wiki/best-practices/api-usage-examples.md +591 -0
  4. package/wiki/best-practices/architectural-patterns.md +415 -0
  5. package/wiki/best-practices/architecture-decisions.md +488 -0
  6. package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +406 -17
  7. package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
  8. package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
  9. package/wiki/best-practices/data-modeling.md +376 -0
  10. package/wiki/best-practices/deployment-strategies.md +698 -0
  11. package/wiki/best-practices/index.md +27 -0
  12. package/wiki/best-practices/performance-optimization.md +196 -0
  13. package/wiki/best-practices/security-guidelines.md +218 -0
  14. package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
  15. package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
  16. package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
  17. package/wiki/changelogs/2025-12-17-refactor.md +1 -1
  18. package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
  19. package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
  20. package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +2 -2
  21. package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
  22. package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
  23. package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
  24. package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
  25. package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
  26. package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
  27. package/wiki/changelogs/index.md +6 -0
  28. package/wiki/changelogs/planned-schema-migrator.md +0 -8
  29. package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
  30. package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
  31. package/wiki/guides/core-concepts/components-guide.md +509 -0
  32. package/wiki/{get-started → guides}/core-concepts/components.md +24 -17
  33. package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
  34. package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
  35. package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
  36. package/wiki/guides/core-concepts/persistent/index.md +119 -0
  37. package/wiki/guides/core-concepts/persistent/models.md +241 -0
  38. package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
  39. package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
  40. package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
  41. package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
  42. package/wiki/guides/get-started/philosophy.md +682 -0
  43. package/wiki/guides/get-started/setup.md +157 -0
  44. package/wiki/guides/index.md +89 -0
  45. package/wiki/guides/reference/glossary.md +243 -0
  46. package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
  47. package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
  48. package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
  49. package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
  50. package/wiki/guides/tutorials/realtime-chat.md +1261 -0
  51. package/wiki/guides/tutorials/testing.md +723 -0
  52. package/wiki/index.md +176 -37
  53. package/wiki/references/base/application.md +27 -0
  54. package/wiki/references/base/bootstrapping.md +31 -26
  55. package/wiki/references/base/components.md +24 -7
  56. package/wiki/references/base/controllers.md +50 -20
  57. package/wiki/references/base/datasources.md +30 -0
  58. package/wiki/references/base/dependency-injection.md +39 -3
  59. package/wiki/references/base/filter-system/application-usage.md +224 -0
  60. package/wiki/references/base/filter-system/array-operators.md +132 -0
  61. package/wiki/references/base/filter-system/comparison-operators.md +109 -0
  62. package/wiki/references/base/filter-system/default-filter.md +428 -0
  63. package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
  64. package/wiki/references/base/filter-system/index.md +127 -0
  65. package/wiki/references/base/filter-system/json-filtering.md +197 -0
  66. package/wiki/references/base/filter-system/list-operators.md +71 -0
  67. package/wiki/references/base/filter-system/logical-operators.md +156 -0
  68. package/wiki/references/base/filter-system/null-operators.md +58 -0
  69. package/wiki/references/base/filter-system/pattern-matching.md +108 -0
  70. package/wiki/references/base/filter-system/quick-reference.md +431 -0
  71. package/wiki/references/base/filter-system/range-operators.md +63 -0
  72. package/wiki/references/base/filter-system/tips.md +190 -0
  73. package/wiki/references/base/filter-system/use-cases.md +452 -0
  74. package/wiki/references/base/index.md +90 -0
  75. package/wiki/references/base/middlewares.md +604 -0
  76. package/wiki/references/base/models.md +215 -23
  77. package/wiki/references/base/providers.md +731 -0
  78. package/wiki/references/base/repositories/advanced.md +555 -0
  79. package/wiki/references/base/repositories/index.md +228 -0
  80. package/wiki/references/base/repositories/mixins.md +331 -0
  81. package/wiki/references/base/repositories/relations.md +486 -0
  82. package/wiki/references/base/repositories.md +40 -635
  83. package/wiki/references/base/services.md +28 -4
  84. package/wiki/references/components/authentication.md +22 -2
  85. package/wiki/references/components/health-check.md +12 -0
  86. package/wiki/references/components/index.md +23 -0
  87. package/wiki/references/components/mail.md +687 -0
  88. package/wiki/references/components/request-tracker.md +16 -0
  89. package/wiki/references/components/socket-io.md +18 -0
  90. package/wiki/references/components/static-asset.md +14 -26
  91. package/wiki/references/components/swagger.md +17 -0
  92. package/wiki/references/configuration/environment-variables.md +427 -0
  93. package/wiki/references/configuration/index.md +73 -0
  94. package/wiki/references/helpers/cron.md +14 -0
  95. package/wiki/references/helpers/crypto.md +15 -0
  96. package/wiki/references/helpers/env.md +16 -0
  97. package/wiki/references/helpers/error.md +17 -0
  98. package/wiki/references/helpers/index.md +14 -0
  99. package/wiki/references/helpers/inversion.md +24 -4
  100. package/wiki/references/helpers/logger.md +19 -0
  101. package/wiki/references/helpers/network.md +11 -0
  102. package/wiki/references/helpers/queue.md +19 -0
  103. package/wiki/references/helpers/redis.md +21 -0
  104. package/wiki/references/helpers/socket-io.md +24 -5
  105. package/wiki/references/helpers/storage.md +18 -10
  106. package/wiki/references/helpers/testing.md +18 -0
  107. package/wiki/references/helpers/types.md +16 -0
  108. package/wiki/references/helpers/uid.md +167 -0
  109. package/wiki/references/helpers/worker-thread.md +16 -0
  110. package/wiki/references/index.md +177 -0
  111. package/wiki/references/quick-reference.md +634 -0
  112. package/wiki/references/src-details/boot.md +3 -3
  113. package/wiki/references/src-details/dev-configs.md +0 -4
  114. package/wiki/references/src-details/docs.md +2 -2
  115. package/wiki/references/src-details/index.md +86 -0
  116. package/wiki/references/src-details/inversion.md +1 -6
  117. package/wiki/references/src-details/mcp-server.md +3 -15
  118. package/wiki/references/utilities/index.md +86 -10
  119. package/wiki/references/utilities/jsx.md +577 -0
  120. package/wiki/references/utilities/request.md +0 -2
  121. package/wiki/references/utilities/statuses.md +740 -0
  122. package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
  123. package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
  124. package/wiki/get-started/best-practices/data-modeling.md +0 -177
  125. package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
  126. package/wiki/get-started/best-practices/performance-optimization.md +0 -97
  127. package/wiki/get-started/best-practices/security-guidelines.md +0 -99
  128. package/wiki/get-started/core-concepts/persistent.md +0 -539
  129. package/wiki/get-started/index.md +0 -65
  130. package/wiki/get-started/philosophy.md +0 -296
  131. package/wiki/get-started/prerequisites.md +0 -113
@@ -0,0 +1,376 @@
1
+ # Data Modeling
2
+
3
+ Ignis streamlines data modeling with Drizzle ORM by providing powerful helpers and "enrichers" that reduce boilerplate code for common schema patterns.
4
+
5
+ ## 1. Base Entity
6
+
7
+ All entity models should extend `BaseEntity`. This provides integration with the framework's repository layer and automatic schema generation support.
8
+
9
+ 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
+
11
+ **Example (`src/models/entities/user.model.ts`):**
12
+
13
+ ```typescript
14
+ import { BaseEntity, extraUserColumns, generateIdColumnDefs, model } from '@venizia/ignis';
15
+ import { pgTable } from 'drizzle-orm/pg-core';
16
+
17
+ @model({ type: 'entity' })
18
+ export class User extends BaseEntity<typeof User.schema> {
19
+ // 1. Define schema as a static property
20
+ static override schema = pgTable('User', {
21
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
22
+ ...extraUserColumns({ idType: 'string' }),
23
+ });
24
+
25
+ // 2. Define relations as a static method (return empty array if none)
26
+ static override relations = () => [];
27
+ }
28
+ ```
29
+
30
+ ## 2. Schema Enrichers
31
+
32
+ Instead of manually defining common columns like primary keys, timestamps, or audit fields in every table, use Ignis "enrichers".
33
+
34
+ **Available Enrichers:**
35
+
36
+ | Enricher | Description | Columns Added |
37
+ |----------|-------------|---------------|
38
+ | `generateIdColumnDefs` | Adds a Primary Key | `id` (text, number, or big-number) |
39
+ | `generatePrincipalColumnDefs` | Adds polymorphic relation fields | `{discriminator}Id`, `{discriminator}Type` |
40
+ | `generateTzColumnDefs` | Adds timestamps | `createdAt`, `modifiedAt` (auto-updating) |
41
+ | `generateUserAuditColumnDefs` | Adds audit fields | `createdBy`, `modifiedBy` |
42
+ | `generateDataTypeColumnDefs` | Adds generic value fields | `nValue` (number), `tValue` (text), `jValue` (json), etc. |
43
+ | `extraUserColumns` | Comprehensive user fields | Combines audit, timestamps, status, and type fields |
44
+
45
+ **Usage Example:**
46
+
47
+ ```typescript
48
+ import {
49
+ generateIdColumnDefs,
50
+ generateTzColumnDefs,
51
+ generateUserAuditColumnDefs,
52
+ } from '@venizia/ignis';
53
+ import { pgTable, text } from 'drizzle-orm/pg-core';
54
+
55
+ export const configurationTable = pgTable(
56
+ 'Configuration',
57
+ {
58
+ // 1. Auto-generate text Primary Key with UUID default
59
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
60
+
61
+ // 2. Auto-generate createdAt / modifiedAt
62
+ ...generateTzColumnDefs(),
63
+
64
+ // 3. Auto-generate createdBy / modifiedBy
65
+ ...generateUserAuditColumnDefs({
66
+ created: { dataType: 'string', columnName: 'created_by' },
67
+ modified: { dataType: 'string', columnName: 'modified_by' },
68
+ }),
69
+
70
+ // 4. Your custom columns
71
+ code: text('code').notNull(),
72
+ description: text('description'),
73
+ group: text('group').notNull(),
74
+ },
75
+ (table) => [
76
+ // Define indexes/constraints here
77
+ unique('UQ_code').on(table.code),
78
+ ]
79
+ );
80
+ ```
81
+
82
+ ### ID Type Options
83
+
84
+ The `generateIdColumnDefs` enricher supports multiple ID strategies:
85
+
86
+ | Data Type | PostgreSQL Type | JavaScript Type | Use Case |
87
+ |-----------|-----------------|-----------------|----------|
88
+ | `string` | `TEXT` | `string` | UUIDs, custom IDs, distributed systems |
89
+ | `number` | `INTEGER GENERATED ALWAYS AS IDENTITY` | `number` | Auto-increment, simple sequences |
90
+ | `big-number` (mode: `number`) | `BIGINT GENERATED ALWAYS AS IDENTITY` | `number` | Large sequences (up to 2^53) |
91
+ | `big-number` (mode: `bigint`) | `BIGINT GENERATED ALWAYS AS IDENTITY` | `bigint` | Very large sequences (up to 2^64) |
92
+
93
+ **Examples:**
94
+
95
+ ```typescript
96
+ // String ID with default UUID generator
97
+ ...generateIdColumnDefs({ id: { dataType: 'string' } })
98
+ // Result: id TEXT PRIMARY KEY DEFAULT crypto.randomUUID()
99
+
100
+ // String ID with custom generator (e.g., nanoid, ulid)
101
+ import { nanoid } from 'nanoid';
102
+ ...generateIdColumnDefs({ id: { dataType: 'string', generator: () => nanoid() } })
103
+
104
+ // Auto-increment integer
105
+ ...generateIdColumnDefs({ id: { dataType: 'number' } })
106
+ // Result: id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY
107
+
108
+ // Big integer for large datasets (JavaScript number - up to 2^53)
109
+ ...generateIdColumnDefs({ id: { dataType: 'big-number', numberMode: 'number' } })
110
+
111
+ // Big integer with native BigInt (up to 2^64)
112
+ ...generateIdColumnDefs({ id: { dataType: 'big-number', numberMode: 'bigint' } })
113
+
114
+ // With sequence options
115
+ ...generateIdColumnDefs({
116
+ id: {
117
+ dataType: 'number',
118
+ sequenceOptions: { startWith: 1000, increment: 1 },
119
+ },
120
+ })
121
+ ```
122
+
123
+ ### Principal Enricher (Polymorphic Relations)
124
+
125
+ Use `generatePrincipalColumnDefs` when a record can belong to different entity types (polymorphic relationship).
126
+
127
+ **Use Case:** A `Comment` can belong to either a `Post` or a `Product`.
128
+
129
+ ```typescript
130
+ import { generateIdColumnDefs, generatePrincipalColumnDefs } from '@venizia/ignis';
131
+ import { pgTable, text } from 'drizzle-orm/pg-core';
132
+
133
+ export const commentTable = pgTable('Comment', {
134
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
135
+
136
+ // Polymorphic relation: commentable can be Post or Product
137
+ ...generatePrincipalColumnDefs({
138
+ discriminator: 'commentable', // Field prefix
139
+ polymorphicIdType: 'string', // ID type of related entities
140
+ defaultPolymorphic: 'Post', // Default type
141
+ }),
142
+
143
+ content: text('content').notNull(),
144
+ });
145
+
146
+ // Generated columns:
147
+ // - commentableId: TEXT NOT NULL
148
+ // - commentableType: TEXT DEFAULT 'Post'
149
+ ```
150
+
151
+ **Querying polymorphic relations:**
152
+ ```typescript
153
+ // Find all comments on a specific post
154
+ const comments = await commentRepo.find({
155
+ filter: {
156
+ where: {
157
+ commentableType: 'Post',
158
+ commentableId: postId,
159
+ },
160
+ },
161
+ });
162
+
163
+ // Find all comments on a product
164
+ const productComments = await commentRepo.find({
165
+ filter: {
166
+ where: {
167
+ commentableType: 'Product',
168
+ commentableId: productId,
169
+ },
170
+ },
171
+ });
172
+ ```
173
+
174
+ ## 3. Defining Relations
175
+
176
+ Relations are defined using the `TRelationConfig` structure within the static `relations` method of your model.
177
+
178
+ ### Relation Types
179
+
180
+ | Type | Constant | Description | Example |
181
+ |------|----------|-------------|---------|
182
+ | One-to-One | `RelationTypes.ONE` | Single related record | User → Profile |
183
+ | One-to-Many | `RelationTypes.MANY` | Multiple related records | User → Posts |
184
+
185
+ ### Basic Relations
186
+
187
+ **One-to-One (belongsTo):**
188
+ ```typescript
189
+ import { BaseEntity, model, RelationTypes, TRelationConfig } from '@venizia/ignis';
190
+ import { User } from './user.model';
191
+
192
+ @model({ type: 'entity' })
193
+ export class Configuration extends BaseEntity<typeof Configuration.schema> {
194
+ static override schema = pgTable('Configuration', {
195
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
196
+ createdBy: text('created_by'),
197
+ // ...
198
+ });
199
+
200
+ // Define relations
201
+ static override relations = (): TRelationConfig[] => [
202
+ {
203
+ name: 'creator', // Relation name used in include
204
+ type: RelationTypes.ONE, // One Configuration → One User
205
+ schema: User.schema, // Related entity's schema
206
+ metadata: {
207
+ fields: [Configuration.schema.createdBy], // Foreign key
208
+ references: [User.schema.id], // Primary key
209
+ },
210
+ },
211
+ ];
212
+ }
213
+ ```
214
+
215
+ **One-to-Many (hasMany):**
216
+ ```typescript
217
+ @model({ type: 'entity' })
218
+ export class User extends BaseEntity<typeof User.schema> {
219
+ static override schema = pgTable('User', {
220
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
221
+ name: text('name').notNull(),
222
+ });
223
+
224
+ static override relations = (): TRelationConfig[] => [
225
+ {
226
+ name: 'posts', // User.posts
227
+ type: RelationTypes.MANY, // One User → Many Posts
228
+ schema: Post.schema,
229
+ metadata: {
230
+ fields: [User.schema.id],
231
+ references: [Post.schema.authorId],
232
+ },
233
+ },
234
+ {
235
+ name: 'comments', // User.comments
236
+ type: RelationTypes.MANY,
237
+ schema: Comment.schema,
238
+ metadata: {
239
+ fields: [User.schema.id],
240
+ references: [Comment.schema.userId],
241
+ },
242
+ },
243
+ ];
244
+ }
245
+ ```
246
+
247
+ ### Using Relations in Queries
248
+
249
+ ```typescript
250
+ // Eager load single relation
251
+ const configs = await configRepo.find({
252
+ filter: {
253
+ include: [{ relation: 'creator' }],
254
+ },
255
+ });
256
+ // Result: [{ id, code, ..., creator: { id, name, email } }]
257
+
258
+ // Eager load multiple relations
259
+ const users = await userRepo.find({
260
+ filter: {
261
+ include: [
262
+ { relation: 'posts' },
263
+ { relation: 'comments' },
264
+ ],
265
+ },
266
+ });
267
+
268
+ // Nested relations (up to 2 levels recommended)
269
+ const users = await userRepo.find({
270
+ filter: {
271
+ include: [{
272
+ relation: 'posts',
273
+ scope: {
274
+ include: [{ relation: 'comments' }],
275
+ },
276
+ }],
277
+ },
278
+ });
279
+ ```
280
+
281
+ > [!TIP]
282
+ > Avoid deeply nested includes (more than 2 levels). Each level adds query complexity. For complex data fetching, consider separate queries.
283
+
284
+ ## 4. Repositories and Auto-Discovery
285
+
286
+ Ignis simplifies the connection between models, repositories, and datasources.
287
+
288
+ ### DataSource Auto-Discovery
289
+
290
+ DataSources automatically discover their schema from the repositories that bind to them. You **do not** need to manually register schemas in the DataSource constructor.
291
+
292
+ ```typescript
293
+ // src/datasources/postgres.datasource.ts
294
+ @datasource({ driver: 'node-postgres' })
295
+ export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
296
+ constructor() {
297
+ super({
298
+ name: PostgresDataSource.name,
299
+ config: { /* connection config */ },
300
+ // NO schema property needed - auto-discovered!
301
+ });
302
+ }
303
+
304
+ 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 });
308
+ }
309
+ }
310
+ ```
311
+
312
+ ### Repository Binding
313
+
314
+ Repositories use the `@repository` decorator to bind a **Model** to a **DataSource**. This binding is what powers the auto-discovery mechanism.
315
+
316
+ **Pattern 1: Zero Boilerplate (Recommended)**
317
+
318
+ For most repositories, you don't need a constructor. The DataSource is automatically injected.
319
+
320
+ ```typescript
321
+ @repository({ model: Configuration, dataSource: PostgresDataSource })
322
+ export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {
323
+ // No constructor needed!
324
+ }
325
+ ```
326
+
327
+ **Pattern 2: Explicit Injection (Advanced)**
328
+
329
+ If you need to perform custom initialization or inject additional dependencies, you can define a constructor. **Important:** The first parameter must be the DataSource.
330
+
331
+ ```typescript
332
+ @repository({ model: User, dataSource: PostgresDataSource })
333
+ export class UserRepository extends ReadableRepository<typeof User.schema> {
334
+ constructor(
335
+ @inject({ key: 'datasources.PostgresDataSource' })
336
+ dataSource: PostgresDataSource,
337
+ ) {
338
+ super(dataSource);
339
+ }
340
+
341
+ // Custom methods
342
+ async findByRealm(realm: string) {
343
+ return this.findOne({ filter: { where: { realm } } });
344
+ }
345
+ }
346
+ ```
347
+
348
+ ## 5. Hidden Properties
349
+
350
+ Protect sensitive data by configuring properties that are excluded at the SQL level. Hidden properties are **never returned** through repository queries.
351
+
352
+ ```typescript
353
+ @model({
354
+ type: 'entity',
355
+ settings: {
356
+ hiddenProperties: ['password', 'secret'],
357
+ },
358
+ })
359
+ export class User extends BaseEntity<typeof User.schema> {
360
+ static override schema = pgTable('User', {
361
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
362
+ email: text('email').notNull(),
363
+ password: text('password'), // Never returned via repository
364
+ secret: text('secret'), // Never returned via repository
365
+ });
366
+ }
367
+ ```
368
+
369
+ **Key points:**
370
+
371
+ - Hidden properties are excluded from SELECT, INSERT RETURNING, UPDATE RETURNING, DELETE RETURNING
372
+ - You can still **filter by** hidden properties in where clauses
373
+ - Hidden properties are **recursively excluded** from included relations
374
+ - Use the connector directly when you need to access hidden data (e.g., password verification)
375
+
376
+ > **Reference:** See [Hidden Properties](../references/base/models.md#hidden-properties) for complete documentation.