@venizia/ignis-docs 0.2.1-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 (145) hide show
  1. package/content/best-practices/architectural-patterns.md +3 -3
  2. package/content/best-practices/code-style-standards/naming-conventions.md +1 -1
  3. package/content/best-practices/contribution-workflow.md +2 -2
  4. package/content/best-practices/error-handling.md +94 -89
  5. package/content/best-practices/security-guidelines.md +5 -5
  6. package/content/extensions/components/api-reference.md +22 -21
  7. package/content/extensions/components/authentication/api.md +64 -28
  8. package/content/extensions/components/authentication/errors.md +19 -5
  9. package/content/extensions/components/authentication/index.md +19 -18
  10. package/content/extensions/components/authentication/usage.md +55 -30
  11. package/content/extensions/components/authorization/api.md +351 -84
  12. package/content/extensions/components/authorization/errors.md +51 -17
  13. package/content/extensions/components/authorization/getting-started.md +227 -0
  14. package/content/extensions/components/authorization/index.md +24 -16
  15. package/content/extensions/components/authorization/usage.md +45 -21
  16. package/content/extensions/components/health-check.md +15 -9
  17. package/content/extensions/components/index.md +24 -90
  18. package/content/extensions/components/mail/api.md +105 -54
  19. package/content/extensions/components/mail/errors.md +12 -10
  20. package/content/extensions/components/mail/index.md +32 -13
  21. package/content/extensions/components/mail/usage.md +26 -18
  22. package/content/extensions/components/request-tracker.md +18 -14
  23. package/content/extensions/components/socket-io/api.md +377 -882
  24. package/content/extensions/components/socket-io/errors.md +49 -51
  25. package/content/extensions/components/socket-io/index.md +72 -88
  26. package/content/extensions/components/socket-io/usage.md +107 -117
  27. package/content/extensions/components/static-asset/api.md +83 -31
  28. package/content/extensions/components/static-asset/errors.md +18 -7
  29. package/content/extensions/components/static-asset/index.md +18 -11
  30. package/content/extensions/components/static-asset/usage.md +11 -6
  31. package/content/extensions/components/template/index.md +3 -3
  32. package/content/extensions/components/websocket/api.md +58 -27
  33. package/content/extensions/components/websocket/errors.md +3 -3
  34. package/content/extensions/components/websocket/index.md +8 -7
  35. package/content/extensions/components/websocket/usage.md +21 -8
  36. package/content/extensions/helpers/cron/index.md +8 -7
  37. package/content/extensions/helpers/crypto/index.md +16 -8
  38. package/content/extensions/helpers/crypto/reference.md +96 -24
  39. package/content/extensions/helpers/env/index.md +14 -10
  40. package/content/extensions/helpers/error/index.md +99 -23
  41. package/content/extensions/helpers/index.md +61 -47
  42. package/content/extensions/helpers/inversion/index.md +23 -6
  43. package/content/extensions/helpers/inversion/reference.md +30 -22
  44. package/content/extensions/helpers/kafka/admin.md +3 -2
  45. package/content/extensions/helpers/kafka/compile-binary.md +69 -44
  46. package/content/extensions/helpers/kafka/consumer.md +24 -21
  47. package/content/extensions/helpers/kafka/examples.md +22 -234
  48. package/content/extensions/helpers/kafka/index.md +30 -62
  49. package/content/extensions/helpers/kafka/producer.md +31 -26
  50. package/content/extensions/helpers/kafka/schema-registry.md +19 -14
  51. package/content/extensions/helpers/logger/hf-logger.md +49 -22
  52. package/content/extensions/helpers/logger/index.md +37 -12
  53. package/content/extensions/helpers/logger/pino.md +31 -11
  54. package/content/extensions/helpers/logger/reference.md +243 -52
  55. package/content/extensions/helpers/network/api.md +65 -30
  56. package/content/extensions/helpers/network/index.md +32 -11
  57. package/content/extensions/helpers/queue/index.md +17 -6
  58. package/content/extensions/helpers/queue/reference.md +52 -25
  59. package/content/extensions/helpers/redis/index.md +29 -11
  60. package/content/extensions/helpers/redis/reference.md +85 -28
  61. package/content/extensions/helpers/secrets/index.md +82 -12
  62. package/content/extensions/helpers/socket-io/api.md +40 -21
  63. package/content/extensions/helpers/socket-io/index.md +26 -10
  64. package/content/extensions/helpers/storage/api.md +42 -24
  65. package/content/extensions/helpers/storage/index.md +10 -7
  66. package/content/extensions/helpers/types/index.md +20 -7
  67. package/content/extensions/helpers/types/reference.md +53 -14
  68. package/content/extensions/helpers/uid/index.md +166 -10
  69. package/content/extensions/helpers/websocket/api.md +52 -13
  70. package/content/extensions/helpers/websocket/index.md +7 -4
  71. package/content/extensions/helpers/worker-thread/index.md +9 -5
  72. package/content/extensions/helpers/worker-thread/reference.md +20 -20
  73. package/content/extensions/index.md +38 -39
  74. package/content/extensions/src-details/mcp-server.md +96 -548
  75. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  76. package/content/guides/core-concepts/persistent/index.md +5 -1
  77. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  78. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  79. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  80. package/content/guides/core-concepts/persistent/search-typesense.md +26 -14
  81. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  82. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  83. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  84. package/content/guides/get-started/philosophy.md +135 -670
  85. package/content/guides/get-started/setup.md +53 -74
  86. package/content/guides/migrations/redis-helpers-migration.md +4 -3
  87. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  88. package/content/guides/migrations/unified-connectors-migration.md +7 -6
  89. package/content/guides/tutorials/realtime-chat.md +1 -1
  90. package/content/references/base/application.md +63 -21
  91. package/content/references/base/components.md +3 -3
  92. package/content/references/base/connectors.md +14 -14
  93. package/content/references/base/controllers.md +12 -12
  94. package/content/references/base/datasources-reference.md +32 -31
  95. package/content/references/base/datasources.md +6 -6
  96. package/content/references/base/dependency-injection.md +12 -11
  97. package/content/references/base/filter-system/application-usage.md +54 -30
  98. package/content/references/base/filter-system/array-operators.md +24 -46
  99. package/content/references/base/filter-system/comparison-operators.md +47 -67
  100. package/content/references/base/filter-system/default-filter.md +59 -53
  101. package/content/references/base/filter-system/fields-order-pagination.md +92 -146
  102. package/content/references/base/filter-system/index.md +25 -13
  103. package/content/references/base/filter-system/json-filtering.md +45 -184
  104. package/content/references/base/filter-system/list-operators.md +23 -53
  105. package/content/references/base/filter-system/logical-operators.md +63 -121
  106. package/content/references/base/filter-system/null-operators.md +34 -104
  107. package/content/references/base/filter-system/pattern-matching.md +40 -55
  108. package/content/references/base/filter-system/quick-reference.md +86 -198
  109. package/content/references/base/filter-system/range-operators.md +18 -46
  110. package/content/references/base/filter-system/tips.md +6 -6
  111. package/content/references/base/filter-system/use-cases.md +33 -15
  112. package/content/references/base/grpc-controllers.md +53 -17
  113. package/content/references/base/index.md +5 -3
  114. package/content/references/base/middlewares.md +11 -10
  115. package/content/references/base/models-reference.md +17 -17
  116. package/content/references/base/models.md +4 -3
  117. package/content/references/base/providers.md +8 -8
  118. package/content/references/base/repositories/advanced.md +224 -326
  119. package/content/references/base/repositories/index.md +19 -6
  120. package/content/references/base/repositories/mixins.md +5 -5
  121. package/content/references/base/repositories/relations.md +160 -293
  122. package/content/references/base/repositories/soft-deletable.md +16 -6
  123. package/content/references/base/secrets.md +17 -13
  124. package/content/references/base/services.md +6 -4
  125. package/content/references/configuration/environment-variables.md +31 -23
  126. package/content/references/configuration/index.md +6 -4
  127. package/content/references/index.md +1 -1
  128. package/content/references/utilities/duration.md +85 -0
  129. package/content/references/utilities/index.md +5 -1
  130. package/content/references/utilities/jsx-reference.md +11 -11
  131. package/content/references/utilities/jsx.md +2 -2
  132. package/content/references/utilities/module.md +78 -25
  133. package/content/references/utilities/request.md +2 -1
  134. package/content/references/utilities/retry.md +139 -0
  135. package/content/references/utilities/schema.md +2 -2
  136. package/content/references/utilities/statuses-reference.md +4 -4
  137. package/content/references/utilities/statuses.md +5 -5
  138. package/dist/mcp-server/index.js +0 -0
  139. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  140. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  141. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  142. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  143. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  144. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  145. package/package.json +17 -16
