@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
@@ -23,44 +23,34 @@ Find rows where the array column contains **all** specified elements.
23
23
  // Schema: tags varchar(100)[]
24
24
  // Data: Product A has ['electronics', 'featured', 'sale']
25
25
 
26
- // Find products with BOTH 'electronics' AND 'featured'
27
26
  { where: { tags: { contains: ['electronics', 'featured'] } } }
28
27
  // SQL: "tags"::text[] @> ARRAY['electronics', 'featured']::text[]
29
-
30
- // Single element (can pass single value or array)
31
- { where: { tags: { contains: ['featured'] } } }
32
- { where: { tags: { contains: 'featured' } } } // Also works
33
- // Matches: ['featured'], ['featured', 'sale'], ['a', 'featured', 'b']
34
28
  ```
35
29
 
30
+ > [!NOTE]
31
+ > A single value is wrapped in an array automatically: `{ contains: 'featured' }` is treated as `{ contains: ['featured'] }`.
32
+
36
33
 
37
34
  ## containedBy (<@)
38
35
 
39
36
  Find rows where **all** array elements are within the specified set.
40
37
 
41
38
  ```typescript
42
- // Find products where ALL tags are in the allowed list
43
39
  { where: { tags: { containedBy: ['sale', 'featured', 'new', 'popular'] } } }
44
40
  // SQL: "tags"::text[] <@ ARRAY['sale', 'featured', 'new', 'popular']::text[]
45
-
46
- // Product A ['featured', 'sale'] -> matches (all in list)
47
- // Product B ['featured', 'clearance'] -> no match ('clearance' not in list)
48
- // Product C [] -> matches (empty is subset of everything)
49
41
  ```
50
42
 
43
+ > [!NOTE]
44
+ > An empty array is a subset of every set, so `tags: []` always matches `containedBy`.
45
+
51
46
 
52
47
  ## overlaps (&&)
53
48
 
54
49
  Find rows where the arrays share at least one common element.
55
50
 
56
51
  ```typescript
57
- // Find products with 'premium' OR 'sale' tag
58
52
  { where: { tags: { overlaps: ['premium', 'sale'] } } }
59
53
  // SQL: "tags"::text[] && ARRAY['premium', 'sale']::text[]
60
-
61
- // Product A ['featured', 'sale'] -> matches (has 'sale')
62
- // Product B ['premium', 'luxury'] -> matches (has 'premium')
63
- // Product C ['new', 'featured'] -> no match (no overlap)
64
54
  ```
65
55
 
66
56
 
@@ -84,52 +74,41 @@ Find rows where the arrays share at least one common element.
84
74
  | "Must have AT LEAST ONE of these tags" | `overlaps` |
85
75
 
86
76
 
87
- ## Empty Array Behavior
88
-
89
- | Operator | SQL Generated | Behavior |
90
- |----------|---------------|----------|
91
- | `contains: []` | `WHERE true` | Returns **ALL** rows |
92
- | `containedBy: []` | `WHERE "col" = '{}'` | Returns only rows with **empty arrays** |
93
- | `overlaps: []` | `WHERE false` | Returns **NO** rows |
94
-
95
- > [!NOTE]
96
- > Single values are automatically wrapped in an array: `{ contains: 'value' }` is treated as `{ contains: ['value'] }`.
97
-
98
-
99
77
  ## Type Handling
100
78
 
101
- **String Arrays** (`varchar[]`, `text[]`, `char[]`):
79
+ The element type of the array column decides the cast in the generated SQL.
80
+
102
81
  ```typescript
82
+ // String arrays (varchar[], text[], char[]) - both sides cast to text[]
103
83
  { where: { tags: { contains: ['a', 'b'] } } }
104
84
  // SQL: "tags"::text[] @> ARRAY['a', 'b']::text[]
105
- ```
106
85
 
107
- Both the column and the array literal are cast to `text[]` for compatibility.
108
-
109
- **Numeric Arrays** (`integer[]`, `numeric[]`):
110
- ```typescript
86
+ // Numeric arrays (integer[], numeric[]) - no cast needed
111
87
  { where: { scores: { contains: [100, 200] } } }
112
88
  // SQL: "scores" @> ARRAY[100, 200]
