@venizia/ignis-docs 0.2.0 → 0.2.1-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) 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 +24 -13
  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 +27 -3
  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 +8 -4
  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 +247 -153
  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 +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -1,712 +1,152 @@
1
- # Repositories Overview
1
+ ---
2
+ title: Repositories
3
+ description: The typed data-access object for one model - CRUD, filters, and transactions without hand-written SQL
4
+ difficulty: intermediate
5
+ ---
2
6
 
3
- Repositories are the data access layer in IGNIS - they provide type-safe CRUD operations for your database entities.
7
+ # Repositories
4
8
 
5
- > [!IMPORTANT] Base vs. Connectors
6
- > `AbstractRepository` (`packages/core/src/base/repositories/core/abstract.ts`) is the engine-neutral root shared by both connectors - it declares the CRUD contract but has no Drizzle, SQL, or mixin composition. The concrete classes documented on this page (`ReadableRepository`, `PersistableRepository`, `DefaultCRUDRepository`, `SoftDeletableRepository`) belong to the **PostgreSQL connector**, built on `PostgresBaseRepository`. Typesense has its own parallel tier - see [Connectors](/references/base/connectors) and [Search & Typesense](/guides/core-concepts/persistent/search-typesense).
9
+ A repository is the typed data-access object for one model. It turns a `@model` schema into `find`, `create`, `updateById`, `deleteById`, and friends, with the query shape validated at compile time.
7
10
 
8
- **Files:** `packages/core/src/base/repositories/core/abstract.ts` (neutral) and `packages/core/src/connectors/postgres/repositories/core/*.ts` (PostgreSQL)
11
+ ## In one example
9
12
 