@@ -6,145 +6,115 @@ difficulty: intermediate
6
6
 
7
7
  # Fields, Ordering & Pagination
8
8
 
9
- Control which fields are returned, how results are sorted, and how to paginate.
10
-
11
-
12
- ## Field Selection
13
-
14
- Control which fields are returned using `fields`:
15
-
16
- ### Array Format (Recommended)
9
+ The `filter` object controls which columns come back, in what order, and how many rows - through the `fields`, `order`, `limit`, and `skip`/`offset` properties.
17
10
 
18
11
  ```typescript
19
12
  import { userRepository } from '@/repositories';
20
13
 
21
14
  await userRepository.find({
22
- filter: {
23
- where: { status: 'active' },
24
- fields: ['id', 'email', 'name']
25
- }
15
+ filter: { fields: ['id', 'email'], order: ['createdAt DESC'], limit: 10 },
26
16
  });
27
- // Returns only: { id, email, name }
28
17
  ```
29
18
 
30
- ### Object Format
19
+ ## Options
20
+
21
+ | Option | Type | Default | Meaning |
22
+ |---|---|---|---|
23
+ | `fields` | `string[] \| Record<string, boolean>` | every column | Inclusion-only column selection. |
24
+ | `order` | `string[]` (`'column ASC\|DESC'`) | insertion order | Sort columns; `ASC` if no direction is given. |
25
+ | `limit` | `number` | `settings.defaultLimit ?? 10` | Row cap. An explicit value always wins. |
26
+ | `skip` / `offset` | `number` | `0` | Rows to skip. Aliases for the same `OFFSET` clause; `skip` wins if both are set. |
27
+ | `options.shouldQueryRange` | `boolean` | `false` | Adds a `range` envelope (`start`/`end`/`total`) to the result. |
28
+
29
+ ## Field selection
30
+
31
+ `fields` accepts an array or an object. Both select the same columns:
31
32
 