113
- ```
114
-
115
- No casting needed for numeric arrays.
116
89
 
117
- **Boolean Arrays**:
118
- ```typescript
90
+ // Boolean arrays - no cast needed
119
91
  { where: { flags: { contains: [true, false] } } }
120
92
  // SQL: "flags" @> ARRAY[true, false]
121
93
  ```
122
94
 
123
95
 
96
+ ## Empty Array Behavior
97
+
98
+ | Operator | SQL generated | Behavior |
99
+ |----------|---------------|----------|
100
+ | `contains: []` | `WHERE true` | Returns **ALL** rows |
101
+ | `containedBy: []` | `WHERE "col" = '{}'` | Returns only rows with **empty arrays** |
102
+ | `overlaps: []` | `WHERE false` | Returns **NO** rows |
103
+
104
+
124
105
  ## Security: Parameterized Values
125
106
 
126
- Every element of `contains`/`containedBy`/`overlaps` is bound as a query parameter -- only the operator token (`@>`/`<@`/`&&`) is raw SQL. See [The Hardening Round](../../../changelogs/2026-07-13-hardening-round) for the prior injection this closed.
107
+ Every element of `contains`/`containedBy`/`overlaps` is bound as a query parameter - only the operator token (`@>`/`<@`/`&&`) is raw SQL. See [The Hardening Round](../../../changelogs/2026-07-13-hardening-round) for the prior injection this closed.
127
108
 
128
109
 
129
110
  ## Defining Array Columns
130
111
 
131
- In your Drizzle schema:
132
-
133
112
  ```typescript
134
113
  import { pgTable, text, varchar, integer } from 'drizzle-orm/pg-core';
135
114
 