10
-
11
- ## Quick Start
12
-
13
- If you're new to repositories, start here:
14
-
15
- ```typescript
16
- import { DefaultCRUDRepository, repository } from '@venizia/ignis';
17
- import { Todo } from '@/models/todo.model';
18
- import { PostgresDataSource } from '@/datasources/postgres.datasource';
19
-
20
- @repository({ model: Todo, dataSource: PostgresDataSource })
21
- export class TodoRepository extends DefaultCRUDRepository<typeof Todo.schema> {
22
- // That's it! You get: find, findOne, create, updateById, deleteById, etc.
23
- }
24
- ```
25
-
26
-
27
- ## Repository Classes
28
-
29
- | Class | Capabilities | Use Case |
30
- |-------|--------------|----------|
31
- | **AbstractRepository** | Engine-neutral base class with lazy dataSource/entity resolution and @model settings getters | Extend for custom repositories |
32
- | **ReadableRepository** | Read-only operations (write methods throw errors) | Views, external tables, read-only access |
33
- | **PersistableRepository** | Read + Write operations | Full CRUD access |
34
- | **DefaultCRUDRepository** | Extends PersistableRepository (no additions) | Standard data tables (recommended) |
35
- | **SoftDeletableRepository** | CRUD + soft delete + restore | Tables with `deletedAt` column |
36
-
37
- **Most common:** Extend `DefaultCRUDRepository` for standard tables, or `SoftDeletableRepository` for soft-delete patterns.
38
-
39
- ### Hierarchy
40
-
41
- ```
42
- BaseHelper
43
- = AbstractRepository (engine-neutral abstract base, declares all CRUD signatures, src/base)
44
- |
45
- +-- PostgresBaseRepository (PostgreSQL connector; hidden-fields + default-filter logic, no mixins)
46
- |
47
- +-- ReadableRepository (implements read ops; write ops throw errors)
48
- |
49
- +-- PersistableRepository (implements write + delete ops, READ_WRITE scope)
50
- |
51
- +-- DefaultCRUDRepository (empty subclass, recommended entry point)
52
- |
53
- +-- SoftDeletableRepository (overrides delete with soft-delete)
54
- ```
55
-
56
- > [!NOTE]
57
- > Prior to the connectors restructure, `AbstractRepository` composed `FieldsVisibilityMixin`/`DefaultFilterMixin` directly onto `BaseHelper`. Those mixins have been **removed** - see [Repository Mixins](./mixins) - and the equivalent behavior lives in `AbstractRepository`'s settings getters and `PostgresBaseRepository` instead.
58
-
59
- ### Type Parameters
60
-
61
- All repository classes share the same four type parameters:
62
-
63
- ```typescript
64
- class DefaultCRUDRepository<
65
- EntitySchema extends TTableSchemaWithId = TTableSchemaWithId,
66
- DataObject extends TTableObject<EntitySchema> = TTableObject<EntitySchema>,
67
- PersistObject extends TTableInsert<EntitySchema> = TTableInsert<EntitySchema>,
68
- ExtraOptions extends IExtraOptions = IDatabaseExtraOptions,
69
- >
70
- ```
71
-
72
- | Parameter | Description |
73
- |-----------|-------------|
74
- | `EntitySchema` | The Drizzle `pgTable` schema type (e.g., `typeof User.schema`) |
75
- | `DataObject` | The inferred SELECT type from the schema |
76
- | `PersistObject` | The inferred INSERT type from the schema |
77
- | `ExtraOptions` | Extra options for operations (defaults to `IDatabaseExtraOptions`, which narrows `IExtraOptions.transaction` to `IDatabaseTransaction`) |
78
-
79
-
80
- ## Available Methods
81
-
82
- ### Read Operations
83
- | Method | Description | Example |
84
- |--------|-------------|---------|
85
- | `find(opts)` | Find multiple records | `repository.find({ filter: { where: { status: 'active' } } })` |
86
- | `find(opts)` with range | Find with pagination range | `repository.find({ filter, options: { shouldQueryRange: true } })` |
87
- | `findOne(opts)` | Find single record | `repository.findOne({ filter: { where: { email } } })` |
88
- | `findById(opts)` | Find by primary key | `repository.findById({ id: '123' })` |
89
- | `count(opts)` | Count matching records | `repository.count({ where: { status: 'active' } })` |
90
- | `existsWith(opts)` | Check if exists | `repository.existsWith({ where: { email } })` |
91
-
92
- ### Write Operations
93
- | Method | Description | Example |
94
- |--------|-------------|---------|
95
- | `create(opts)` | Create single record | `repository.create({ data: { title: 'New' } })` |
96
- | `createAll(opts)` | Create multiple records | `repository.createAll({ data: [{ title: 'A' }, { title: 'B' }] })` |
97
- | `updateById(opts)` | Update by primary key | `repository.updateById({ id: '123', data: { title: 'Updated' } })` |
98
- | `updateAll(opts)` | Update matching records | `repository.updateAll({ data: { status: 'published' }, where: { status: 'draft' } })` |
99
- | `updateBy(opts)` | Alias for `updateAll` | `repository.updateBy({ data: { status: 'published' }, where: { status: 'draft' } })` |
100
- | `deleteById(opts)` | Delete by primary key | `repository.deleteById({ id: '123' })` |
101
- | `deleteAll(opts)` | Delete matching records | `repository.deleteAll({ where: { status: 'archived' } })` |
102
- | `deleteBy(opts)` | Alias for `deleteAll` | `repository.deleteBy({ where: { status: 'archived' } })` |
103
-
104
-
105
- ## Method Signatures
106
-
107
- ### Read Operations
108
-
109
- ```typescript
110
- // Count matching records
111
- count(opts: {
112
- where: TWhere<DataObject>;
113
- options?: IExtraOptions;
114
- }): Promise<{ count: number }>;
115
-
116
- // Check if any record matches
117
- existsWith(opts: {
118
- where: TWhere<DataObject>;
119
- options?: IExtraOptions;
120
- }): Promise<boolean>;
121
-
122
- // Find multiple records (returns array)
123
- find<R = DataObject>(opts: {
124
- filter: TFilter<DataObject>;
125
- options?: IExtraOptions & { shouldQueryRange?: false };
126
- }): Promise<R[]>;
127
-
128
- // Find multiple records with range info (returns data + range)
129
- find<R = DataObject>(opts: {
130
- filter: TFilter<DataObject>;
131
- options: IExtraOptions & { shouldQueryRange: true };
132
- }): Promise<{ data: Array<R>; range: TDataRange }>;
133
-
134
- // Find single record
135
- findOne<R = DataObject>(opts: {
136
- filter: TFilter<DataObject>;
137
- options?: IExtraOptions;
138
- }): Promise<R | null>;
139
-
140
- // Find by primary key
141
- findById<R = DataObject>(opts: {
142
- id: IdType;
143
- filter?: Omit<TFilter<DataObject>, 'where'>;
144
- options?: IExtraOptions;
145
- }): Promise<R | null>;
146
- ```
147
-
148
- ### Write Operations
149
-
150
- ```typescript
151
- // Create single record (returns created data by default)
152
- create<R = DataObject>(opts: {
153
- data: PersistObject;
154
- options?: IExtraOptions & { shouldReturn?: true };
155
- }): Promise<{ count: number; data: R }>;
156
-
157
- // Create single record (skip returning data)
158
- create(opts: {
159
- data: PersistObject;
160
- options: IExtraOptions & { shouldReturn: false };
161
- }): Promise<{ count: number; data: undefined | null }>;
162
-
163
- // Create multiple records (returns created data by default)
164
- createAll<R = DataObject>(opts: {
165
- data: Array<PersistObject>;
166
- options?: IExtraOptions & { shouldReturn?: true };
167
- }): Promise<{ count: number; data: Array<R> }>;
168
-
169
- // Create multiple records (skip returning data)
170
- createAll(opts: {
171
- data: Array<PersistObject>;
172
- options: IExtraOptions & { shouldReturn: false };
173
- }): Promise<{ count: number; data: undefined | null }>;
174
-
175
- // Update by primary key (returns updated data by default)
176
- updateById<R = DataObject>(opts: {
177
- id: IdType;
178
- data: Partial<PersistObject>;
179
- options?: IExtraOptions & { shouldReturn?: true };
180
- }): Promise<{ count: number; data: R }>;
181
-
182
- // Update by primary key (skip returning data)
183
- updateById(opts: {
184
- id: IdType;
185
- data: Partial<PersistObject>;
186
- options: IExtraOptions & { shouldReturn: false };
187
- }): Promise<{ count: number; data: undefined | null }>;
188
-
189
- // Update matching records (returns updated data by default)
190
- updateAll<R = DataObject>(opts: {
191
- data: Partial<PersistObject>;
192
- where: TWhere<DataObject>;
193
- options?: IExtraOptions & { shouldReturn?: true; force?: boolean };
194
- }): Promise<{ count: number; data: Array<R> }>;
195
-
196
- // Update matching records (skip returning data)
197
- updateAll(opts: {
198
- data: Partial<PersistObject>;
199
- where: TWhere<DataObject>;
200
- options: IExtraOptions & { shouldReturn: false; force?: boolean };
201
- }): Promise<{ count: number; data: undefined | null }>;
202
-
203
- // updateBy is an alias for updateAll (same signatures)
204
-
205
- // Delete by primary key (returns deleted data by default)
206
- deleteById<R = DataObject>(opts: {
207
- id: IdType;
208
- options?: IExtraOptions & { shouldReturn?: true };
209
- }): Promise<{ count: number; data: R }>;
210
-
211
- // Delete by primary key (skip returning data)
212
- deleteById(opts: {
213
- id: IdType;
214
- options: IExtraOptions & { shouldReturn: false };
215
- }): Promise<{ count: number; data: undefined | null }>;
216
-
217
- // Delete matching records (returns deleted data by default)
218
- deleteAll<R = DataObject>(opts: {
219
- where: TWhere<DataObject>;
220
- options?: IExtraOptions & { shouldReturn?: true; force?: boolean };
221
- }): Promise<{ count: number; data: Array<R> }>;
222
-
223
- // Delete matching records (skip returning data)
224
- deleteAll(opts: {
225
- where: TWhere<DataObject>;
226
- options: IExtraOptions & { shouldReturn: false; force?: boolean };
227
- }): Promise<{ count: number; data: undefined | null }>;
228
-
229
- // deleteBy is an alias for deleteAll (same signatures)
230
- ```
231
-
232
-
233
- ## IExtraOptions
234
-
235
- All repository operations accept an `options` parameter with these fields:
236
-
237
- ```typescript
238
- interface IExtraOptions {
239
- /** Transaction context - switches the underlying Drizzle connector. */
240
- transaction?: ITransaction;
241
-
242
- /** Operation logging configuration. */
243
- log?: { use: boolean; level?: TLogLevel };
244
-
245
- /** If true, bypass the default filter configured in model settings (e.g., soft delete). */
246
- shouldSkipDefaultFilter?: boolean;
247
-
248
- /** Row-level locking (requires transaction, incompatible with Query API). */
249
- lock?: TLockOptions;
250
- }
251
- ```
252
-
253
- > [!NOTE]
254
- > Postgres narrows this via `IDatabaseExtraOptions` (`connectors/postgres/repositories/common`), which overrides `transaction` to `IDatabaseTransaction` so `options.transaction.connector` needs no cast.
255
-
256
- Additional fields are available as intersections on specific methods:
257
-
258
- | Field | Type | Methods | Description |
259
- |-------|------|---------|-------------|
260
- | `shouldReturn` | `boolean` | `create`, `createAll`, `updateById`, `updateAll`, `deleteById`, `deleteAll` | If `false`, skip returning the data (only return count). Defaults to `true`. |
261
- | `shouldQueryRange` | `boolean` | `find` | If `true`, returns `{ data, range: { start, end, total } }` instead of a plain array. |
262
- | `force` | `boolean` | `updateAll`, `deleteAll`, `updateBy`, `deleteBy` | Required to allow empty `where` conditions. |
263
-
264
-
265
- ## TDataRange
266
-
267
- When `shouldQueryRange: true` is used, the range follows the HTTP Content-Range standard:
268
-
269
- ```typescript
270
- type TDataRange = {
271
- start: number; // Inclusive start index (based on skip/offset)
272
- end: number; // Inclusive end index
273
- total: number; // Total matching records (ignoring limit)
274
- };
275
- ```
276
-
277
-
278
- ## AbstractRepository / PostgresBaseRepository Properties
279
-
280
- ### dataSource
281
-
282
- Getter/setter for the repository's datasource. Throws if accessed before being set (either via constructor or `@repository` auto-injection).
283
-
284
- ```typescript
285
- // AbstractRepository (engine-neutral, src/base)
286
- get dataSource(): AbstractDataSource;
287
- set dataSource(value: AbstractDataSource);
288
- setDataSource(opts: { dataSource: AbstractDataSource }): void;
289
-
290
- // PostgresBaseRepository narrows the return type
291
- get dataSource(): IPostgresDataSource;
292
- set dataSource(value: IPostgresDataSource);
293
- ```
294
-
295
- ### entity
296
-
297
- Lazy-resolved from `@repository` metadata on first access. Can also be set explicitly via constructor `entityClass` option.
298
-
299
- ```typescript
300
- // AbstractRepository
301
- get entity(): AbstractEntity;
302
- set entity(value: AbstractEntity);
303
- getEntity(): AbstractEntity;
304
-
305
- // PostgresBaseRepository narrows the return type
306
- get entity(): BasePostgresEntity<EntitySchema>;
307
- set entity(value: BasePostgresEntity<EntitySchema>);
308
- getEntitySchema(): EntitySchema;
309
- ```
310
-
311
- `BaseEntity` is a compatibility alias re-exported for `BasePostgresEntity` - prefer `BasePostgresEntity` in new code.
312
-
313
- ### operationScope
314
-
315
- Returns the repository's operation scope: `'READ_ONLY'`, `'WRITE_ONLY'`, or `'READ_WRITE'`.
316
-
317
- ```typescript
318
- get operationScope(): TRepositoryOperationScope;
319
- ```
320
-
321
- - `ReadableRepository` defaults to `READ_ONLY`
322
- - `PersistableRepository` and `DefaultCRUDRepository` default to `READ_WRITE`
323
-
324
- ### filterBuilder (PostgresBaseRepository+)
325
-
326
- Access to the `FilterBuilder` instance used for converting filter objects to Drizzle SQL. Not present on the engine-neutral `AbstractRepository`.
327
-
328
- ```typescript
329
- get filterBuilder(): FilterBuilder;
330
- ```
331
-
332
- ### connector (PostgresBaseRepository+)
333
-
334
- Shortcut for `this.dataSource.connector`. Not present on the engine-neutral `AbstractRepository`.
335
-
336
- ```typescript
337
- get connector(): IPostgresDataSource['connector'];
338
- getConnector(): IPostgresDataSource['connector'];
339
- ```
340
-
341
- ### updateBuilder (PersistableRepository+)
342
-
343
- Access to the `UpdateBuilder` instance used for transforming update data (including JSON path updates).
344
-
345
- ```typescript
346
- get updateBuilder(): UpdateBuilder;
347
- ```
348
-
349
-
350
- ## Key Methods
351
-
352
- ### beginTransaction (PostgresBaseRepository+)
353
-
354
- Delegates to the repository's datasource. Not present on the engine-neutral `AbstractRepository` - each connector adds it with its own transaction type (or omits it, like typesense).
13
+ The smallest real repository: bind a model and a datasource, extend `DefaultCRUDRepository`.
355
14
 
