turbine-orm 0.50.0 → 0.51.0

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 (186) hide show
  1. package/README.md +66 -66
  2. package/dist/adapters/cockroachdb.d.ts +5 -5
  3. package/dist/adapters/cockroachdb.js +10 -10
  4. package/dist/adapters/index.d.ts +5 -5
  5. package/dist/adapters/index.js +7 -7
  6. package/dist/adapters/yugabytedb.d.ts +7 -7
  7. package/dist/adapters/yugabytedb.js +10 -10
  8. package/dist/cjs/adapters/cockroachdb.d.ts +5 -5
  9. package/dist/cjs/adapters/cockroachdb.js +10 -10
  10. package/dist/cjs/adapters/index.d.ts +5 -5
  11. package/dist/cjs/adapters/index.js +7 -7
  12. package/dist/cjs/adapters/yugabytedb.d.ts +7 -7
  13. package/dist/cjs/adapters/yugabytedb.js +10 -10
  14. package/dist/cjs/cli/config.d.ts +13 -2
  15. package/dist/cjs/cli/config.js +3 -2
  16. package/dist/cjs/cli/destructive.d.ts +1 -1
  17. package/dist/cjs/cli/destructive.js +1 -1
  18. package/dist/cjs/cli/index.d.ts +10 -10
  19. package/dist/cjs/cli/index.js +49 -45
  20. package/dist/cjs/cli/loader.d.ts +7 -7
  21. package/dist/cjs/cli/loader.js +9 -9
  22. package/dist/cjs/cli/mcp.js +4 -4
  23. package/dist/cjs/cli/migrate.d.ts +5 -5
  24. package/dist/cjs/cli/migrate.js +11 -11
  25. package/dist/cjs/cli/studio-ui.generated.js +1 -1
  26. package/dist/cjs/cli/ui.d.ts +2 -2
  27. package/dist/cjs/cli/ui.js +2 -2
  28. package/dist/cjs/client.d.ts +49 -38
  29. package/dist/cjs/client.js +57 -56
  30. package/dist/cjs/dialect.d.ts +62 -18
  31. package/dist/cjs/dialect.js +40 -2
  32. package/dist/cjs/errors.d.ts +5 -5
  33. package/dist/cjs/errors.js +11 -11
  34. package/dist/cjs/generate.d.ts +6 -6
  35. package/dist/cjs/generate.js +31 -29
  36. package/dist/cjs/index-advisor.d.ts +5 -5
  37. package/dist/cjs/index-advisor.js +0 -0
  38. package/dist/cjs/index.d.ts +1 -1
  39. package/dist/cjs/index.js +7 -7
  40. package/dist/cjs/introspect.d.ts +35 -9
  41. package/dist/cjs/introspect.js +83 -32
  42. package/dist/cjs/mssql.d.ts +11 -11
  43. package/dist/cjs/mssql.js +64 -29
  44. package/dist/cjs/mysql.d.ts +8 -8
  45. package/dist/cjs/mysql.js +61 -23
  46. package/dist/cjs/nested-write.d.ts +21 -2
  47. package/dist/cjs/nested-write.js +51 -14
  48. package/dist/cjs/optional-peer-import.cjs +7 -7
  49. package/dist/cjs/optional-peer-import.d.cts +7 -7
  50. package/dist/cjs/pipeline-submittable.d.ts +2 -2
  51. package/dist/cjs/pipeline-submittable.js +6 -6
  52. package/dist/cjs/pipeline.d.ts +1 -1
  53. package/dist/cjs/pipeline.js +4 -4
  54. package/dist/cjs/powdb-introspect.d.ts +1 -1
  55. package/dist/cjs/powdb-introspect.js +1 -1
  56. package/dist/cjs/powdb.d.ts +28 -28
  57. package/dist/cjs/powdb.js +66 -66
  58. package/dist/cjs/powql.d.ts +27 -27
  59. package/dist/cjs/powql.js +73 -52
  60. package/dist/cjs/query/aggregates.d.ts +1 -1
  61. package/dist/cjs/query/aggregates.js +5 -5
  62. package/dist/cjs/query/batched-loader.d.ts +11 -11
  63. package/dist/cjs/query/batched-loader.js +24 -24
  64. package/dist/cjs/query/builder.d.ts +39 -21
  65. package/dist/cjs/query/builder.js +99 -57
  66. package/dist/cjs/query/compound-unique.d.ts +1 -1
  67. package/dist/cjs/query/compound-unique.js +0 -0
  68. package/dist/cjs/query/deferred.d.ts +12 -6
  69. package/dist/cjs/query/deferred.js +1 -1
  70. package/dist/cjs/query/filters.d.ts +31 -11
  71. package/dist/cjs/query/filters.js +67 -14
  72. package/dist/cjs/query/index.d.ts +1 -1
  73. package/dist/cjs/query/index.js +1 -1
  74. package/dist/cjs/query/relations.d.ts +9 -9
  75. package/dist/cjs/query/relations.js +164 -57
  76. package/dist/cjs/query/types.d.ts +86 -35
  77. package/dist/cjs/query/types.js +1 -1
  78. package/dist/cjs/query/utils.d.ts +27 -10
  79. package/dist/cjs/query/utils.js +86 -14
  80. package/dist/cjs/query/where.d.ts +47 -28
  81. package/dist/cjs/query/where.js +130 -31
  82. package/dist/cjs/query/writes.d.ts +24 -5
  83. package/dist/cjs/query/writes.js +102 -13
  84. package/dist/cjs/realtime.d.ts +7 -7
  85. package/dist/cjs/realtime.js +9 -9
  86. package/dist/cjs/schema-builder.d.ts +18 -7
  87. package/dist/cjs/schema-builder.js +17 -10
  88. package/dist/cjs/schema-metadata.d.ts +3 -3
  89. package/dist/cjs/schema-metadata.js +9 -9
  90. package/dist/cjs/schema-sql.d.ts +9 -9
  91. package/dist/cjs/schema-sql.js +20 -20
  92. package/dist/cjs/schema.d.ts +19 -9
  93. package/dist/cjs/schema.js +6 -6
  94. package/dist/cjs/serverless.d.ts +15 -15
  95. package/dist/cjs/serverless.js +16 -16
  96. package/dist/cjs/sqlite.d.ts +8 -8
  97. package/dist/cjs/sqlite.js +53 -22
  98. package/dist/cjs/typed-sql.d.ts +4 -4
  99. package/dist/cjs/typed-sql.js +5 -5
  100. package/dist/cli/config.d.ts +13 -2
  101. package/dist/cli/config.js +3 -2
  102. package/dist/cli/destructive.d.ts +1 -1
  103. package/dist/cli/destructive.js +1 -1
  104. package/dist/cli/index.d.ts +10 -10
  105. package/dist/cli/index.js +49 -45
  106. package/dist/cli/loader.d.ts +7 -7
  107. package/dist/cli/loader.js +9 -9
  108. package/dist/cli/mcp.js +4 -4
  109. package/dist/cli/migrate.d.ts +5 -5
  110. package/dist/cli/migrate.js +11 -11
  111. package/dist/cli/studio-ui.generated.js +1 -1
  112. package/dist/cli/ui.d.ts +2 -2
  113. package/dist/cli/ui.js +2 -2
  114. package/dist/client.d.ts +49 -38
  115. package/dist/client.js +57 -56
  116. package/dist/dialect.d.ts +62 -18
  117. package/dist/dialect.js +40 -2
  118. package/dist/errors.d.ts +5 -5
  119. package/dist/errors.js +11 -11
  120. package/dist/generate.d.ts +6 -6
  121. package/dist/generate.js +31 -29
  122. package/dist/index-advisor.d.ts +5 -5
  123. package/dist/index-advisor.js +0 -0
  124. package/dist/index.d.ts +1 -1
  125. package/dist/index.js +7 -7
  126. package/dist/introspect.d.ts +35 -9
  127. package/dist/introspect.js +82 -32
  128. package/dist/mssql.d.ts +11 -11
  129. package/dist/mssql.js +64 -29
  130. package/dist/mysql.d.ts +8 -8
  131. package/dist/mysql.js +61 -23
  132. package/dist/nested-write.d.ts +21 -2
  133. package/dist/nested-write.js +51 -14
  134. package/dist/optional-peer-import.cjs +7 -7
  135. package/dist/optional-peer-import.d.cts +7 -7
  136. package/dist/pipeline-submittable.d.ts +2 -2
  137. package/dist/pipeline-submittable.js +6 -6
  138. package/dist/pipeline.d.ts +1 -1
  139. package/dist/pipeline.js +4 -4
  140. package/dist/powdb-introspect.d.ts +1 -1
  141. package/dist/powdb-introspect.js +1 -1
  142. package/dist/powdb.d.ts +28 -28
  143. package/dist/powdb.js +66 -66
  144. package/dist/powql.d.ts +27 -27
  145. package/dist/powql.js +73 -52
  146. package/dist/query/aggregates.d.ts +1 -1
  147. package/dist/query/aggregates.js +5 -5
  148. package/dist/query/batched-loader.d.ts +11 -11
  149. package/dist/query/batched-loader.js +24 -24
  150. package/dist/query/builder.d.ts +39 -21
  151. package/dist/query/builder.js +100 -58
  152. package/dist/query/compound-unique.d.ts +1 -1
  153. package/dist/query/compound-unique.js +0 -0
  154. package/dist/query/deferred.d.ts +12 -6
  155. package/dist/query/deferred.js +1 -1
  156. package/dist/query/filters.d.ts +31 -11
  157. package/dist/query/filters.js +66 -13
  158. package/dist/query/index.d.ts +1 -1
  159. package/dist/query/index.js +1 -1
  160. package/dist/query/relations.d.ts +9 -9
  161. package/dist/query/relations.js +165 -58
  162. package/dist/query/types.d.ts +86 -35
  163. package/dist/query/types.js +1 -1
  164. package/dist/query/utils.d.ts +27 -10
  165. package/dist/query/utils.js +84 -14
  166. package/dist/query/where.d.ts +47 -28
  167. package/dist/query/where.js +129 -32
  168. package/dist/query/writes.d.ts +24 -5
  169. package/dist/query/writes.js +101 -13
  170. package/dist/realtime.d.ts +7 -7
  171. package/dist/realtime.js +9 -9
  172. package/dist/schema-builder.d.ts +18 -7
  173. package/dist/schema-builder.js +17 -10
  174. package/dist/schema-metadata.d.ts +3 -3
  175. package/dist/schema-metadata.js +9 -9
  176. package/dist/schema-sql.d.ts +9 -9
  177. package/dist/schema-sql.js +20 -20
  178. package/dist/schema.d.ts +19 -9
  179. package/dist/schema.js +6 -6
  180. package/dist/serverless.d.ts +15 -15
  181. package/dist/serverless.js +16 -16
  182. package/dist/sqlite.d.ts +8 -8
  183. package/dist/sqlite.js +53 -22
  184. package/dist/typed-sql.d.ts +4 -4
  185. package/dist/typed-sql.js +5 -5
  186. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm — Where-filter type guards and shape helpers