32
33
  ```typescript
33
- // Include specific fields (only keys with `true` are selected)
34
+ // Array format (recommended)
34
35
  await userRepository.find({
35
- filter: {
36
- fields: { id: true, email: true, name: true }
37
- }
36
+ filter: { where: { status: 'active' }, fields: ['id', 'email', 'name'] },
37
+ });
38
+ // Returns only: { id, email, name }
39
+
40
+ // Object format - only keys set to `true` are selected
41
+ await userRepository.find({
42
+ filter: { fields: { id: true, email: true, name: true } },
38
43
  });
39
44
  ```
40
45
 
41
46
  > [!NOTE]
42
- > The object format only supports inclusion (`true` values). Keys set to `false` are simply ignored -- they do not exclude fields. To select specific fields, list the ones you want with `true` or use the array format.
43
-
47
+ > The object format is inclusion-only. A key set to `false` is ignored, not excluded - it neither adds nor removes the column. To exclude a column, omit its key or use the array format.
44
48
 
45
49
  ## Ordering
46
50
 
47
- ### Basic Ordering
51
+ Each entry in `order` is a `'column DIRECTION'` string. Direction defaults to `ASC` and only `ASC`/`DESC` (case-insensitive) are valid:
48
52
 
49
53
  ```typescript
50
- // Single column, descending
51
- await userRepository.find({
52
- filter: { order: ['createdAt DESC'] }
53
- });
54
-
55
- // Multiple columns
56
- await userRepository.find({
57
- filter: { order: ['status ASC', 'createdAt DESC'] }
58
- });
59
-
60
- // Default direction is ASC
61
- await userRepository.find({
62
- filter: { order: ['name'] } // Same as 'name ASC'
63
- });
54
+ await userRepository.find({ filter: { order: ['createdAt DESC'] } });
55
+ await userRepository.find({ filter: { order: ['status ASC', 'createdAt DESC'] } });
56
+ await userRepository.find({ filter: { order: ['name'] } }); // same as 'name ASC'
64
57
  ```