356
15
  ```typescript
357
- beginTransaction(opts?: IDatabaseTransactionOptions): Promise<IDatabaseTransaction>;
358
- ```
359
-
360
- Usage:
16
+ import { repository, DefaultCRUDRepository } from '@venizia/ignis';
17
+ import { User } from '../models/user.model';
18
+ import { PostgresDataSource } from '../datasources/postgres.datasource';
361
19
 
362
- ```typescript
363
- const tx = await repository.beginTransaction();
364
- try {
365
- await repository.create({ data: { name: 'John' }, options: { transaction: tx } });
366
- await repository.updateById({ id: '456', data: { count: 1 }, options: { transaction: tx } });
367
- await tx.commit();
368
- } catch (e) {
369
- await tx.rollback();
370
- }
20
+ @repository({ model: User, dataSource: PostgresDataSource })
21
+ export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
371
22
  ```
372
23
 
373
- ### buildQuery
24
+ That's it - `UserRepository` already has `find`, `findOne`, `findById`, `create`, `createAll`, `updateById`, `updateAll`, `deleteById`, `deleteAll`, `count`, and `existsWith`.
374
25
 
375
- Converts a `TFilter` into Drizzle query options, automatically excluding hidden properties from `@model` settings:
26
+ ## How it works
376
27
 
