@venizia/ignis-docs 0.0.7 → 0.0.8-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 (172) hide show
  1. package/dist/mcp-server/common/paths.d.ts +4 -2
  2. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  3. package/dist/mcp-server/common/paths.js +8 -6
  4. package/dist/mcp-server/common/paths.js.map +1 -1
  5. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  6. package/dist/mcp-server/helpers/docs.helper.js +1 -1
  7. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  8. package/dist/mcp-server/tools/base.tool.d.ts +1 -1
  9. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
  10. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
  11. package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
  12. package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
  13. package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
  14. package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
  15. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  16. package/dist/mcp-server/tools/docs/search-documents.tool.js +1 -1
  17. package/dist/mcp-server/tools/docs/search-documents.tool.js.map +1 -1
  18. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  19. package/dist/mcp-server/tools/github/list-project-files.tool.js +1 -1
  20. package/dist/mcp-server/tools/github/list-project-files.tool.js.map +1 -1
  21. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  22. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  23. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  24. package/package.json +9 -9
  25. package/wiki/best-practices/api-usage-examples.md +9 -9
  26. package/wiki/best-practices/architectural-patterns.md +19 -3
  27. package/wiki/best-practices/architecture-decisions.md +6 -6
  28. package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
  29. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  30. package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
  31. package/wiki/best-practices/code-style-standards/index.md +2 -2
  32. package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
  33. package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
  34. package/wiki/best-practices/data-modeling.md +1 -1
  35. package/wiki/best-practices/deployment-strategies.md +1 -1
  36. package/wiki/best-practices/error-handling.md +2 -2
  37. package/wiki/best-practices/performance-optimization.md +3 -3
  38. package/wiki/best-practices/security-guidelines.md +2 -2
  39. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  40. package/wiki/{references → extensions}/components/authentication/api.md +12 -20
  41. package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
  42. package/wiki/{references → extensions}/components/authentication/index.md +5 -8
  43. package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
  44. package/wiki/{references → extensions}/components/authorization/api.md +62 -13
  45. package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
  46. package/wiki/{references → extensions}/components/authorization/index.md +93 -6
  47. package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
  48. package/wiki/{references → extensions}/components/health-check.md +5 -4
  49. package/wiki/{references → extensions}/components/index.md +2 -0
  50. package/wiki/{references → extensions}/components/mail/index.md +1 -1
  51. package/wiki/{references → extensions}/components/request-tracker.md +1 -1
  52. package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
  53. package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
  54. package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
  55. package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
  56. package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
  57. package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
  58. package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
  59. package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
  60. package/wiki/{references → extensions}/components/swagger.md +3 -3
  61. package/wiki/{references → extensions}/components/template/index.md +4 -4
  62. package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
  63. package/wiki/{references → extensions}/components/template/single-page.md +1 -1
  64. package/wiki/{references → extensions}/components/websocket/api.md +7 -6
  65. package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
  66. package/wiki/{references → extensions}/components/websocket/index.md +17 -11
  67. package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
  68. package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
  69. package/wiki/{references → extensions}/helpers/env/index.md +9 -5
  70. package/wiki/{references → extensions}/helpers/error/index.md +2 -7
  71. package/wiki/{references → extensions}/helpers/index.md +18 -6
  72. package/wiki/{references → extensions}/helpers/kafka/admin.md +13 -1
  73. package/wiki/{references → extensions}/helpers/kafka/consumer.md +32 -31
  74. package/wiki/{references → extensions}/helpers/kafka/examples.md +20 -20
  75. package/wiki/{references → extensions}/helpers/kafka/index.md +61 -54
  76. package/wiki/{references → extensions}/helpers/kafka/producer.md +21 -20
  77. package/wiki/{references → extensions}/helpers/kafka/schema-registry.md +25 -25
  78. package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
  79. package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
  80. package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
  81. package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
  82. package/wiki/{references → extensions}/helpers/template/index.md +1 -1
  83. package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
  84. package/wiki/{references → extensions}/helpers/types/index.md +63 -16
  85. package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
  86. package/wiki/extensions/index.md +48 -0
  87. package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
  88. package/wiki/guides/core-concepts/application/index.md +95 -35
  89. package/wiki/guides/core-concepts/components-guide.md +23 -19
  90. package/wiki/guides/core-concepts/components.md +34 -10
  91. package/wiki/guides/core-concepts/dependency-injection.md +99 -34
  92. package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
  93. package/wiki/guides/core-concepts/persistent/datasources.md +37 -19
  94. package/wiki/guides/core-concepts/persistent/index.md +6 -6
  95. package/wiki/guides/core-concepts/persistent/models.md +50 -6
  96. package/wiki/guides/core-concepts/persistent/repositories.md +83 -8
  97. package/wiki/guides/core-concepts/persistent/transactions.md +39 -8
  98. package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +32 -35
  99. package/wiki/guides/core-concepts/services.md +19 -6
  100. package/wiki/guides/get-started/5-minute-quickstart.md +17 -17
  101. package/wiki/guides/get-started/philosophy.md +1 -1
  102. package/wiki/guides/index.md +2 -2
  103. package/wiki/guides/reference/glossary.md +7 -7
  104. package/wiki/guides/reference/mcp-docs-server.md +1 -1
  105. package/wiki/guides/tutorials/building-a-crud-api.md +45 -39
  106. package/wiki/guides/tutorials/complete-installation.md +74 -51
  107. package/wiki/guides/tutorials/ecommerce-api.md +39 -30
  108. package/wiki/guides/tutorials/realtime-chat.md +12 -13
  109. package/wiki/guides/tutorials/testing.md +2 -2
  110. package/wiki/index.md +4 -3
  111. package/wiki/references/base/application.md +341 -21
  112. package/wiki/references/base/bootstrapping.md +43 -13
  113. package/wiki/references/base/components.md +259 -8
  114. package/wiki/references/base/controllers.md +556 -253
  115. package/wiki/references/base/datasources.md +159 -79
  116. package/wiki/references/base/dependency-injection.md +299 -48
  117. package/wiki/references/base/filter-system/application-usage.md +18 -2
  118. package/wiki/references/base/filter-system/array-operators.md +14 -6
  119. package/wiki/references/base/filter-system/comparison-operators.md +9 -3
  120. package/wiki/references/base/filter-system/default-filter.md +28 -3
  121. package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
  122. package/wiki/references/base/filter-system/index.md +169 -11
  123. package/wiki/references/base/filter-system/json-filtering.md +51 -18
  124. package/wiki/references/base/filter-system/list-operators.md +4 -3
  125. package/wiki/references/base/filter-system/logical-operators.md +7 -2
  126. package/wiki/references/base/filter-system/null-operators.md +50 -0
  127. package/wiki/references/base/filter-system/quick-reference.md +82 -243
  128. package/wiki/references/base/filter-system/range-operators.md +7 -1
  129. package/wiki/references/base/filter-system/tips.md +34 -7
  130. package/wiki/references/base/filter-system/use-cases.md +6 -5
  131. package/wiki/references/base/grpc-controllers.md +984 -0
  132. package/wiki/references/base/index.md +32 -24
  133. package/wiki/references/base/middleware.md +347 -0
  134. package/wiki/references/base/models.md +390 -46
  135. package/wiki/references/base/providers.md +14 -14
  136. package/wiki/references/base/repositories/advanced.md +195 -69
  137. package/wiki/references/base/repositories/index.md +447 -12
  138. package/wiki/references/base/repositories/mixins.md +103 -98
  139. package/wiki/references/base/repositories/relations.md +129 -45
  140. package/wiki/references/base/repositories/soft-deletable.md +104 -23
  141. package/wiki/references/base/services.md +94 -14
  142. package/wiki/references/index.md +12 -10
  143. package/wiki/references/quick-reference.md +98 -65
  144. package/wiki/references/utilities/crypto.md +21 -4
  145. package/wiki/references/utilities/date.md +25 -7
  146. package/wiki/references/utilities/index.md +26 -24
  147. package/wiki/references/utilities/jsx.md +54 -54
  148. package/wiki/references/utilities/module.md +8 -6
  149. package/wiki/references/utilities/parse.md +16 -9
  150. package/wiki/references/utilities/performance.md +22 -7
  151. package/wiki/references/utilities/promise.md +19 -16
  152. package/wiki/references/utilities/request.md +48 -26
  153. package/wiki/references/utilities/schema.md +69 -6
  154. package/wiki/references/utilities/statuses.md +131 -140
  155. /package/wiki/{references → extensions}/components/mail/api.md +0 -0
  156. /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
  157. /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
  158. /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
  159. /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
  160. /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
  161. /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
  162. /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
  163. /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
  164. /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
  165. /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
  166. /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
  167. /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
  168. /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
  169. /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
  170. /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
  171. /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
  172. /package/wiki/{references → extensions}/src-details/mcp-server.md +0 -0