2
+ * turbine-orm, Where-filter type guards and shape helpers
3
3
  *
4
4
  * Pure detection / fingerprint utilities used by the query builder's WHERE
5
5
  * compiler. Kept out of builder.ts so the class file stays about SQL assembly
@@ -10,7 +10,7 @@ import type { ArrayFilter, ColumnRef, JsonFilter, JsonPathOrderBy, OrderBySpec,
10
10
  export declare function isWhereOperator(value: unknown): value is WhereOperator;
11
11
  /**
12
12
  * True for a *plain object literal* that reached an equality fallthrough
13
- * without matching any known filter shape — the misspelled-operator case.
13
+ * without matching any known filter shape, the misspelled-operator case.
14
14
  * Class instances (Buffer for bytea, Decimal wrappers, ...) are legitimate
15
15
  * bind values and return false, as do arrays and Dates.
16
16
  */
@@ -31,7 +31,7 @@ export declare function isColumnRef(value: unknown): value is ColumnRef;
31
31
  /**
32
32
  * Fingerprint the SHAPE of a where-operator object. Null-valued `equals` /
33
33
  * `not` compile to parameterless `IS NULL` / `IS NOT NULL` (different SQL, no
34
- * param pushed), so null-ness is part of the shape — without it a cache entry
34
+ * param pushed), so null-ness is part of the shape, without it a cache entry
35
35
  * warmed by `{ not: 5 }` would serve `{ not: null }` with a desynced param list.
36
36
  *
37
37
  * Column references ({@link ColumnRef}) compile the referenced column into the
@@ -44,7 +44,7 @@ export declare function fingerprintOperatorShape(value: WhereOperator): string;
44
44
  /**
45
45
  * Guard for the value of an `equals` operator reaching the plain-equality
46
46
  * operator path. A plain object literal can only legitimately be an equality
47
- * value on a json/jsonb column — and those route to the JSONB filter branch
47
+ * value on a json/jsonb column, and those route to the JSONB filter branch
48
48
  * BEFORE the operator branch, so any plain object that reaches here is a
49
49
  * mistake (e.g. `{ equals: { foo: 1 } }` on a text column). Shared by the
50
50
  * SQL-build path and the cache-hit param-collect path so a warmed cache can
@@ -56,16 +56,21 @@ export declare function assertBindableEqualsOperand(value: unknown, column: stri
56
56
  * cache fingerprint. The SQL-build and cache-hit param-collect paths MUST
57
57
  * enumerate object keys in this exact order: fingerprints sort keys, so two
58
58
  * where clauses with the same fields in different insertion order share one
59
- * cache entry — if build/collect iterated insertion order, the cached SQL's
59
+ * cache entry, if build/collect iterated insertion order, the cached SQL's
60
60
  * `$N` placeholders would bind the wrong values (cross-tenant-leak class).
61
61
  * Array order (OR/AND members) is positional and is never sorted.
62
62
  */
63
63
  export declare function sortedKeys(obj: Record<string, unknown>): string[];
64
64
  /** {@link sortedKeys}, but yielding `[key, value]` pairs. */
65
65
  export declare function sortedEntries<V>(obj: Record<string, V>): [string, V][];
66
- /** Known atomic-update operator keys — used to detect operator objects vs plain JSON values */
66
+ /** Known atomic-update operator keys, used to detect operator objects vs plain JSON values */
67
67
  export declare const UPDATE_OPERATOR_KEYS: Set<string>;
68
- /** Known JSONB operator keys */
68
+ /**
69
+ * Known JSONB operator keys. `stringContains` / `stringStartsWith` /
70
+ * `stringEndsWith` are appended below, once {@link JSON_STRING_OPERATORS} is
71
+ * declared: they have no `WhereOperator` counterpart, so their presence is an
72
+ * unambiguous JSON-filter signal.
73
+ */
69
74
  export declare const JSONB_OPERATOR_KEYS: Set<string>;
70
75
  /**
71
76
  * JSON range comparison operators → SQL comparison tokens, in the FIXED order
@@ -77,16 +82,31 @@ export declare const JSONB_OPERATOR_KEYS: Set<string>;
77
82
  * and they always require `path`.
78
83
  */
79
84
  export declare const JSON_RANGE_OPERATORS: Record<'gt' | 'gte' | 'lt' | 'lte', string>;
85
+ /**
86
+ * JSON substring operators → the LIKE pattern each one builds around its
87
+ * escaped operand, in the FIXED order the build and collect paths iterate
88
+ * them. These compare the TEXT at `path` (which every one of them requires),
89
+ * so they are the JSON counterpart of the scalar `contains` / `startsWith` /
90
+ * `endsWith` operators rather than of jsonb containment.
91
+ *
92
+ * They are deliberately NOT named `contains` / `startsWith` / `endsWith`:
93
+ * `contains` on a JSON column already means whole-document containment
94
+ * (`@>`), and silently changing that would break every existing caller.
95
+ */
96
+ export declare const JSON_STRING_OPERATORS: Record<'stringContains' | 'stringStartsWith' | 'stringEndsWith', (escaped: string) => string>;
97
+ export declare const JSON_FILTER_KEYS: ReadonlySet<string>;
80
98
  /**
81
99
  * Value-invariant shape fingerprint for a {@link JsonFilter}. Range operators
82
100
  * are annotated with the comparison value's kind (`#n` numeric / `#s` string)
83
- * because a numeric comparison compiles to a `::numeric` cast — a different
84
- * SQL text than the text comparison — so the two must never share a cached
85
- * SQL entry.
101
+ * because a numeric comparison compiles to a `::numeric` cast, a different
102
+ * SQL text than the text comparison, so the two must never share a cached
103
+ * SQL entry. `mode` is part of the shape for the same reason: it selects
104
+ * between `LIKE` and the dialect's case-insensitive form, which is different
105
+ * SQL text.
86
106
  */
87
107
  export declare function fingerprintJsonFilterShape(filter: JsonFilter): string;
88
108
  /**
89
- * JSONB operator keys that are *unique* to {@link JsonFilter} — they cannot
109
+ * JSONB operator keys that are *unique* to {@link JsonFilter}, they cannot
90
110
  * appear in any other where-filter shape, so the presence of one of these is
91
111
  * an unambiguous signal that the user meant a JSON filter. Used by the
92
112
  * strict-validation path so that `{ contains: 'foo' }` (which is also a valid
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm — Where-filter type guards and shape helpers
2
+ * turbine-orm, Where-filter type guards and shape helpers
3
3
  *
4
4
  * Pure detection / fingerprint utilities used by the query builder's WHERE
5
5
  * compiler. Kept out of builder.ts so the class file stays about SQL assembly
@@ -24,7 +24,7 @@ export function isWhereOperator(value) {
24
24
  }
25
25
  /**
26
26
  * True for a *plain object literal* that reached an equality fallthrough
27
- * without matching any known filter shape — the misspelled-operator case.
27
+ * without matching any known filter shape, the misspelled-operator case.
28
28
  * Class instances (Buffer for bytea, Decimal wrappers, ...) are legitimate
29
29
  * bind values and return false, as do arrays and Dates.
30
30
  */
@@ -57,7 +57,7 @@ export function isColumnRef(value) {
57
57
  /**
58
58
  * Fingerprint the SHAPE of a where-operator object. Null-valued `equals` /
59
59
  * `not` compile to parameterless `IS NULL` / `IS NOT NULL` (different SQL, no
60
- * param pushed), so null-ness is part of the shape — without it a cache entry
60
+ * param pushed), so null-ness is part of the shape, without it a cache entry
61
61
  * warmed by `{ not: 5 }` would serve `{ not: null }` with a desynced param list.
62
62
  *
63
63
  * Column references ({@link ColumnRef}) compile the referenced column into the
@@ -85,7 +85,7 @@ export function fingerprintOperatorShape(value) {
85
85
  /**
86
86
  * Guard for the value of an `equals` operator reaching the plain-equality
87
87
  * operator path. A plain object literal can only legitimately be an equality
88
- * value on a json/jsonb column — and those route to the JSONB filter branch
88
+ * value on a json/jsonb column, and those route to the JSONB filter branch
89
89
  * BEFORE the operator branch, so any plain object that reaches here is a
90
90
  * mistake (e.g. `{ equals: { foo: 1 } }` on a text column). Shared by the
91
91
  * SQL-build path and the cache-hit param-collect path so a warmed cache can
@@ -103,7 +103,7 @@ export function assertBindableEqualsOperand(value, column) {
103
103
  * cache fingerprint. The SQL-build and cache-hit param-collect paths MUST
104
104
  * enumerate object keys in this exact order: fingerprints sort keys, so two
105
105
  * where clauses with the same fields in different insertion order share one
106
- * cache entry — if build/collect iterated insertion order, the cached SQL's
106
+ * cache entry, if build/collect iterated insertion order, the cached SQL's
107
107
  * `$N` placeholders would bind the wrong values (cross-tenant-leak class).
108
108
  * Array order (OR/AND members) is positional and is never sorted.
109
109
  */
@@ -117,9 +117,14 @@ export function sortedEntries(obj) {
117
117
  // ---------------------------------------------------------------------------
118
118
  // Atomic-update / JSONB / Array / text-search / vector key sets
119
119
  // ---------------------------------------------------------------------------
120
- /** Known atomic-update operator keys — used to detect operator objects vs plain JSON values */
120
+ /** Known atomic-update operator keys, used to detect operator objects vs plain JSON values */
121
121
  export const UPDATE_OPERATOR_KEYS = new Set(['set', 'increment', 'decrement', 'multiply', 'divide']);
122
- /** Known JSONB operator keys */
122
+ /**
123
+ * Known JSONB operator keys. `stringContains` / `stringStartsWith` /
124
+ * `stringEndsWith` are appended below, once {@link JSON_STRING_OPERATORS} is
125
+ * declared: they have no `WhereOperator` counterpart, so their presence is an
126
+ * unambiguous JSON-filter signal.
127
+ */
123
128
  export const JSONB_OPERATOR_KEYS = new Set(['path', 'equals', 'contains', 'hasKey']);
124
129
  /**
125
130
  * JSON range comparison operators → SQL comparison tokens, in the FIXED order
@@ -136,23 +141,71 @@ export const JSON_RANGE_OPERATORS = {
136
141
  lt: '<',
137
142
  lte: '<=',
138
143
  };
144
+ /**
145
+ * JSON substring operators → the LIKE pattern each one builds around its
146
+ * escaped operand, in the FIXED order the build and collect paths iterate
147
+ * them. These compare the TEXT at `path` (which every one of them requires),
148
+ * so they are the JSON counterpart of the scalar `contains` / `startsWith` /
149
+ * `endsWith` operators rather than of jsonb containment.
150
+ *
151
+ * They are deliberately NOT named `contains` / `startsWith` / `endsWith`:
152
+ * `contains` on a JSON column already means whole-document containment
153
+ * (`@>`), and silently changing that would break every existing caller.
154
+ */
155
+ export const JSON_STRING_OPERATORS = {
156
+ stringContains: (escaped) => `%${escaped}%`,
157
+ stringStartsWith: (escaped) => `${escaped}%`,
158
+ stringEndsWith: (escaped) => `%${escaped}`,
159
+ };
160
+ /**
161
+ * Every key a {@link JsonFilter} may carry. Used by the strict-key check so an
162
+ * unrecognized operator is REFUSED rather than dropped.
163
+ *
164
+ * This existed as tribal knowledge spread across `buildJsonFilterClauses` and
165
+ * `collectJsonFilterParams`: each simply ignored what it did not recognize, so
166
+ * `{ path: ['title'], string_contains: 'x' }` (the Prisma spelling) compiled to
167
+ * no predicate at all and returned every row of the table. The scalar operator
168
+ * path has always thrown on an unknown key; this set is what lets the JSON path
169
+ * behave the same way.
170
+ */
171
+ for (const k of Object.keys(JSON_STRING_OPERATORS))
172
+ JSONB_OPERATOR_KEYS.add(k);
173
+ export const JSON_FILTER_KEYS = new Set([
174
+ 'path',
175
+ 'equals',
176
+ 'contains',
177
+ 'hasKey',
178
+ 'mode',
179
+ ...Object.keys(JSON_RANGE_OPERATORS),
180
+ ...Object.keys(JSON_STRING_OPERATORS),
181
+ ]);
139
182
  /**
140
183
  * Value-invariant shape fingerprint for a {@link JsonFilter}. Range operators
141
184
  * are annotated with the comparison value's kind (`#n` numeric / `#s` string)
142
- * because a numeric comparison compiles to a `::numeric` cast — a different
143
- * SQL text than the text comparison — so the two must never share a cached
144
- * SQL entry.
185
+ * because a numeric comparison compiles to a `::numeric` cast, a different
186
+ * SQL text than the text comparison, so the two must never share a cached
187
+ * SQL entry. `mode` is part of the shape for the same reason: it selects
188
+ * between `LIKE` and the dialect's case-insensitive form, which is different
189
+ * SQL text.
145
190
  */
146
191
  export function fingerprintJsonFilterShape(filter) {
147
192
  const obj = filter;
148
193
  const parts = Object.keys(obj)
149
194
  .filter((k) => obj[k] !== undefined)
150
195
  .sort()
151
- .map((k) => (k in JSON_RANGE_OPERATORS ? `${k}#${typeof obj[k] === 'number' ? 'n' : 's'}` : k));
196
+ .map((k) => {
197
+ if (k in JSON_RANGE_OPERATORS)
198
+ return `${k}#${typeof obj[k] === 'number' ? 'n' : 's'}`;
199
+ // `mode` carries its VALUE, not just its presence: 'insensitive' and any
200
+ // other spelling select different SQL, so they must not share an entry.
201
+ if (k === 'mode')
202
+ return `mode#${String(obj[k])}`;
203
+ return k;
204
+ });
152
205
  return `json(${parts.join(',')})`;
153
206
  }
154
207
  /**
155
- * JSONB operator keys that are *unique* to {@link JsonFilter} — they cannot
208
+ * JSONB operator keys that are *unique* to {@link JsonFilter}, they cannot
156
209
  * appear in any other where-filter shape, so the presence of one of these is
157
210
  * an unambiguous signal that the user meant a JSON filter. Used by the
158
211
  * strict-validation path so that `{ contains: 'foo' }` (which is also a valid
@@ -160,7 +213,7 @@ export function fingerprintJsonFilterShape(filter) {
160
213
  * set: on non-JSON columns it is a plain equality operator (`WhereOperator`),
161
214
  * so it must fall through instead of throwing.
162
215
  */
163
- export const JSONB_UNIQUE_KEYS = new Set(['path', 'hasKey']);
216
+ export const JSONB_UNIQUE_KEYS = new Set(['path', 'hasKey', ...Object.keys(JSON_STRING_OPERATORS)]);
164
217
  /** Check if a value is a JSONB filter object */
165
218
  export function isJsonFilter(value) {
166
219
  if (value === null ||
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm — Query builder barrel
2
+ * turbine-orm, Query builder barrel
3
3
  *
4
4
  * Re-exports every public symbol from the query submodules so that
5
5
  * `import { … } from './query/index.js'` is a drop-in replacement for the
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm — Query builder barrel
2
+ * turbine-orm, Query builder barrel
3
3
  *
4
4
  * Re-exports every public symbol from the query submodules so that
5
5
  * `import { … } from './query/index.js'` is a drop-in replacement for the
@@ -21,11 +21,11 @@ import type { BuilderCtx } from './where.js';
21
21
  * generation (see {@link buildRelationShape}) and consumed by the
22
22
  * transform to map key-less positional arrays back to keyed objects.
23
23
  *
24
- * - `keys` — camelCase field names in emitted array position, INCLUDING nested
24
+ * - `keys`, camelCase field names in emitted array position, INCLUDING nested
25
25
  * relation slots (a nested relation occupies one more position after the
26
26
  * scalar columns, in `sortedEntries(with)` order).
27
- * - `nested` — sub-shape for each key in `keys` that is itself a relation slot.
28
- * - `cardinality` — `'one'` (belongsTo/hasOne, a single positional array or
27
+ * - `nested`, sub-shape for each key in `keys` that is itself a relation slot.
28
+ * - `cardinality`, `'one'` (belongsTo/hasOne, a single positional array or
29
29
  * null) vs `'many'` (an array of positional arrays).
30
30
  */
31
31
  export interface RelationShape {
@@ -58,7 +58,7 @@ export declare function collectRelationSubqueryParams(qi: BuilderCtx, relDef: Re
58
58
  * Value-shape fingerprint for a single orderBy entry, so two queries whose
59
59
  * ORDER BY differs only in nulls placement, vector metric, or relation-count
60
60
  * vs relation-column never collide on one cached SQL string. Captures the
61
- * SQL-shaping bits (direction, nulls, metric, relation keys) — never values.
61
+ * SQL-shaping bits (direction, nulls, metric, relation keys), never values.
62
62
  */
63
63
  export declare function orderByEntryFingerprint(qi: BuilderCtx, d: unknown, targetTable?: string): string;
64
64
  export declare function buildOrderBy(qi: BuilderCtx, orderBy: OrderByClause, params?: unknown[], lateralSink?: string[]): string;
@@ -70,7 +70,7 @@ export declare function buildOrderBy(qi: BuilderCtx, orderBy: OrderByClause, par
70
70
  export declare function isRelationOrderByValue(_qi: BuilderCtx, value: unknown): boolean;
71
71
  /**
72
72
  * Render the ` NULLS FIRST` / ` NULLS LAST` suffix for a column ordering.
73
- * Only PostgreSQL and SQLite support the `NULLS FIRST/LAST` grammar — on any
73
+ * Only PostgreSQL and SQLite support the `NULLS FIRST/LAST` grammar, on any
74
74
  * other engine a caller asking for explicit nulls placement gets a clear
75
75
  * {@link UnsupportedFeatureError} (E017) instead of broken SQL.
76
76
  */
@@ -225,7 +225,7 @@ export declare function collectManyToManyTargetGlobalFilter(qi: BuilderCtx, relD
225
225
  /**
226
226
  * Param-collect mirror of {@link buildRelationCountExpr}'s global-filter
227
227
  * params (hasMany direct filter, or manyToMany EXISTS-on-target). Only pushes
228
- * when a filter applies — no-op otherwise.
228
+ * when a filter applies, no-op otherwise.
229
229
  */
230
230
  export declare function collectRelationCountParams(qi: BuilderCtx, relDef: RelationDef, params: unknown[]): void;
231
231
  export declare function getCamelDateFields(qi: BuilderCtx, table: string, meta: TableMetadata): Set<string>;
@@ -277,7 +277,7 @@ export declare function makeNestedParser(qi: BuilderCtx, withClause: WithClause,
277
277
  /**
278
278
  * Return a shallow copy of a top-level row with each relation column decoded
279
279
  * from its positional array(s) into the object representation. Only relation
280
- * columns are positional — base scalar columns stay object-keyed — so the
280
+ * columns are positional, base scalar columns stay object-keyed, so the
281
281
  * result is exactly what the object encoding would have handed parseNestedRow.
282
282
  */
283
283
  export declare function decodePositionalRelations(qi: BuilderCtx, row: Record<string, unknown>, shapes: Record<string, RelationShape>): Record<string, unknown>;
@@ -319,7 +319,7 @@ export interface FlattenNode {
319
319
  alias: string;
320
320
  /** Alias of the real target table inside the derived table (`f0s`). */
321
321
  srcAlias: string;
322
- /** Target-side correlation columns (provably unique — see {@link provableUniqueTargetKey}). */
322
+ /** Target-side correlation columns (provably unique, see {@link provableUniqueTargetKey}). */
323
323
  keyColumns: string[];
324
324
  /**
325
325
  * Match discriminator. A top-level node projects the constant `1` (its
@@ -394,7 +394,7 @@ export declare function provableUniqueTargetKey(relDef: RelationDef, targetMeta:
394
394
  * it emits today, down to the cache key).
395
395
  *
396
396
  * The plan is a pure function of the schema, the `with` clause shape and
397
- * `includePii` — never of any bound value — so the build path, the cache-hit
397
+ * `includePii`, never of any bound value, so the build path, the cache-hit
398
398
  * param-collect path and the row assembler can each recompute it and agree.
399
399
  */
400
400
  export declare function planFlattenWith(qi: BuilderCtx, table: string, withClause: WithClause, includePii?: boolean): FlattenPlan | null;