377
- ```typescript
378
- buildQuery(opts: { filter: TFilter<DataObject> }): TDrizzleQueryOptions;
379
- ```
380
-
381
- The returned `TDrizzleQueryOptions` contains:
382
-
383
- ```typescript
384
- type TDrizzleQueryOptions = Partial<{
385
- limit: number;
386
- offset: number;
387
- orderBy: SQL[];
388
- where: SQL;
389
- with: Record<string, true | TDrizzleQueryOptions>;
390
- columns: Record<string, boolean>;
391
- }>;
392
- ```
28
+ - **Engine-neutral contract, PostgreSQL implementation.** `AbstractRepository` (engine-neutral, `src/base`) declares the CRUD contract - no SQL, no Drizzle. The PostgreSQL connector implements it as a chain of classes, each layer adding one capability (see table below).
29
+ - **Datasource is auto-injected.** `@repository({ model, dataSource })` auto-injects the datasource at constructor param[0] and lazily resolves the entity class from its own metadata. A plain `extends DefaultCRUDRepository<...> {}` needs no constructor at all.
30
+ - **One options object per verb.** Reads and updates carry a `filter` (`where`, `fields`, `include`, `order`, `limit`, `offset`). Writes carry `data`. Every verb also accepts an `options` bag for `transaction`, `shouldReturn`, and `shouldSkipDefaultFilter`.
393
31
 