@@ -2,7 +2,7 @@
2
2
  title: Providers Reference
3
3
  description: Technical reference for the Provider pattern in IGNIS
4
4
  difficulty: advanced
5
- lastUpdated: 2026-01-03
5
+ lastUpdated: 2026-03-15
6
6
  ---
7
7
 
8
8
  # Providers Reference
@@ -461,38 +461,38 @@ export class MailQueueExecutorProvider extends BaseProvider<TGetMailQueueExecuto
461
461
 
462
462
  ### Example 3: Middleware Provider
463
463
 
464
- Providers can also produce middleware:
464
+ Providers can also produce middleware. `RequestSpyMiddleware` is a real-world example that implements `IProvider<MiddlewareHandler>` directly (extending `BaseHelper`, not `BaseProvider`):
465
465
 
466
466
  ```typescript
467
- import { RequestSpyMiddleware } from '@venizia/ignis';
468
-
469
- // RequestSpyMiddleware is a provider that produces Hono middleware
470
- @injectable()
467
+ // From packages/core/src/base/middlewares/request-spy.middleware.ts
471
468
  export class RequestSpyMiddleware extends BaseHelper implements IProvider<MiddlewareHandler> {
472
469
  static readonly REQUEST_ID_KEY = 'requestId';
473
470
 
474
471
  constructor() {
475
- super({ scope: RequestSpyMiddleware.name });
472
+ super({ scope: 'SpyMW' });
476
473
  }
477
474
 
475
+ /** Returns a Hono middleware that logs request details and duration. */
478
476
  value() {
479
477
  return createMiddleware(async (context, next) => {
478
+ const t = performance.now();
480
479
  const requestId = context.get(RequestSpyMiddleware.REQUEST_ID_KEY);
480
+ const method = context.req.method;
481
+ const path = context.req.path ?? '/';
481
482
 
482
- this.logger.info('[spy][%s] START | path: %s', requestId, context.req.path);
483
+ this.logger.info('[%s][=>] %s %s', requestId, method, path);
483
484
 
484
485
  await next();
485
486
 
486
- this.logger.info('[spy][%s] DONE | path: %s', requestId, context.req.path);
487
+ const duration = (performance.now() - t).toFixed(2);
488
+ this.logger.info('[%s][<=] %s %s | Took: %s (ms)', requestId, method, path, duration);
487
489
  });
488
490
  }
489
491
  }
490
-
491
- // Usage
492
- const requestSpy = new RequestSpyMiddleware();
493
- app.use(requestSpy.value());
494
492
  ```