65
58
 
66
- ### Valid Directions
67
-
68
- Only `ASC` and `DESC` (case-insensitive) are accepted. Invalid directions throw an error:
59
+ An invalid direction throws before the query runs:
69
60
 
70
61
  ```
71
62
  Error: Invalid direction: 'RANDOM' | Expected: 'ASC' or 'DESC'
72
63
  ```
73
64
 
74
- ### JSON Path Ordering
75
-
76
- Order by nested fields in JSON columns:
65
+ Order by a nested key inside a JSON column with dot-path notation:
77
66
 
78
67
  ```typescript
79
- await userRepository.find({
80
- filter: { order: ['metadata.priority DESC'] }
81
- });
68
+ await userRepository.find({ filter: { order: ['metadata.priority DESC'] } });
82
69
  // SQL: ORDER BY "metadata" #> '{priority}' DESC
83
70
 
84
- await userRepository.find({
85
- filter: { order: ['settings.display.theme ASC'] }
86
- });
71
+ await userRepository.find({ filter: { order: ['settings.display.theme ASC'] } });
87
72
  ```
88
73
 
89
- ### JSONB Sort Order
74
+ JSONB values sort by type first, then by value within the type:
90
75
 
91
- | JSONB Type | Sort Order |
92
- |------------|------------|
76
+ | JSONB type | Sort position |
77
+ |---|---|
93
78
  | `null` | First (lowest) |
94
- | `boolean` | `false` < `true` |
79
+ | `boolean` | `false` before `true` |
95
80
  | `number` | Numeric order |
96
- | `string` | Lexicographic |
81
+ | `string` | Lexicographic order |
97
82
  | `array` | Element-wise |
98
- | `object` | Key-value |
83
+ | `object` | Key-value order |
99
84
 
85
+ See [JSON Filtering](./json-filtering) for the full path syntax.
100
86
 
101
87
  ## Pagination
102
88
 
103
- ### Limit and Skip/Offset
104
-
105
- Both `skip` and `offset` are supported as aliases -- they both map to the SQL `OFFSET` clause. When both are provided, `skip` takes precedence.
89
+ `limit` caps the row count; `skip` (or its alias `offset`) sets how many rows to skip. Combine them for page N:
106
90
 
107
91
  ```typescript
108
- // First 10 results (default limit is 10)
109
- await userRepository.find({
110
- filter: { limit: 10 }
111
- });
112
-
113
- // Page 2 (skip first 10, get next 10)
114
- await userRepository.find({
115
- filter: { limit: 10, skip: 10 }
116
- });
92
+ await userRepository.find({ filter: { limit: 10 } }); // first 10
93
+ await userRepository.find({ filter: { limit: 10, skip: 10 } }); // page 2
117
94
 
118
- // Using offset (equivalent to skip)
119
- await userRepository.find({
120
- filter: { limit: 10, offset: 10 }
121
- });
122
-
123
- // Page N formula: skip = (page - 1) * limit
124
95
  const page = 3;
125
96
  const pageSize = 20;
126
97
  await userRepository.find({
127
- filter: {
128
- limit: pageSize,
129
- skip: (page - 1) * pageSize
130
- }
98
+ filter: { limit: pageSize, skip: (page - 1) * pageSize },
131
99
  });
132
100
  ```
133
101
 
134
102
  > [!TIP]
135
- > Always use `limit` for public-facing endpoints to prevent memory exhaustion. The default limit is 10 if not specified.
103
+ > Set `limit` on every public-facing endpoint. An unbounded query can exhaust memory - the repository always falls back to a default of `10`, never to "no limit".
136
104
 
137
- ### Default Limit
105
+ ### Default limit resolution
138
106
 
139
- When a query omits `limit`, the repository resolves one with this precedence:
107
+ A query that omits `limit` gets one from this precedence chain:
140
108
 
141
109
  ```
142
- query.limit ?? model settings.defaultLimit ?? DEFAULT_LIMIT (10)
110
+ query.limit ?? settings.defaultLimit ?? DEFAULT_LIMIT (10)
143
111
  ```