32
+ **PostgreSQL class chain**
394
33
 
395
- ## Dual Query API
34
+ | Class | Alias | Adds |
35
+ |---|---|---|
36
+ | `RelationalBaseRepository` | `PostgresBaseRepository` | `FilterBuilder`/`UpdateBuilder`, hidden-column exclusion |
37
+ | `ReadableRelationalRepository` | `ReadableRepository` | The read verbs |
38
+ | `PersistableRelationalRepository` | `PersistableRepository` | create/update/delete |
39
+ | `DefaultRelationalRepository` | `DefaultCRUDRepository` | Empty - the recommended entry point |
40
+ | `SoftDeletableRelationalRepository` | `SoftDeletableRepository` | Overrides delete to set `deletedAt` instead of removing the row |
396
41
 
397
- `ReadableRepository` automatically selects the optimal query strategy:
42
+ ## Common tasks
398
43
 
399
- - **Core API** (Drizzle `select().from()`): ~15-20% faster for flat queries without relations or field selection
400
- - **Query API** (Drizzle `connector.query[entity].findMany()`): Supports `include` for relations and `fields` for column selection
44
+ ### Read with a filter
401
45
 
402
- The selection is automatic based on filter complexity:
46
+ `find` and `findOne` take `filter.where`, plus `order` and `limit` for paging.
403
47
 
404
48
  ```typescript
405
- // Uses Core API (no include, no fields)
406
- await repository.find({ filter: { where: { status: 'active' }, limit: 10 } });
407
-
408
- // Uses Query API (has include)
409
- await repository.find({
410
- filter: {
411
- where: { status: 'active' },
412
- include: [{ relation: 'posts' }],
413
- },
414
- });
415
-
416
- // Uses Query API (has fields)
417
- await repository.find({
49
+ const users = await userRepository.find({
418
50
  filter: {
419
- fields: { id: true, name: true },
420
51
  where: { status: 'active' },
52
+ order: ['createdAt DESC'],
53
+ limit: 20,
421
54
  },
422
55
  });
423
56
  ```
424
57
 
58
+ See [Filter System](/references/base/filter-system/) for every operator (`gte`, `like`, `inq`, JSON paths, `and`/`or`).
425
59
 
426
- ## Constructor
427
-
428
- ### AbstractRepository
429
-
430
- ```typescript
431
- constructor(
432
- dataSource?: AbstractDataSource,
433
- opts?: {
434
- scope?: string;
435
- entityClass?: TClass<AbstractEntity>;
436
- operationScope?: TRepositoryOperationScope;
437
- },
438
- )
439
- ```
440
-
441
- - `dataSource` -- DataSource instance (optional; auto-injected by `@repository` decorator)
442
- - `opts.scope` -- Logger scope name (defaults to class name)
443
- - `opts.entityClass` -- Entity class to instantiate (optional; lazy-resolved from `@repository` metadata)
444
- - `opts.operationScope` -- Defaults to `READ_ONLY`
445
-
446
- ### ReadableRepository (PostgreSQL)
447
-
448
- ```typescript
449
- constructor(
450
- ds?: IPostgresDataSource,
451
- opts?: { entityClass?: TClass<BasePostgresEntity<EntitySchema>> },
452
- )
453
- ```
454
-
455
- Forces `operationScope` to `READ_ONLY`.
60
+ ### Create a record
456
61
 
457
- ### PersistableRepository (PostgreSQL)
62
+ `create` returns `{ count, data }`, not the bare record - `count` is `1` on success, `data` is the inserted row (or `null` if `options.shouldReturn: false`).
458
63
 