@@ -137,7 +116,6 @@ export const productTable = pgTable('Product', {
137
116
  id: text('id').primaryKey(),
138
117
  name: text('name').notNull(),
139
118
 
140
- // Array columns
141
119
  tags: varchar('tags', { length: 100 }).array(), // varchar(100)[]
142
120
  categories: text('categories').array(), // text[]
143
121
  scores: integer('scores').array(), // integer[]
@@ -152,6 +130,6 @@ export const productTable = pgTable('Product', {
152
130
 
153
131
  **Files:**
154
132
 
155
- - [`packages/core/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, `buildPgArrayComparison`
156
- - [`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
157
- - [`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` constants
133
+ - [`packages/core-server/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, `buildPgArrayComparison`
134
+ - [`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
135
+ - [`packages/filter/src/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/filter/src/common/operators.ts) - `QueryOperators` constants
@@ -6,116 +6,96 @@ difficulty: intermediate
6
6
 
7
7
  # Comparison Operators
8
8
 
9
- Equality and comparison operators for filtering records.
9
+ Compares a field against a value: equality, inequality, and ordering.
10
10
 
11
+ | Operator | SQL | Meaning |
12
+ |----------|-----|---------|
13
+ | `eq` | `=` / `IS NULL` | Equal to |
14
+ | `ne` | `!=` / `IS NOT NULL` | Not equal to |
15
+ | `neq` | `!=` / `IS NOT NULL` | Alias for `ne` |
16
+ | `gt` | `>` | Greater than |
17
+ | `gte` | `>=` | Greater than or equal |
18
+ | `lt` | `<` | Less than |
19
+ | `lte` | `<=` | Less than or equal |
11
20
 
12
- ## eq - Equal To
13
-
14
- Matches records where field equals the value.
21
+ ## eq
15
22
 
16
23
  ```typescript
17
- // Implicit equality
18
- { where: { status: 'active' } }
19
-
20
- // Explicit form
21
24
  { where: { status: { eq: 'active' } } }
22
-
23
25
  // SQL: WHERE "status" = 'active'
24
26
  ```
25
27
 
26
- **Special Cases:**
27
- ```typescript
28
- // Null equality
29
- { where: { deletedAt: null } }
30
- { where: { deletedAt: { eq: null } } }
31
- // SQL: WHERE "deleted_at" IS NULL
32
-
33
- // Array shorthand (becomes IN)
34
- { where: { id: [1, 2, 3] } }
35
- // SQL: WHERE "id" IN (1, 2, 3)
36
-
37
- // Empty array shorthand
38
- { where: { id: [] } }
39
- // SQL: WHERE false (no results)
40
- ```
41
-
28
+ **Notice:** the bare shorthand `{ status: 'active' }` (no operator key) means the same thing.
42
29
 
43
- ## ne / neq - Not Equal To
30
+ **Edge cases:**
31
+ - `{ eq: null }` compiles to `IS NULL`, never `= NULL`.
32
+ - Bare array `{ field: [1, 2, 3] }` (no operator key) compiles to `IN (1, 2, 3)`.
33
+ - An explicit `{ eq: [1, 2, 3] }` does not become `IN` - it compares the column to an array value.
34
+ - Bare empty array `{ field: [] }` matches no rows (`WHERE false`).
44
35
 
45
- Matches records where field does NOT equal the value. Both `ne` and `neq` are aliases and behave identically.
36
+ ## ne / neq
46
37
 
47
38
  ```typescript
48
39
  { where: { status: { ne: 'deleted' } } }
49
- { where: { status: { neq: 'deleted' } } } // Alias
50
-
51
40
  // SQL: WHERE "status" != 'deleted'
52
-
53
- // Null handling
54
- { where: { deletedAt: { ne: null } } }
55
- { where: { deletedAt: { neq: null } } }
56
- // SQL: WHERE "deleted_at" IS NOT NULL
57
41
  ```
58
42
 
59
- > [!NOTE]
60
- > When compared against a **real value** (not `null`), `ne`/`neq` follow SQL three-valued logic: a row whose field is `NULL` never matches `{ field: { neq: value } }`, because `NULL <> value` evaluates to UNKNOWN rather than TRUE. To include NULL rows, add an explicit branch: `{ or: [{ field: { neq: value } }, { field: null }] }`.
43
+ **Notice:** `ne` and `neq` are the same operator under two names.
61
44
 
45
+ **Edge cases:**
46
+ - `{ ne: null }` compiles to `IS NOT NULL`.
47
+ - SQL three-valued logic applies: a `NULL` field never matches `{ ne: value }`, because `NULL <> value` is UNKNOWN.
48
+ - Add an `or` branch to include NULL rows: `{ or: [{ field: { ne: value } }, { field: null }] }`.
62
49
 
63
- ## gt - Greater Than
50
+ ## gt
64
51
 
65
52
  ```typescript
66
- // Numbers
67
53
  { where: { price: { gt: 100 } } }
68
54
  // SQL: WHERE "price" > 100
69
-
70
- // Dates
71
- { where: { createdAt: { gt: new Date('2024-01-01') } } }
72
- // SQL: WHERE "created_at" > '2024-01-01'
73
-
74
- // Strings (lexicographic)
75
- { where: { name: { gt: 'M' } } }
76
- // SQL: WHERE "name" > 'M'
77
55
  ```
78
56
 
57
+ **Notice:** works on numbers, dates, and strings (lexicographic comparison).
58
+
59
+ **Edge cases:**
60
+ - `{ gt: null }` compiles to `"price" > NULL`, which is never true - no rows match.
61
+ - Use `is`/`exists` instead to check for null.
62
+ - Combine with other operators in the same object: `{ gte: 18, lt: 65 }`.
79
63
 
80
- ## gte - Greater Than or Equal
64
+ ## gte
81
65
 
82
66
  ```typescript
83
67
  { where: { quantity: { gte: 10 } } }
84
68
  // SQL: WHERE "quantity" >= 10
85
-
86
- // Combined with other operators
87
- { where: { age: { gte: 18, lt: 65 } } }
88
- // SQL: WHERE "age" >= 18 AND "age" < 65
89
69
  ```
90
70
 
71
+ **Notice:** inclusive of the boundary value.
72
+
73
+ **Edge cases:**
74
+ - Same null behavior as `gt`: `{ gte: null }` matches no rows.
91
75
 
92
- ## lt - Less Than
76
+ ## lt
93
77
 
94
78
  ```typescript
95
79
  { where: { stock: { lt: 5 } } }
96
80
  // SQL: WHERE "stock" < 5
97
81
  ```
98
82
 
83
+ **Notice:** exclusive of the boundary value.
84
+
85
+ **Edge cases:**
86
+ - Same null behavior as `gt`: `{ lt: null }` matches no rows.
99
87
 
100
- ## lte - Less Than or Equal
88
+ ## lte
101
89
 
102
90
  ```typescript
103
91
  { where: { rating: { lte: 3 } } }
104
92
  // SQL: WHERE "rating" <= 3
105
93
  ```
106
94
 
95
+ **Notice:** inclusive of the boundary value.
107
96
 
108
- ## Summary
109
-
110
- | Operator | SQL | Description |
111
- |----------|-----|-------------|
112
- | `eq` | `=` / `IS NULL` | Equal to (handles null) |
113
- | `ne` | `!=` / `IS NOT NULL` | Not equal to (handles null) |
114
- | `neq` | `!=` / `IS NOT NULL` | Alias for `ne` |
115
- | `gt` | `>` | Greater than |
116
- | `gte` | `>=` | Greater than or equal |
117
- | `lt` | `<` | Less than |
118
- | `lte` | `<=` | Less than or equal |
97
+ **Edge cases:**
98
+ - Same null behavior as `gt`: `{ lte: null }` matches no rows.
119
99
 
120
100
  ## See also
121
101
 
@@ -125,6 +105,6 @@ Matches records where field does NOT equal the value. Both `ne` and `neq` are al
125
105
 
126
106
  **Files:**
127
107
 
128
- - [`packages/core/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, per-operator SQL builders
129
- - [`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
130
- - [`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` constants
108
+ - [`packages/core-server/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, per-operator SQL builders
109
+ - [`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
110
+ - [`packages/filter/src/common/operators.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/filter/src/common/operators.ts) - `QueryOperators` constants
@@ -2,14 +2,12 @@
2
2
  title: Default Filter
3
3
  description: Automatically apply filter conditions to all repository queries
4
4
  difficulty: intermediate
5
- lastUpdated: 2026-03-15
5
+ lastUpdated: 2026-07-23
6
6
  ---
7
7
 
8
8
  # Default Filter <Badge type="tip" text="v0.0.5+" />
9
9
 
10
- A model's `settings.defaultFilter` merges into every `find`/`findOne`/`findById`/`count`/`updateAll`/`deleteAll` call for that model - the standard way to implement soft delete, multi-tenancy, active-record scoping, and query-limit protection without repeating a `where` clause at every call site.
11
-
12
- ## Quick start
10
+ A model's `settings.defaultFilter` merges into every `find`/`findOne`/`findById`/`count`/`updateById`/`updateAll`/`deleteById`/`deleteAll` call for that model. It's the standard way to implement soft delete, multi-tenancy, active-record scoping, and query-limit protection without repeating a `where` clause at every call site.
13
11
 
14
12
  ```typescript
15
13
  import { model, BaseEntity } from '@venizia/ignis';
@@ -17,15 +15,21 @@ import { userTable } from '@/schemas';
17
15
 
18
16
  @model({
19
17
  type: 'entity',
20
- settings: {
21
- defaultFilter: { where: { isDeleted: false }, limit: 100 },
22
- },
18
+ settings: { defaultFilter: { where: { isDeleted: false }, limit: 100 } },
23
19
  })
24
20
  export class User extends BaseEntity<typeof User.schema> {
25
21
  static override schema = userTable;
26
22
  }
27
23
  ```
28
24
 
25
+ ## Options
26
+
27
+ | Option | Type | Default | Meaning |
28
+ |---|---|---|---|
29
+ | `settings.defaultFilter` | `TFilter` | none | Filter merged into every query for the model - `where`, `limit`, `offset`, `order`, `fields`, `include` are all valid inside it. |
30
+ | `settings.defaultLimit` | `number` (positive integer) | `DEFAULT_LIMIT` (`10`) | Per-model row cap. Independent of `defaultFilter` - see [Fields, Order & Pagination -> Default limit resolution](./fields-order-pagination#default-limit-resolution). |
31
+ | `options.shouldSkipDefaultFilter` | `boolean` | `false` | Skips the `defaultFilter` merge for one call. Does not drop `defaultLimit`. |
32
+
29
33
  ```typescript
30
34
  import { userRepository } from '@/repositories';
31
35
 
@@ -38,7 +42,7 @@ await userRepository.find({ filter: { where: { status: 'active' } } });
38
42
  `applyDefaultFilter()` merges the model's `defaultFilter` with the caller's filter via `FilterBuilder.mergeFilter()`.
39
43
 
40
44
  - **`where` narrows per-key.** See the narrowing law below.
41
- - **Everything else is user-wins-if-provided.** A caller value replaces the default, but a caller value of `undefined` never does - a filter built by spreading an optional object can't silently blow away a tenant scope or a limit.
45
+ - **Everything else is user-wins-if-provided.** A caller value replaces the default, but a caller value of `undefined` never does. A filter built by spreading an optional object can't silently blow away a tenant scope or a limit.
42
46
 
43
47
  | Property | Merge strategy |
44
48
  |---|---|
@@ -47,7 +51,7 @@ await userRepository.find({ filter: { where: { status: 'active' } } });
47
51
 
48
52
  ### The `where` narrowing law
49
53
 
50
- Keys present on only one side pass through untouched. When the **same key** appears on both sides, the outcome depends on shape:
54
+ Keys present on only one side pass through untouched. When the same key appears on both sides, the outcome depends on shape:
51
55
 
52
56
  | Default | Caller | Result |
53
57
  |---|---|---|
@@ -58,7 +62,7 @@ Keys present on only one side pass through untouched. When the **same key** appe
58
62
 
59
63
  - **`and` collisions concatenate.** Both conjunct lists merge into one.
60
64
  - **`or` collisions cannot concatenate** - that would union, not narrow - so each side's `or` group becomes its own conjunct instead.
61
- - **Non-scalar collisions always AND-compose.** A default scope - a `createdAt` floor, a tenant `inq` - can be narrowed by a caller filter but never widened or dropped.
65
+ - **Non-scalar collisions always AND-compose.** Take a default scope - a `createdAt` floor, a tenant `inq`. A caller filter can narrow it, but never widen or drop it.
62
66
  - **Only scalar-over-scalar is a true override.** Every other collision shape composes rather than replaces.
63
67
 
64
68
  ```typescript
@@ -80,7 +84,7 @@ Non-colliding keys still combine with an implicit AND, exactly like two `where`
80
84
 
81
85
  ## Bypassing the default filter
82
86
 
83
- Pass `shouldSkipDefaultFilter: true` in `options` to skip the merge entirely. It is honored by every repository verb - `find`, `findOne`, `findById`, `count`, `updateById`, `updateAll`, `deleteById`, `deleteAll`:
87
+ Pass `shouldSkipDefaultFilter: true` in `options` to skip the merge entirely. Every repository verb honors it - `find`, `findOne`, `findById`, `count`, `updateById`, `updateAll`, `deleteById`, `deleteAll`:
84
88
 
85
89
  ```typescript
86
90
  // Normal - default filter applies
@@ -95,6 +99,17 @@ await repository.find({
95
99
  // WHERE "role" = 'admin' (includes soft-deleted rows)
96
100
  ```
97
101
 
102
+ `updateById` and `deleteById` merge the default filter into their `{ id }` condition the same way `updateAll`/`deleteAll` merge it into their `where`. The bypass applies to all four identically:
103
+
104
+ ```typescript
105
+ // Also merges the default filter into { id: postId } - skip to update a soft-deleted row
106
+ await postRepository.updateById({
107
+ id: postId,
108
+ data: { title: 'Restored' },
109
+ options: { shouldSkipDefaultFilter: true },
110
+ });
111
+ ```
112
+
98
113
  It composes with a transaction the same way any other option does:
99
114
 
100
115
  ```typescript
@@ -121,9 +136,25 @@ try {
121
136
  | Cross-tenant analytics | Count/aggregate across every tenant |
122
137
  | Data migration | Update rows regardless of status |
123
138
 
139
+ `shouldSkipDefaultFilter` lives on the same options interface every repository verb accepts:
140
+
141
+ ```typescript
142
+ interface IExtraOptions extends IWithTransaction {
143
+ shouldSkipDefaultFilter?: boolean;
144
+ log?: TRepositoryLogOptions;
145
+ lock?: TLockOptions;
146
+ }
147
+
148
+ interface IWithTransaction {
149
+ transaction?: ITransaction;
150
+ }
151
+ ```
152
+
153
+ `log` and `lock` are documented in [Advanced Repository Features](../repositories/advanced.md) - `log` only takes effect on write verbs (`create`/`updateById`/`updateAll`/`deleteById`/`deleteAll`), not on reads.
154
+
124
155
  ## Configuring a default filter
125
156
 
126
- Any `TFilter` property is valid inside `defaultFilter` - `where`, `limit`, `offset`, `order`, `fields`, `include` (see [Filter System Overview](./)). The two recurring shapes:
157
+ Any `TFilter` property is valid inside `defaultFilter` - `where`, `limit`, `offset`, `order`, `fields`, `include` (see [Filter System Overview](./)). Two shapes cover most cases.
127
158
 
128
159
  **Soft delete or multi-tenant scoping** - a `where` clause that every query must carry:
129
160
 
@@ -144,7 +175,7 @@ await postRepository.updateById({
144
175
  });
145
176
  ```
146
177
 
147
- **Query-limit protection** - prefer the dedicated `settings.defaultLimit` over a `limit` inside `defaultFilter`. It resolves independently (`query.limit ?? defaultLimit ?? 10`, see [Fields, Order & Pagination -> Default Limit](./fields-order-pagination#default-limit)) and, unlike `defaultFilter`, is **not** dropped by `shouldSkipDefaultFilter`:
178
+ **Query-limit protection** - prefer the dedicated `settings.defaultLimit` over a `limit` inside `defaultFilter`. It resolves independently (`query.limit ?? defaultLimit ?? 10`, see [Fields, Order & Pagination -> Default limit resolution](./fields-order-pagination#default-limit-resolution)). Unlike `defaultFilter`, it is not dropped by `shouldSkipDefaultFilter`:
148
179
 
149
180
  ```typescript
150
181
  @model({
@@ -153,7 +184,7 @@ await postRepository.updateById({
153
184
  })
154
185
  export class LogEntry extends BaseEntity<typeof LogEntry.schema> {}
155
186
 
156
- await logEntryRepository.find({ filter: {} }); // LIMIT 1000
187
+ await logEntryRepository.find({ filter: {} }); // LIMIT 1000
157
188
  await logEntryRepository.find({ filter: { limit: 50 } }); // LIMIT 50
158
189
  ```
159
190
 
@@ -190,7 +221,7 @@ await repository.find({
190
221
  +------------------+
191
222
  ```
192
223
 
193
- `RelationalBaseRepository` (compatibility alias `PostgresBaseRepository`, `packages/core/src/connectors/postgres/repositories/core/base.ts`) implements the default-filter behavior directly - no mixin is composed onto it:
224
+ `RelationalBaseRepository` (compatibility alias `PostgresBaseRepository`, `packages/core-server/src/connectors/postgres/repositories/core/base.ts`) implements the default-filter behavior directly - no mixin is composed onto it:
194
225
 
195
226
  ```typescript
196
227
  hasDefaultFilter(): boolean
@@ -199,53 +230,26 @@ getDefaultLimit(): number | undefined
199
230
  applyDefaultFilter(opts: { userFilter?: TFilter; shouldSkipDefaultFilter?: boolean }): TFilter
200
231
  ```
201
232
 
202
- `getDefaultFilter()` reads `this.modelSettings?.defaultFilter`, where `modelSettings` is a protected getter on `AbstractRepository` (`src/base/repositories/core/abstract.ts`) resolved from `MetadataRegistry` by the entity's constructor (not by name string) on first access, then memoized.
233
+ `getDefaultFilter()` reads `this.modelSettings?.defaultFilter`. `modelSettings` is a protected getter on `AbstractRepository` (`src/base/repositories/core/abstract.ts`), resolved from `MetadataRegistry` by the entity's constructor - not by name string - on first access, then memoized.
234
+
235
+ Read verbs (`find`/`findOne`/`findById`/`count`) call `applyDefaultFilter()` directly. Write verbs (`updateById`/`updateAll`/`deleteById`/`deleteAll`) route through the shared `_update`/`_delete` helpers instead. Those helpers call it against `{ where: opts.where }` (or `{ id }` for the `ById` forms) before building the SQL condition.
203
236
 
204
237
  > [!NOTE]
205
238
  > An older `DefaultFilterMixin` implemented this same behavior via mixin composition. It is no longer composed onto any repository class - see [Repository Mixins (Removed)](../repositories/mixins.md) for history.
206
239
 
207
- The merge itself is `FilterBuilder.mergeFilter()`:
240
+ The merge itself is `FilterBuilder.mergeFilter()`. Reach it through the datasource's query dialect -
241
+ `FilterBuilder` is abstract, so you never construct it directly:
208
242
 
209
243
  ```typescript
210
- const filterBuilder = new FilterBuilder();
244
+ const queryDialect = dataSource.getQueryDialect();
211
245
 
212
- filterBuilder.mergeFilter({
246
+ queryDialect.mergeFilter({
213
247
  defaultFilter: { where: { isDeleted: false }, limit: 100 },
214
248
  userFilter: { where: { status: 'active' }, limit: 10 },
215
249
  });
216
250
  // { where: { isDeleted: false, status: 'active' }, limit: 10 }
217
251
  ```
218
252
 
219
- ### `IExtraOptions`
220
-
221
- `shouldSkipDefaultFilter` lives on the same options interface every repository verb accepts:
222
-
223
- ```typescript
224
- interface IExtraOptions extends IWithTransaction {
225
- shouldSkipDefaultFilter?: boolean;
226
- log?: TRepositoryLogOptions;
227
- lock?: TLockOptions;
228
- }
229
-
230
- interface IWithTransaction {
231
- transaction?: ITransaction;
232
- }
233
- ```
234
-
235
- `log` and `lock` are documented in [Advanced Repository Features](../repositories/advanced.md) - `log` only takes effect on write verbs (`create`/`updateById`/`updateAll`/`deleteById`/`deleteAll`), not on reads.
236
-
237
- ## Quick reference
238
-
239
- | Want to... | Code |
240
- |---|---|
241
- | Configure a default filter | `@model({ settings: { defaultFilter: { ... } } })` |
242
- | Bypass the default filter | `options: { shouldSkipDefaultFilter: true }` |
243
- | Bypass for one relation | `include: [{ relation: 'x', shouldSkipDefaultFilter: true }]` |
244
- | Combine with a transaction | `options: { transaction: tx, shouldSkipDefaultFilter: true }` |
245
- | Check if a model has a default | `repository.hasDefaultFilter()` |
246
- | Read the raw default filter | `repository.getDefaultFilter()` |
247
- | Read the raw default limit | `repository.getDefaultLimit()` |
248
-
249
253
  ## See also
250
254
 
251
255
  - [Filter System Overview](./) - the `filter` shape and every operator family
@@ -255,7 +259,9 @@ interface IWithTransaction {
255
259
 
256
260
  **Files:**
257
261
 
258
- - [`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.mergeFilter()`/`mergeWhere()`, the narrowing merge
259
- - [`packages/core/src/connectors/postgres/repositories/core/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/core/base.ts) - `RelationalBaseRepository`, `applyDefaultFilter`/`getDefaultFilter`/`getDefaultLimit`
260
- - [`packages/core/src/base/metadata/persistents.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/metadata/persistents.ts) - `@model` decorator, `defaultLimit` validation
261
- - [`packages/core/src/base/repositories/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/repositories/common/types.ts) - `IExtraOptions`, `IWithTransaction`
262
+ - [`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.mergeFilter()`/`mergeWhere()`, the narrowing merge
263
+ - [`packages/core-server/src/connectors/postgres/repositories/core/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/core/base.ts) - `RelationalBaseRepository`, `applyDefaultFilter`/`getDefaultFilter`/`getDefaultLimit`
264
+ - [`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`/`findOne`/`count` calling `applyDefaultFilter`
265
+ - [`packages/core-server/src/connectors/postgres/repositories/core/persistable.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/core/persistable.ts) - `_update`/`_delete` calling `applyDefaultFilter` for `updateById`/`updateAll`/`deleteById`/`deleteAll`
266
+ - [`packages/core-server/src/base/metadata/persistents.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/metadata/persistents.ts) - `@model` decorator, `defaultLimit` validation
267
+ - [`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) - `IExtraOptions`, `IWithTransaction`