495
493
 
494
+ Note that `RequestSpyMiddleware.value()` does not accept a `container` parameter -- the `IProvider<T>` interface defines `value(container: Container): T`, but implementations may ignore the parameter when they don't need container access. In practice, `RequestSpyMiddleware` is registered via `RequestTrackerComponent`, which binds it as a provider in the DI container and resolves it automatically.
495
+
496
496
 
497
497
  ## Common Patterns
498
498
 
@@ -708,7 +708,7 @@ export class ConfigProvider extends BaseProvider<Config> {
708
708
  - **Related References:**
709
709
  - [Services](./services.md) - Business logic layer
710
710
  - [Dependency Injection](./dependency-injection.md) - DI container and injection
711
- - [Middlewares](./middlewares.md) - Middleware providers
711
+ - [Middleware](./middleware.md) - Built-in middlewares (includes `RequestSpyMiddleware` provider)
712
712
 
713
713
  - **Guides:**
714
714
  - [Dependency Injection Guide](/guides/core-concepts/dependency-injection.md)
@@ -101,6 +101,114 @@ async function transferFunds(fromId: string, toId: string, amount: number) {
101
101
  ```
102
102
 
103
103
 
104
+ ## Row-Level Locking
105
+
106
+ Acquire pessimistic locks on selected rows within a transaction using PostgreSQL's `SELECT ... FOR UPDATE/SHARE` syntax.
107
+
108
+ ### Basic Usage
109
+
110
+ Pass `lock` in options alongside a `transaction`:
111
+
112
+ ```typescript
113
+ const tx = await repo.beginTransaction();
114
+
115
+ try {
116
+ // Lock the row — other transactions will wait
117
+ const item = await repo.findOne({
118
+ filter: { where: { id: '123' } },
119
+ options: {
120
+ transaction: tx,
121
+ lock: { strength: 'update' },
122
+ },
123
+ });
124
+
125
+ // Safe to modify — no concurrent changes possible
126
+ await repo.updateById({
127
+ id: '123',
128
+ data: { quantity: item.quantity - 1 },
129
+ options: { transaction: tx },
130
+ });
131
+
132
+ await tx.commit();
133
+ } catch (error) {
134
+ await tx.rollback();
135
+ throw error;
136
+ }
137
+ ```
138
+
139
+ ### Lock Strengths
140
+
141
+ Use the `LockStrengths` constant class or string literals:
142
+
143
+ ```typescript
144
+ import { LockStrengths } from '@venizia/ignis';
145
+
146
+ // Using constant
147
+ lock: { strength: LockStrengths.UPDATE }
148
+
149
+ // Using string literal
150
+ lock: { strength: 'update' }
151
+ ```
152
+
153
+ | Strength | SQL | Use Case |
154
+ |----------|-----|----------|
155
+ | `update` | `FOR UPDATE` | Exclusive lock for writes |
156
+ | `no key update` | `FOR NO KEY UPDATE` | Exclusive lock, allows concurrent `FOR KEY SHARE` |
157
+ | `share` | `FOR SHARE` | Shared read lock, prevents writes |
158
+ | `key share` | `FOR KEY SHARE` | Weakest lock, only prevents key changes |
159
+
160
+ ### Wait Behavior
161
+
162
+ Control what happens when rows are already locked:
163
+
164
+ ```typescript
165
+ // Skip locked rows (queue-style worker pattern)
166
+ const items = await repo.find({
167
+ filter: { where: { status: 'pending' }, limit: 10 },
168
+ options: {
169
+ transaction: tx,
170
+ lock: { strength: 'update', config: { skipLocked: true } },
171
+ },
172
+ });
173
+
174
+ // Fail immediately instead of waiting
175
+ const item = await repo.findOne({
176
+ filter: { where: { id: '123' } },
177
+ options: {
178
+ transaction: tx,
179
+ lock: { strength: 'update', config: { noWait: true } },
180
+ },
181
+ });
182
+ ```
183
+
184
+ | Config | SQL | Behavior |
185
+ |--------|-----|----------|
186
+ | *(none)* | `FOR UPDATE` | Wait until lock is released |
187
+ | `{ noWait: true }` | `FOR UPDATE NOWAIT` | Throw error immediately if locked |
188
+ | `{ skipLocked: true }` | `FOR UPDATE SKIP LOCKED` | Silently skip locked rows |
189
+
190
+ ### Constraints
191
+
192
+ > [!WARNING]
193
+ > Row-level locking requires a **transaction** and is **incompatible with `include`/`fields`** in the filter (these use the Drizzle Query API which does not support `.for()`).
194
+
195
+ ```typescript
196
+ // Error — no transaction
197
+ await repo.findOne({
198
+ filter: { where: { id: '123' } },
199
+ options: { lock: { strength: 'update' } },
200
+ });
201
+
202
+ // Error — include uses Query API
203
+ await repo.findOne({
204
+ filter: { where: { id: '123' }, include: [{ relation: 'posts' }] },
205
+ options: { transaction: tx, lock: { strength: 'update' } },
206
+ });
207
+ ```
208
+
209
+ **Supported methods:** `find`, `findOne`, `findById`
210
+
211
+
104
212
  ## Hidden Properties
105
213
 
106
214
  Automatically exclude sensitive fields from query results.
@@ -207,7 +315,7 @@ const usersWithPosts = await repo.find({
207
315
 
208
316
  | Filter Options | API Used | Performance |
209
317
  |----------------|----------|-------------|
210
- | `where`, `limit`, `order`, `offset` only | Core API | ~15-20% faster |
318
+ | `where`, `limit`, `order`, `offset`/`skip` only | Core API | ~15-20% faster |
211
319
  | Has `include` (relations) | Query API | Standard |
212
320
  | Has `fields` selection | Query API | Standard |
213
321
 
@@ -216,7 +324,7 @@ const usersWithPosts = await repo.find({
216
324
  Prevent memory exhaustion on large tables:
217
325
 
218
326
  ```typescript
219
- // Good - bounded result set
327
+ // Good - bounded result set
220
328
  await repo.find({
221
329
  filter: {
222
330
  where: { status: 'active' },
@@ -230,42 +338,39 @@ await repo.find({
230
338
  });
231
339
  ```
232
340
 
233
- ### Pagination Pattern
341
+ > [!NOTE]
342
+ > The default limit is `10` when using the `FilterSchema` Zod validation (via `LimitSchema`). However, when calling repository methods directly without schema validation, no default limit is applied.
343
+
344
+ ### Pagination with Data Range
345
+
346
+ Use `shouldQueryRange` to get both data and total count in a single call:
234
347
 
235
348
  ```typescript
236
- async function getPaginatedUsers(page: number, pageSize: number = 20) {
237
- const [users, total] = await Promise.all([
238
- userRepo.find({
239
- filter: {
240
- where: { status: 'active' },
241
- limit: pageSize,
242
- skip: (page - 1) * pageSize,
243
- order: ['createdAt DESC']
244
- }
245
- }),
246
- userRepo.count({ where: { status: 'active' } })
247
- ]);
248
-
249
- return {
250
- data: users,
251
- pagination: {
252
- page,
253
- pageSize,
254
- total,
255
- totalPages: Math.ceil(total / pageSize)
256
- }
257
- };
258
- }
349
+ const result = await userRepo.find({
350
+ filter: {
351
+ where: { status: 'active' },
352
+ limit: 20,
353
+ skip: 40,
354
+ order: ['createdAt DESC']
355
+ },
356
+ options: { shouldQueryRange: true }
357
+ });
358
+
359
+ // Result type: { data: User[], range: { start: number, end: number, total: number } }
360
+ // range follows HTTP Content-Range standard (inclusive end index)
361
+ // Example: { data: [...20 users], range: { start: 40, end: 59, total: 150 } }
259
362
  ```
260
363
 
364
+ This runs `find` and `count` in parallel via `Promise.all` for optimal performance.
365
+
261
366
  ### WeakMap Cache
262
367
 
263
368
  The filter builder caches table column metadata, avoiding repeated reflection:
264
369
 
265
370
  ```typescript
266
371
  // Internal optimization - automatic
267
- // First query: getTableColumns(schema) cached
268
- // Subsequent queries: retrieved from WeakMap
372
+ // First query: getTableColumns(schema) -> cached in WeakMap
373
+ // Subsequent queries: retrieved from WeakMap cache
269
374
  ```
270
375
 
271
376
 
@@ -281,8 +386,7 @@ const result1 = await repo.create({
281
386
  data: { name: 'John' },
282
387
  options: { shouldReturn: false }
283
388
  });
284
- // Type: Promise<{ count: number; data: null }>
285
- console.log(result1.data); // null
389
+ // Type: Promise<{ count: number; data: undefined | null }>
286
390
 
287
391
  // shouldReturn: true (default) - TypeScript knows data is the entity
288
392
  const result2 = await repo.create({
@@ -327,7 +431,7 @@ if (user) {
327
431
  **Supported Methods:**
328
432
  - `find<R>()`, `findOne<R>()`, `findById<R>()`
329
433
  - `create<R>()`, `createAll<R>()`
330
- - `updateById<R>()`, `updateAll<R>()`
434
+ - `updateById<R>()`, `updateAll<R>()`, `updateBy<R>()`
331
435
  - `deleteById<R>()`, `deleteAll<R>()`, `deleteBy<R>()`
332
436
 
333
437
 
@@ -355,7 +459,7 @@ await repo.updateById({
355
459
  });
356
460
  ```
357
461
 
358
- **Available on:** `create`, `createAll`, `updateById`, `updateAll`, `deleteById`, `deleteAll`, `deleteBy`
462
+ **Available on:** `create`, `createAll`, `updateById`, `updateAll`, `updateBy`, `deleteById`, `deleteAll`, `deleteBy` (all write operations that go through `_create`, `_update`, or `_delete` internal methods)
359
463
 
360
464
  ### Query Interface Validation
361
465
 
@@ -377,10 +481,10 @@ The repository validates schema registration on startup:
377
481
  Prevents accidental mass updates/deletes:
378
482
 
379
483
  ```typescript
380
- // Throws error - empty where without force
484
+ // Throws error - empty where without force
381
485
  await repo.deleteAll({ where: {} });
382
486
 
383
- // Explicit force flag - logs warning, proceeds
487
+ // Explicit force flag - logs warning, proceeds
384
488
  await repo.deleteAll({
385
489
  where: {},
386
490
  options: { force: true }
@@ -393,29 +497,16 @@ await repo.deleteAll({
393
497
  | Empty `where` | Throws error | Logs warning, proceeds |
394
498
  | Valid `where` | Executes normally | Executes normally |
395
499
 
396
- ### Constructor Type Validation
500
+ > [!NOTE]
501
+ > This protection applies to `updateAll`, `updateBy`, `deleteAll`, and `deleteBy`. The `updateById` and `deleteById` methods always have a non-empty where (`{ id }`) so they are not affected.
397
502
 
398
- The `@repository` decorator validates constructor parameters:
503
+ ### Transaction Safety
399
504
 
400
- ```typescript
401
- // ❌ Error: First parameter must extend AbstractDataSource
402
- @repository({ model: User, dataSource: PostgresDataSource })
403
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
404
- constructor(dataSource: any) { // 'any' not allowed!
405
- super(dataSource);
406
- }
407
- }
505
+ The `resolveConnector` method validates transaction state before use:
408
506
 
409
- // ✅ Correct: Concrete DataSource type
410
- @repository({ model: User, dataSource: PostgresDataSource })
411
- export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
412
- constructor(
413
- @inject({ key: 'datasources.PostgresDataSource' })
414
- dataSource: PostgresDataSource,
415
- ) {
416
- super(dataSource);
417
- }
418
- }
507
+ ```typescript
508
+ // If a transaction has already been committed or rolled back:
509
+ // Error: [UserRepository][resolveConnector] Transaction is no longer active
419
510
  ```
420
511
 
421
512
 
@@ -444,12 +535,13 @@ const results = await connector
444
535
 
445
536
  ## Repository Class Hierarchy
446
537
 
447
- | Class | Description |
448
- |-------|-------------|
449
- | `AbstractRepository` | Base class, defines method signatures |
450
- | `ReadableRepository` | Read-only operations (find, findOne, count) |
451
- | `PersistableRepository` | Adds write operations (create, update, delete) |
452
- | `DefaultCRUDRepository` | Full CRUD - **use this one** |
538
+ | Class | Scope | Description |
539
+ |-------|-------|-------------|
540
+ | `AbstractRepository` | N/A | Abstract base class, defines all method signatures, combines `FieldsVisibilityMixin` + `DefaultFilterMixin` |
541
+ | `ReadableRepository` | `READ_ONLY` | Read-only operations (`find`, `findOne`, `findById`, `count`, `existsWith`). Write operations throw errors. |
542
+ | `PersistableRepository` | `READ_WRITE` | Adds write operations (`create`, `update`, `delete`) with `UpdateBuilder` |
543
+ | `DefaultCRUDRepository` | `READ_WRITE` | Extends `PersistableRepository` with no additional logic - **recommended default** |
544
+ | `SoftDeletableRepository` | `READ_WRITE` | Extends `DefaultCRUDRepository` with soft delete + restore operations |
453
545
 
454
546
  ### Creating a Read-Only Repository
455
547
 
@@ -461,6 +553,15 @@ export class AuditLogRepository extends ReadableRepository<typeof AuditLog.schem
461
553
  }
462
554
  ```
463
555
 
556
+ ### Alias Methods
557
+
558
+ `AbstractRepository` provides two alias methods for convenience:
559
+
560
+ - `updateBy(opts)` - Alias for `updateAll(opts)`. Delegates directly.
561
+ - `deleteBy(opts)` - Alias for `deleteAll(opts)`. Delegates directly.
562
+
563
+ Both accept the same parameters (`where`, `data`/`options`) and support `shouldReturn` and `force` options.
564
+
464
565
 
465
566
  ## Default Filter Bypass
466
567
 
@@ -515,7 +616,7 @@ await tx.commit();
515
616
 
516
617
  ## Nested JSON Updates
517
618
 
518
- Repositories support updating specific fields within `json` or `jsonb` columns without overwriting the entire object. This is achieved using **JSON Path Notation** in the update data.
619
+ Repositories support updating specific fields within `json` or `jsonb` columns without overwriting the entire object. This is achieved using **JSON Path Notation** in the update data via the `UpdateBuilder`.
519
620
 
520
621
  ### Basic Usage
521
622
 
@@ -540,9 +641,10 @@ await repo.updateById({
540
641
 
541
642
  - **Deep Nesting:** Update properties at any depth (e.g., `settings.display.font.size`).
542
643
  - **Array Access:** Update array elements by index (e.g., `tags[0]`).
543
- - **Auto-Creation:** Creates missing intermediate keys automatically.
544
- - **Type Safety:** Validates that the target column is a JSON type.
545
- - **Multiple Updates:** Chain multiple updates to the same or different columns.
644
+ - **Auto-Creation:** Creates missing intermediate keys automatically (`jsonb_set` with `create_missing = true`).
645
+ - **Type Safety:** Validates that the target column is a JSON/JSONB type.
646
+ - **Multiple Updates:** Multiple updates to the same column are chained as nested `jsonb_set` calls.
647
+ - **Mixed Updates:** Combine regular column updates with JSON path updates in a single call.
546
648
 
547
649
  ### Examples
548
650
 
@@ -580,7 +682,7 @@ await repo.updateById({
580
682
  data: {
581
683
  status: 'active', // Regular column
582
684
  'metadata.lastLogin': now, // JSON path
583
- 'preferences.lang': 'en' // Another JSON path
685
+ 'preferences.lang': 'en' // Another JSON path
584
686
  }
585
687
  });
586
688
  ```
@@ -588,14 +690,34 @@ await repo.updateById({
588
690
  ### Security & Validation
589
691
 
590
692
  The framework validates JSON paths to prevent SQL injection:
591
- - **Allowed Characters:** Alphanumeric, underscores `_`, hyphens `-`, and brackets `[]`.
592
- - **Validation:** Invalid paths (e.g., containing SQL commands or special characters) throw an error before reaching the database.
593
- - **Values:** Values are safely serialized and parameterized.
693
+ - **Allowed Characters:** Path components must match `/^[a-zA-Z_][a-zA-Z0-9_-]*$|^\d+$/` (identifiers, kebab-case, or array indices).
694
+ - **Column Type Validation:** Only `json` and `jsonb` columns are allowed. Other column types throw an error.
695
+ - **Values:** Values are serialized to JSONB literals with proper escaping.
594
696
 
595
697
  > [!NOTE]
596
698
  > This feature uses PostgreSQL's `jsonb_set` function. It is only available for columns defined as `json` or `jsonb`.
597
699
 
598
700
 
701
+ ## ExtraOptions Reference
702
+
703
+ All repository operations accept an `options` parameter with these fields:
704
+
705
+ | Option | Type | Default | Description |
706
+ |--------|------|---------|-------------|
707
+ | `transaction` | `ITransaction` | - | Transaction context for the operation |
708
+ | `log` | `{ use: boolean; level?: TLogLevel }` | - | Enable operation logging |
709
+ | `shouldSkipDefaultFilter` | `boolean` | `false` | Bypass the default filter from model settings |
710
+ | `lock` | `TLockOptions` | - | Row-level locking (requires transaction, Core API only) |
711
+
712
+ Write operations additionally support:
713
+
714
+ | Option | Type | Default | Description |
715
+ |--------|------|---------|-------------|
716
+ | `shouldReturn` | `boolean` | `true` | Return the created/updated/deleted data |
717
+ | `force` | `boolean` | `false` | Allow empty `where` condition on bulk operations |
718
+ | `shouldQueryRange` | `boolean` | `false` | Return `{ data, range }` with total count (find only) |
719
+
720
+
599
721
  ## Quick Reference
600
722
 
601
723
  | Feature | Code |
@@ -605,9 +727,12 @@ The framework validates JSON paths to prevent SQL injection:
605
727
  | Commit | `await tx.commit()` |
606
728
  | Rollback | `await tx.rollback()` |
607
729
  | Bypass default filter | `options: { shouldSkipDefaultFilter: true }` |
730
+ | Lock rows for update | `options: { transaction: tx, lock: { strength: 'update' } }` |
731
+ | Lock + skip locked | `options: { transaction: tx, lock: { strength: 'update', config: { skipLocked: true } } }` |
608
732
  | Enable logging | `options: { log: { use: true, level: 'debug' } }` |
609
733
  | Force delete all | `options: { force: true }` |
610
734
  | Skip returning data | `options: { shouldReturn: false }` |
735
+ | Get data + count | `options: { shouldQueryRange: true }` |
611
736
  | Access connector | `repo.getConnector()` |
612
737
 
613
738
 
@@ -618,6 +743,7 @@ The framework validates JSON paths to prevent SQL injection:
618
743
  - [Default Filter](../filter-system/default-filter.md) - Automatic filter configuration
619
744
  - [Repository Mixins](./mixins.md) - Composable features
620
745
  - [Relations & Includes](./relations.md) - Eager loading
746
+ - [Soft-Deletable Repository](./soft-deletable.md) - Soft delete operations
621
747
  - [JSON Path Filtering](../filter-system/json-filtering) - JSONB queries
622
748
  - [Array Operators](../filter-system/array-operators) - PostgreSQL arrays
623
749
 
@@ -629,7 +755,7 @@ The framework validates JSON paths to prevent SQL injection:
629
755
  - [DataSources](/guides/core-concepts/persistent/datasources) - Database connections
630
756
 
631
757
  - **Related Topics:**
632
- - [Repository Mixins](./mixins) - Soft delete and auditing
758
+ - [Repository Mixins](./mixins) - Composable mixin features
633
759
  - [Relations & Includes](./relations) - Loading related data
634
760
  - [Filter System](/references/base/filter-system/) - Query operators
635
761