459
64
  ```typescript
460
- constructor(
461
- ds?: IPostgresDataSource,
462
- opts?: { entityClass?: TClass<BasePostgresEntity<EntitySchema>> },
463
- )
65
+ const { count, data } = await userRepository.create({
66
+ data: { email: 'jane@example.com' },
67
+ });
464
68
  ```
465
69
 
466
- Forces `operationScope` to `READ_WRITE`. Also creates an `UpdateBuilder` instance.
467
-
70
+ ### Update by id
468
71
 
469
- ## Documentation Sections
470
-
471
- This documentation is split into focused guides:
472
-
473
- ### [Filter System](/references/base/filter-system/)
474
- Complete reference for querying data - operators, JSON filtering, array operators, default filters, and query patterns.
72
+ `updateById` returns the same `{ count, data }` shape, with `data` set to the updated row.
475
73
 
476
74
  ```typescript
477
- // Preview
478
- await repository.find({
479
- filter: {
480
- where: {
481
- status: 'active',
482
- age: { gte: 18 },
483
- 'metadata.priority': { gte: 3 },
484
- tags: { contains: ['featured'] }
485
- },
486
- order: ['createdAt DESC'],
487
- limit: 20
488
- }
75
+ const { data: updated } = await userRepository.updateById({
76
+ id: '123',
77
+ data: { email: 'new@example.com' },
489
78
  });
490
79
  ```
491
80
 
492
- ### [Relations & Includes](./relations.md)
493
- Fetch related data using `include` for eager loading and nested queries.
81
+ ### Soft delete
494
82
 
495
- ```typescript
496
- // Preview
497
- await repository.find({
498
- filter: {
499
- include: [{
500
- relation: 'posts',
501
- scope: { where: { published: true } }
502
- }]
503
- }
504
- });
505
- ```
506
-
507
- ### [SoftDeletableRepository](./soft-deletable.md)
508
- Soft-delete and restore operations using `deletedAt` timestamps instead of physical deletion.
83
+ Extend `SoftDeletableRepository` instead of `DefaultCRUDRepository` on a model with a `deletedAt` column - `deleteById` sets the timestamp instead of removing the row, and `restoreById` clears it.
509
84
 
510
85
  ```typescript
511
- // Preview
512
86
  @repository({ model: Category, dataSource: PostgresDataSource })
513
87
  export class CategoryRepository extends SoftDeletableRepository<typeof Category.schema> {}
514
88
 
515
- // Soft delete (sets deletedAt)
516
- await repository.deleteById({ id: '123' });
517
- // Restore
518
- await repository.restoreById({ id: '123' });
519
- // Hard delete (physical removal)
520
- await repository.deleteById({ id: '123', options: { shouldHardDelete: true } });
89
+ await categoryRepository.deleteById({ id: '123' }); // sets deletedAt
90
+ await categoryRepository.restoreById({ id: '123' }); // clears deletedAt
521
91
  ```
522
92
 
523
- ### [Advanced Features](./advanced.md)
524
- Transactions, hidden properties, default filter bypass, performance optimization, and type inference.
525
-
526
- ```typescript
527
- // Preview
528
- const tx = await repository.beginTransaction();
529
- try {
530
- await repository.create({ data, options: { transaction: tx } });
531
- await tx.commit();
532
- } catch (e) {
533
- await tx.rollback();
534
- }
535
- ```
93
+ See [SoftDeletableRepository](./soft-deletable) for hard delete and bulk restore.
536
94
 
537
- ### [Repository Mixins (Removed)](./mixins.md)
538
- Tombstone for the removed `DefaultFilterMixin` and `FieldsVisibilityMixin` - where the equivalent behavior lives now.
539
-
540
-
541
- ## @repository Decorator
542
-
543
- **Both `model` AND `dataSource` are required** for schema auto-discovery:
544
-
545
- ```typescript
546
- @repository({ model: Model, dataSource: DataSourceClass })
547
- ```
95
+ ### Include relations
548
96
 
549
- The decorator accepts `IRepositoryMetadata`:
97
+ Pass `include` in the filter to eager-load related rows, with an optional nested `scope` filter.
550
98
 
551
99
  ```typescript
552
- interface IRepositoryMetadata<Schema, Model, DataSource> {
553
- model: TValueOrResolver<TClass<Model>>;
554
- dataSource: string | TValueOrResolver<TClass<DataSource>>;
555
- operationScope?: TRepositoryOperationScope; // 'READ_ONLY' | 'WRITE_ONLY' | 'READ_WRITE'
556
- }
100
+ await userRepository.find({
101
+ filter: {
102
+ include: [{ relation: 'posts', scope: { where: { published: true } } }],
103
+ },
104
+ });
557
105
  ```
558
106
 