144
112
 
145
- - **`query.limit`** - an explicit `limit` in the filter always wins.
146
- - **`settings.defaultLimit`** - a per-model default set on the `@model` decorator. Must be a positive integer (validated at decoration time). Applies to top-level `find()` and to every to-many relation (using the related model's own `defaultLimit`).
147
- - **`DEFAULT_LIMIT`** - the global fallback, `10`.
113
+ | Source | Meaning |
114
+ |---|---|
115
+ | `query.limit` | An explicit `limit` in the caller's filter. Always wins. |
116
+ | `settings.defaultLimit` | A per-model default on the `@model` decorator. Must be a positive integer - `@model` validates it at decoration time. Applies to top-level `find()` and to every to-many relation, using the related model's own `defaultLimit`. |
117
+ | `DEFAULT_LIMIT` | The global fallback, `10`. |
148
118
 
149
119
  ```typescript
150
120
  import { model, BaseEntity } from '@venizia/ignis';
@@ -153,105 +123,80 @@ import { countryRepository } from '@/repositories';
153
123
 
154
124
  @model({
155
125
  type: 'entity',
156
- settings: { defaultLimit: 200 }, // Small lookup table - default to 200 rows
126
+ settings: { defaultLimit: 200 }, // small lookup table - default to 200 rows
157
127
  })
158
128
  export class Country extends BaseEntity<typeof Country.schema> {
159
129
  static override schema = countryTable;
160
130
  }
161
131
 
162
- await countryRepository.find({ filter: {} }); // LIMIT 200
163
- await countryRepository.find({ filter: { limit: 10 } }); // LIMIT 10 (explicit wins)
132
+ await countryRepository.find({ filter: {} }); // LIMIT 200
133
+ await countryRepository.find({ filter: { limit: 10 } }); // LIMIT 10 (explicit wins)
164
134
  ```
165
135
 
166
136
  > [!NOTE]
167
- > `defaultLimit` is independent of `defaultFilter`: passing `shouldSkipDefaultFilter` to bypass the default `where` clause does **not** drop the default limit. There is no "unbounded" sentinel - to fetch more rows, pass an explicit `limit`.
137
+ > `defaultLimit` is independent of `defaultFilter`. Passing `shouldSkipDefaultFilter` bypasses the default `where` clause but never drops the default limit. There is no "unbounded" sentinel - to fetch more rows, pass an explicit `limit`.
168
138
 
169
- ### Pagination Helper
139
+ A small helper keeps page-to-filter math in one place:
170
140
 
171
141
  ```typescript
172
142
  function getPaginationFilter(page: number, pageSize: number = 20) {
173
- return {
174
- limit: pageSize,
175
- skip: (page - 1) * pageSize
176
- };
143
+ return { limit: pageSize, skip: (page - 1) * pageSize };
177
144
  }
178
145
 
179
- // Usage
180
- const filter = {
181
- where: { status: 'active' },
182
- ...getPaginationFilter(3, 20)
183
- };
146
+ const filter = { where: { status: 'active' }, ...getPaginationFilter(3, 20) };
184
147
  // { where: {...}, limit: 20, skip: 40 }
185
148
  ```
186
149
 
150
+ ## Range queries (Content-Range header)
187
151
 
188
- ## Range Queries (Content-Range Header)
189
-
190
- When building paginated APIs, you often need to return the total count alongside the data for pagination UI. Use `shouldQueryRange: true` to get range information following the HTTP Content-Range standard.
191
-
192
- ### Basic Usage
152
+ Set `options.shouldQueryRange: true` to get the total row count alongside the data, formatted for the HTTP `Content-Range` header:
193
153
 
194
154
  ```typescript
195
155
  const result = await userRepository.find({
196
156
  filter: { limit: 10, skip: 20 },
197
- options: { shouldQueryRange: true }
157
+ options: { shouldQueryRange: true },
198
158
  });
199
159
 
200
- // Result structure:
201
- // {
202
- // data: [...], // Array of records
203
- // range: {
204
- // start: 20, // Starting index (inclusive)
205
- // end: 29, // Ending index (inclusive)
206
- // total: 100 // Total matching records
207
- // }
208
- // }
160
+ // result.data -> the matching rows
161
+ // result.range -> { start: 20, end: 29, total: 100 }
209
162
  ```
210
163
 
211
- ### Setting HTTP Headers
164
+ `range` has this shape:
212
165
 
213
- Use the range information to set standard HTTP headers:
166
+ ```typescript
167
+ type TDataRange = {
168
+ start: number; // starting index, 0-based, inclusive
169
+ end: number; // ending index, 0-based, inclusive
170
+ total: number; // total rows matching the query
171
+ };
172
+ ```
173
+
174
+ Build the header value from `range`:
214
175
 
215
176
  ```typescript
216
177
  const { data, range } = await userRepository.find({
217
178
  filter: { limit: 10, skip: 20, where: { status: 'active' } },
218
- options: { shouldQueryRange: true }
179
+ options: { shouldQueryRange: true },
219
180
  });
220
181
 
221
- // Format: "records start-end/total"
222
- const contentRange = data.length > 0
223
- ? `records ${range.start}-${range.end}/${range.total}`
224
- : `records */${range.total}`;
182
+ const contentRange =
183
+ data.length > 0 ? `records ${range.start}-${range.end}/${range.total}` : `records */${range.total}`;
225
184
 
226
185
  res.setHeader('Content-Range', contentRange);
227
186
  // -> "records 20-29/100"
228
187
  ```
229
188
 
230
- ### TDataRange Type
231
-
232
- ```typescript
233
- type TDataRange = {
234
- start: number; // Starting index (0-based, inclusive)
235
- end: number; // Ending index (0-based, inclusive)
236
- total: number; // Total count matching the query
237
- };
238
- ```
239
-
240
- ### Content-Range Format Reference
241
-
242
- | Scenario | Content-Range Header |
243
- |----------|---------------------|
189
+ | Scenario | Content-Range header |
190
+ |---|---|
244
191
  | Items 0-9 of 100 | `records 0-9/100` |
245
192
  | Items 20-29 of 100 | `records 20-29/100` |
246
193
  | No items found | `records */0` |
247
194
  | Last page (items 90-99) | `records 90-99/100` |
248
195
 
249
- ### Performance Note
250
-
251
- When `shouldQueryRange: true`, the repository executes the data query and count query **in parallel** using `Promise.all` for optimal performance.
252
-
196
+ > [!NOTE]
197
+ > With `shouldQueryRange: true`, the repository runs the data query and the count query in parallel via `Promise.all`.
253
198
 
254
- ## Combined Example
199
+ ## Combined example
255
200
 
256
201
  ```typescript
257
202
  await userRepository.find({
@@ -260,12 +205,12 @@ await userRepository.find({
260
205
  fields: ['id', 'name', 'price', 'createdAt'],
261
206
  order: ['price ASC', 'createdAt DESC'],
262
207
  limit: 20,
263
- skip: 0
264
- }
208
+ skip: 0,
209
+ },
265
210
  });
266
211
  ```
267
212
 
268
- ### With Range Information
213
+ With range information:
269
214
 
270
215
  ```typescript
271
216
  const { data, range } = await userRepository.find({
@@ -274,9 +219,9 @@ const { data, range } = await userRepository.find({
274
219
  fields: ['id', 'name', 'price', 'createdAt'],
275
220
  order: ['price ASC', 'createdAt DESC'],
276
221
  limit: 20,
277
- skip: 0
222
+ skip: 0,
278
223
  },
279
- options: { shouldQueryRange: true }
224
+ options: { shouldQueryRange: true },
280
225
  });
281
226
 
282
227
  console.log(`Showing ${range.start}-${range.end} of ${range.total}`);
@@ -292,7 +237,8 @@ console.log(`Showing ${range.start}-${range.end} of ${range.total}`);
292
237
 
293
238
  **Files:**
294
239
 
295
- - [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, `toColumns`/`toOrderBy`
296
- - [`packages/core/src/base/repositories/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/operators.ts) - `Sorts` constants
297
- - [`packages/core/src/base/repositories/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/constants.ts) - `DEFAULT_LIMIT`
298
- - [`packages/core/src/base/repositories/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/types.ts) - `TDataRange`, `buildDataRange`
240
+ - [`packages/core-server/src/connectors/relational/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/relational/repositories/dialect/filter.ts) - `FilterBuilder`, `toColumns`/`toOrderBy`
241
+ - [`packages/core-server/src/connectors/postgres/repositories/core/readable.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/core/readable.ts) - `find()`'s `query.limit ?? getDefaultLimit() ?? DEFAULT_LIMIT` resolution
242
+ - [`packages/filter/src/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/filter/src/common/operators.ts) - `Sorts` constants
243
+ - [`packages/core-server/src/base/repositories/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/repositories/common/constants.ts) - `DEFAULT_LIMIT`
244
+ - [`packages/core-server/src/base/repositories/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/repositories/common/types.ts) - `TDataRange`, `buildDataRange`
@@ -6,11 +6,20 @@ difficulty: intermediate
6
6
 
7
7
  # Filter System
8
8
 
9
- A filter is the object every repository read, update, and delete verb accepts to shape a query - which rows (`where`), which columns (`fields`), what order (`order`), and how much (`limit`/`skip`).
9
+ Every repository read, update, and delete verb takes the same `filter` object. It picks rows (`where`), columns (`fields`), order (`order`), and how many (`limit`/`skip`).
10
+
11
+ The vocabulary ships as its own package, **`@venizia/ignis-filter`**. Applications on `@venizia/ignis` already get every name here re-exported from the core barrel, so nothing changes for them. Install it directly only when you want the filter language **without** the server framework - a browser or a Web Worker - since it resolves no node builtin and no server-only dependency:
12
+
13
+ ```typescript
14
+ import { QueryOperators, Sorts, type TFilter } from '@venizia/ignis-filter';
15
+ import { FilterSchema, WhereSchema } from '@venizia/ignis-filter/schemas';
16
+ ```
17
+
18
+ On a server take the schemas from `@venizia/ignis` instead: the ones on that subpath carry no OpenAPI metadata, so a route built on them documents nothing.
10
19
 
11
20
  ## In one example
12
21
 
13
- A filter shapes one query - `where` picks rows, `fields` picks columns, `order` sorts, `limit` bounds the result:
22
+ `where` picks rows, `fields` picks columns, `order` sorts, `limit` bounds the result:
14
23
 
15
24
  ```typescript
16
25
  import { postRepository } from '@/repositories';
@@ -28,7 +37,7 @@ const posts = await postRepository.find({
28
37
  });
29
38
  ```
30
39
 
31
- `postRepository` is a `@repository({ model: Post, dataSource })`-bound repository - `Post`'s schema comes from `@/schemas`. See [Models](/references/base/models) and [Repositories](../repositories/).
40
+ `postRepository` is a `@repository({ model: Post, dataSource })`-bound repository. `Post`'s schema comes from `@/schemas` - see [Models](/references/base/models) and [Repositories](../repositories/).
32
41
 
33
42
  ```sql
34
43
  -- Equivalent SQL
@@ -42,9 +51,9 @@ LIMIT 20
42
51
  ## How it works
43
52
 
44
53
  - **`TFilter` maps straight to SQL.** Every property corresponds to one clause of the generated query - see the table below.
45
- - **`where` takes a bare value or an operator object.** A bare value is implicit equality (`null` becomes `IS NULL`, an array becomes `IN`); an operator object keys into one of the operator families.
46
- - **Multiple `where` keys are an implicit AND.** A dot-notation key (`'metadata.path'`) targets a JSON/JSONB column instead of a top-level column and accepts the same operators, with automatic numeric casting when the operand is a number.
47
- - **A model's `settings.defaultFilter` merges into every query for that model**, narrowing-only - see [Default filter](#default-filter) below.
54
+ - **`where` takes a bare value or an operator object.** A bare value is implicit equality (`null` becomes `IS NULL`, an array becomes `IN`). An operator object keys into one of the operator families.
55
+ - **Multiple `where` keys are an implicit AND.** A dot-notation key (`'metadata.path'`) targets a JSON/JSONB column instead of a top-level column, and accepts the same operators. IGNIS casts the operand automatically when it's a number.
56
+ - **A model's `settings.defaultFilter` merges into every query for that model** - see [Default filter](#default-filter) below.
48
57
 
49
58
  | Filter property | SQL equivalent | Purpose |
50
59
  |---|---|---|
@@ -68,6 +77,8 @@ LIMIT 20
68
77
  | Array (PostgreSQL) | `contains`, `containedBy`, `overlaps` | `{ tags: { contains: ['typescript'] } }` |
69
78
  | JSON path | comparison, null, list, range, and pattern operators, on a `'column.path'` key | `{ 'metadata.score': { gt: 80 } }` |
70
79
 
80
+ Full operator-by-operator tables, one line each, live on the [Quick Reference](./quick-reference) page.
81
+
71
82
  ### Fields, order, and pagination
72
83
 
73
84
  - **`fields`** selects columns - an array, or a `{ field: true }` object (inclusion-only; `false` is ignored).
@@ -78,7 +89,8 @@ LIMIT 20
78
89
  ### Default filter
79
90
 
80
91
  - **Applies automatically.** A model's `settings.defaultFilter` merges into every read, update, and delete for that model.
81
- - **Narrowing only.** When both the default and the caller's filter constrain the same field, the two conditions are AND-composed rather than one replacing the other - a caller can never widen or drop a scope like soft-delete or multi-tenancy by accident.
92
+ - **AND-composes on collision.** When the default and the caller's filter constrain the same field, IGNIS AND-composes the two conditions instead of one replacing the other.
93
+ - **One override escape.** Setting that same field to a plain scalar (not an operator object) replaces the default outright - the one intentional opt-out, and it needs no `shouldSkipDefaultFilter`. The full collision table lives on the [Default Filter](./default-filter) page.
82
94
 
83
95
  ```typescript
84
96
  import { model, BaseEntity } from '@venizia/ignis';
@@ -93,13 +105,13 @@ export class Post extends BaseEntity<typeof Post.schema> {
93
105
  }
94
106
 
95
107
  await postRepository.find({ filter: { where: { status: 'published' } } });
96
- // WHERE "isDeleted" = false AND "status" = 'published' - both conditions apply
108
+ // WHERE "isDeleted" = false AND "status" = 'published' - different keys, both apply
97
109
 
98
110
  await postRepository.find({
99
111
  filter: { where: { status: 'published' } },
100
112
  options: { shouldSkipDefaultFilter: true },
101
113
  });
102
- // WHERE "status" = 'published' - default filter skipped
114
+ // WHERE "status" = 'published' - default filter skipped entirely
103
115
  ```
104
116
 
105
117
  ## Operators
@@ -118,7 +130,7 @@ Each operator family and every long-form topic has its own page:
118
130
  | [Array Operators](./array-operators) | `contains`, `containedBy`, `overlaps` (PostgreSQL array columns) |
119
131
  | [JSON Filtering](./json-filtering) | Dot-path queries into JSON/JSONB columns |
120
132
  | [Fields, Order & Pagination](./fields-order-pagination) | `fields`, `order`, `limit`/`skip`/`offset`, `defaultLimit` |
121
- | [Default Filter](./default-filter) | `settings.defaultFilter`, merge semantics, `shouldSkipDefaultFilter` |
133
+ | [Default Filter](./default-filter) | `settings.defaultFilter`, the collision/narrowing law, `shouldSkipDefaultFilter` |
122
134
  | [Application Usage](./application-usage) | How a filter flows controller -> service -> repository |
123
135
  | [Use Case Gallery](./use-cases) | Real-world filters with the SQL they produce |
124
136
  | [Tips & Edge Cases](./tips) | Performance notes and common gotchas |
@@ -132,6 +144,6 @@ Each operator family and every long-form topic has its own page:
132
144
 
133
145
  **Files:**
134
146
 
135
- - [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
136
- - [`packages/core/src/base/repositories/query-schemas/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/query-schemas/filter.ts) - `TFilter`/`TInclusion` types
137
- - [`packages/core/src/base/repositories/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/operators.ts) - `QueryOperators`/`Sorts` constants
147
+ - [`packages/core-server/src/connectors/relational/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/relational/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
148
+ - [`packages/filter/src/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/filter/src/common/types.ts) - `TFilter`/`TInclusion` types
149
+ - [`packages/filter/src/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/filter/src/common/operators.ts) - `QueryOperators`/`Sorts` constants