559
- ```typescript
560
- // WRONG - Missing dataSource
561
- @repository({ model: User })
562
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
563
-
564
- // WRONG - Missing model
565
- @repository({ dataSource: PostgresDataSource })
566
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
567
-
568
- // CORRECT
569
- @repository({ model: User, dataSource: PostgresDataSource })
570
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
571
- ```
107
+ See [Relations & Includes](./relations) for one-to-many, many-to-many, and nested includes.
572
108
 
573
- ### Zero Boilerplate Pattern (Recommended)
109
+ ### Run inside a transaction
574
110
 
575
- DataSource is auto-injected - no constructor needed:
111
+ `beginTransaction()` delegates to the datasource; pass the handle as `options.transaction` on any repository call to run it inside that transaction.
576
112
 
577
113
  ```typescript
578
- @repository({ model: User, dataSource: PostgresDataSource })
579
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
580
- // Custom methods only - no boilerplate!
581
-
582
- async findByEmail(opts: { email: string }) {
583
- return this.findOne({ filter: { where: { email: opts.email } } });
584
- }
585
- }
586
- ```
114
+ const transaction = await userRepository.beginTransaction();
587
115
 
588
- ### Explicit @inject Pattern
589
-
590
- When you need constructor control:
591
-
592
- ```typescript
593
- @repository({ model: User, dataSource: PostgresDataSource })
594
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
595
- constructor(
596
- @inject({ key: 'datasources.PostgresDataSource' })
597
- dataSource: PostgresDataSource,
598
- ) {
599
- super(dataSource);
600
- }
116
+ try {
117
+ await userRepository.create({ data: { email: 'a@example.com' }, options: { transaction } });
118
+ await transaction.commit();
119
+ } catch (error) {
120
+ await transaction.rollback();
121
+ throw error;
601
122
  }
602
123
  ```
603
124
 
604
- ### Lazy Resolution
125
+ See [DataSources](/references/base/datasources) for the rollback-safe pattern (`rollback()` itself can throw). See [Advanced Features](./advanced) for isolation levels and other transaction options.
605
126
 
606
- The `@repository` decorator enables two lazy resolution mechanisms:
127
+ ### Retry a read behind a replicated pool
607
128
 
608
- 1. **Entity resolution**: The `entity` getter auto-resolves the model class from `@repository` metadata on first access, so you never need to pass `entityClass` manually.
609
- 2. **DataSource resolution**: The DataSource is auto-injected at constructor param[0] unless an explicit `@inject` is present.
610
-
611
-
612
- ## Safety Features
613
-
614
- ### Empty Where Protection
615
-
616
- Prevents accidental mass updates/deletes (in `PersistableRepository` and above):
129
+ A read right after a write can hit a replica that has not caught up. Pass `retry` to re-read until the result is fresh:
617
130
 
618
131
  ```typescript
619
- // Throws error - empty where without force flag
620
- await repository.deleteAll({ where: {} });
621
- await repository.updateAll({ data: { status: 'archived' }, where: {} });
622
-
623
- // Explicitly allow with force flag (logs warning)
624
- await repository.deleteAll({ where: {}, options: { force: true } });
625
- await repository.updateAll({ data: { status: 'archived' }, where: {}, options: { force: true } });
132
+ const user = await userRepository.findById({
133
+ id,
134
+ options: { retry: { maxAttempts: 4 } },
135
+ });
626
136
  ```
627
137
 
628
- | Scenario | `force: false` (default) | `force: true` |
629
- |----------|-------------------------|---------------|
630
- | Empty `where` | Throws error | Logs warning, proceeds |
631
- | Valid `where` | Executes normally | Executes normally |
138
+ Full options and rules: [Advanced Features - Read Retry](./advanced#read-retry-replica-lag).
632
139
 
633
- ### ReadableRepository Write Protection
140
+ ## See also
634
141
 
635
- All write methods (`create`, `createAll`, `updateById`, `updateAll`, `deleteById`, `deleteAll`) throw errors on `ReadableRepository`, enforcing the read-only scope at runtime. The thrown error carries `messageCode: 'core.repository.operation_not_allowed'` (`RepositoryErrorCodes.OPERATION_NOT_ALLOWED`).
636
-
637
-
638
- ## TFilter Reference
639
-
640
- ```typescript
641
- type TFilter<T = any> = {
642
- where?: TWhere<T>;
643
- fields?: Partial<{ [K in keyof T]: boolean }> | Array<keyof T>;
644
- include?: Array<{
645
- relation: string;
646
- scope?: TFilter;
647
- shouldSkipDefaultFilter?: boolean;
648
- }>;
649
- order?: string[]; // e.g., ['createdAt DESC', 'name ASC']
650
- limit?: number; // Defaults to 10
651
- offset?: number;
652
- skip?: number; // Alias for offset
653
- };
654
- ```
142
+ - [Relations & Includes](./relations) - eager loading, nested `scope` filters, many-to-many
143
+ - [Advanced Features](./advanced) - transactions, hidden properties, `shouldQueryRange`, performance, read retry
144
+ - [SoftDeletableRepository](./soft-deletable) - soft delete, restore, hard delete
145
+ - [Repository Mixins (Removed)](./mixins) - where `FieldsVisibilityMixin`/`DefaultFilterMixin` behavior lives now
146
+ - [Filter System](/references/base/filter-system/) - every `where` operator, ordering, pagination
147
+ - [Repositories Guide](/guides/core-concepts/persistent/repositories) - creating repositories step by step
655
148
 
149
+ **Files:**
656
150
 
657
- ## Quick Reference
658
-
659
- | Want to... | Code |
660
- |------------|------|
661
- | Find all active | `repository.find({ filter: { where: { status: 'active' } } })` |
662
- | Find with range info | `repository.find({ filter, options: { shouldQueryRange: true } })` |
663
- | Find by ID | `repository.findById({ id: '123' })` |
664
- | Find with relations | `repository.find({ filter: { include: [{ relation: 'posts' }] } })` |
665
- | Create one | `repository.create({ data: { name: 'John' } })` |
666
- | Create without returning data | `repository.create({ data: { name: 'John' }, options: { shouldReturn: false } })` |
667
- | Create many | `repository.createAll({ data: [{ name: 'A' }, { name: 'B' }] })` |
668
- | Update by ID | `repository.updateById({ id: '123', data: { name: 'Jane' } })` |
669
- | Update by condition | `repository.updateAll({ data: { status: 'published' }, where: { status: 'draft' } })` |
670
- | Delete by ID | `repository.deleteById({ id: '123' })` |
671
- | Delete by condition | `repository.deleteBy({ where: { status: 'archived' } })` |
672
- | Soft delete | `repository.deleteById({ id: '123' })` (with `SoftDeletableRepository`) |
673
- | Restore soft-deleted | `repository.restoreById({ id: '123' })` (with `SoftDeletableRepository`) |
674
- | Hard delete (bypass soft) | `repository.deleteById({ id: '123', options: { shouldHardDelete: true } })` |
675
- | Count matching | `repository.count({ where: { status: 'active' } })` |
676
- | Check exists | `repository.existsWith({ where: { email: 'test@example.com' } })` |
677
- | Skip default filter | `repository.find({ filter, options: { shouldSkipDefaultFilter: true } })` |
678
- | Use transaction | `repository.create({ data, options: { transaction: tx } })` |
679
-
680
-
681
- ## Next Steps
682
-
683
- - **New to filtering?** Start with [Filter System](/references/base/filter-system/)
684
- - **Need related data?** See [Relations & Includes](./relations.md)
685
- - **Need soft delete?** See [SoftDeletableRepository](./soft-deletable.md)
686
- - **Need transactions?** Go to [Advanced Features](./advanced.md)
687
-
688
- ## See Also
689
-
690
- - **Related Concepts:**
691
- - [Repositories Guide](/guides/core-concepts/persistent/repositories) - Creating repositories tutorial
692
- - [Models](/guides/core-concepts/persistent/models) - Entity definitions used by repositories
693
- - [DataSources](/guides/core-concepts/persistent/datasources) - Database connections
694
- - [Services](/guides/core-concepts/services) - Use repositories for data access
695
- - [Transactions](/guides/core-concepts/persistent/transactions) - Multi-operation consistency
696
-
697
- - **Repository Topics:**
698
- - [Relations & Includes](./relations) - Loading related data
699
- - [Advanced Features](./advanced) - JSON updates, transactions, performance tuning
700
- - [Repository Mixins (Removed)](./mixins) - Where mixin behavior lives now
701
-
702
- - **Filtering:**
703
- - [Filter System Overview](/references/base/filter-system/) - Complete filtering guide
704
- - [Filter Quick Reference](/references/base/filter-system/quick-reference) - All operators cheat sheet
705
-
706
- - **Best Practices:**
707
- - [Data Modeling](/best-practices/data-modeling) - Repository design patterns
708
- - [Performance Optimization](/best-practices/performance-optimization) - Query optimization
709
-
710
- - **Tutorials:**
711
- - [Building a CRUD API](/guides/tutorials/building-a-crud-api) - Repository examples
712
- - [E-commerce API](/guides/tutorials/ecommerce-api) - Advanced queries and relations
151
+ - [`packages/core-server/src/base/repositories/core/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/repositories/core/abstract.ts) - neutral `AbstractRepository`
152
+ - [`packages/core-server/src/connectors/postgres/repositories/core/index.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/core/index.ts) - PostgreSQL hierarchy + compatibility aliases