turbine-orm 0.81.0 → 0.90.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 (295) hide show
  1. package/README.md +26 -25
  2. package/dist/checkout.d.ts +27 -2
  3. package/dist/checkout.js +75 -4
  4. package/dist/cjs/checkout.d.ts +27 -2
  5. package/dist/cjs/checkout.js +76 -3
  6. package/dist/cjs/cli/code-first-tags.d.ts +62 -0
  7. package/dist/cjs/cli/code-first-tags.js +124 -0
  8. package/dist/cjs/cli/compile-query.js +9 -0
  9. package/dist/cjs/cli/config.d.ts +35 -6
  10. package/dist/cjs/cli/config.js +17 -1
  11. package/dist/cjs/cli/destructive.d.ts +7 -4
  12. package/dist/cjs/cli/destructive.js +224 -20
  13. package/dist/cjs/cli/error-catalog.js +36 -0
  14. package/dist/cjs/cli/error-text.d.ts +15 -0
  15. package/dist/cjs/cli/error-text.js +40 -0
  16. package/dist/cjs/cli/index.d.ts +62 -4
  17. package/dist/cjs/cli/index.js +568 -108
  18. package/dist/cjs/cli/local-server-guard.d.ts +53 -0
  19. package/dist/cjs/cli/local-server-guard.js +119 -0
  20. package/dist/cjs/cli/local-server-output.d.ts +41 -0
  21. package/dist/cjs/cli/local-server-output.js +103 -0
  22. package/dist/cjs/cli/mcp.d.ts +8 -0
  23. package/dist/cjs/cli/mcp.js +121 -32
  24. package/dist/cjs/cli/migrate.d.ts +113 -5
  25. package/dist/cjs/cli/migrate.js +270 -21
  26. package/dist/cjs/cli/observe.d.ts +6 -1
  27. package/dist/cjs/cli/observe.js +85 -55
  28. package/dist/cjs/cli/pii-predicate-guard.d.ts +43 -23
  29. package/dist/cjs/cli/pii-predicate-guard.js +98 -37
  30. package/dist/cjs/cli/prisma-resolve.js +17 -13
  31. package/dist/cjs/cli/prisma-schema.js +12 -4
  32. package/dist/cjs/cli/studio-ui.generated.js +1 -1
  33. package/dist/cjs/cli/studio.d.ts +14 -2
  34. package/dist/cjs/cli/studio.js +244 -69
  35. package/dist/cjs/cli/ui.d.ts +1 -1
  36. package/dist/cjs/client.d.ts +558 -85
  37. package/dist/cjs/client.js +1270 -395
  38. package/dist/cjs/connection-guard.d.ts +13 -0
  39. package/dist/cjs/connection-guard.js +28 -0
  40. package/dist/cjs/connection-url.d.ts +51 -16
  41. package/dist/cjs/connection-url.js +67 -34
  42. package/dist/cjs/diagnostics.d.ts +81 -0
  43. package/dist/cjs/diagnostics.js +147 -0
  44. package/dist/cjs/dialect.d.ts +449 -20
  45. package/dist/cjs/dialect.js +117 -76
  46. package/dist/cjs/engine-config.d.ts +7 -6
  47. package/dist/cjs/errors.d.ts +126 -6
  48. package/dist/cjs/errors.js +256 -13
  49. package/dist/cjs/generate.d.ts +57 -7
  50. package/dist/cjs/generate.js +635 -279
  51. package/dist/cjs/global-filters-config.d.ts +59 -0
  52. package/dist/cjs/global-filters-config.js +367 -0
  53. package/dist/cjs/index.d.ts +9 -4
  54. package/dist/cjs/index.js +10 -2
  55. package/dist/cjs/introspect.d.ts +31 -6
  56. package/dist/cjs/introspect.js +108 -50
  57. package/dist/cjs/log-codes.d.ts +12 -0
  58. package/dist/cjs/log-codes.js +77 -0
  59. package/dist/cjs/logger.d.ts +87 -0
  60. package/dist/cjs/logger.js +98 -0
  61. package/dist/cjs/mssql.d.ts +8 -5
  62. package/dist/cjs/mssql.js +352 -65
  63. package/dist/cjs/mysql.d.ts +3 -5
  64. package/dist/cjs/mysql.js +420 -70
  65. package/dist/cjs/nested-write.d.ts +3 -0
  66. package/dist/cjs/nested-write.js +179 -114
  67. package/dist/cjs/pipeline-submittable.d.ts +12 -0
  68. package/dist/cjs/pipeline-submittable.js +118 -44
  69. package/dist/cjs/pipeline.js +21 -26
  70. package/dist/cjs/powdb-introspect.d.ts +5 -2
  71. package/dist/cjs/powdb-introspect.js +20 -15
  72. package/dist/cjs/powdb-shared.d.ts +36 -5
  73. package/dist/cjs/powdb-shared.js +67 -8
  74. package/dist/cjs/powdb.d.ts +4 -0
  75. package/dist/cjs/powdb.js +183 -51
  76. package/dist/cjs/powql.d.ts +394 -51
  77. package/dist/cjs/powql.js +1829 -284
  78. package/dist/cjs/prisma-compat.d.ts +23 -15
  79. package/dist/cjs/prisma-compat.js +507 -372
  80. package/dist/cjs/query/aggregates.d.ts +17 -1
  81. package/dist/cjs/query/aggregates.js +261 -29
  82. package/dist/cjs/query/batched-loader.d.ts +25 -6
  83. package/dist/cjs/query/batched-loader.js +103 -20
  84. package/dist/cjs/query/builder.d.ts +325 -184
  85. package/dist/cjs/query/builder.js +1452 -490
  86. package/dist/cjs/query/column-ref.d.ts +61 -0
  87. package/dist/cjs/query/column-ref.js +73 -0
  88. package/dist/cjs/query/compound-unique.d.ts +21 -0
  89. package/dist/cjs/query/compound-unique.js +98 -18
  90. package/dist/cjs/query/deferred.d.ts +53 -16
  91. package/dist/cjs/query/expr.d.ts +217 -0
  92. package/dist/cjs/query/expr.js +859 -0
  93. package/dist/cjs/query/filters.d.ts +155 -53
  94. package/dist/cjs/query/filters.js +613 -76
  95. package/dist/cjs/query/index.d.ts +2 -0
  96. package/dist/cjs/query/index.js +7 -1
  97. package/dist/cjs/query/json-value.d.ts +43 -0
  98. package/dist/cjs/query/json-value.js +59 -0
  99. package/dist/cjs/query/normalize-args.d.ts +87 -0
  100. package/dist/cjs/query/normalize-args.js +527 -0
  101. package/dist/cjs/query/option-surface.d.ts +16 -4
  102. package/dist/cjs/query/option-surface.js +57 -14
  103. package/dist/cjs/query/relation-names.d.ts +14 -0
  104. package/dist/cjs/query/relation-names.js +77 -18
  105. package/dist/cjs/query/relations.d.ts +30 -3
  106. package/dist/cjs/query/relations.js +219 -63
  107. package/dist/cjs/query/row-lock.d.ts +49 -0
  108. package/dist/cjs/query/row-lock.js +138 -0
  109. package/dist/cjs/query/sql-comment.d.ts +161 -0
  110. package/dist/cjs/query/sql-comment.js +381 -0
  111. package/dist/cjs/query/stream-idle.d.ts +132 -0
  112. package/dist/cjs/query/stream-idle.js +212 -0
  113. package/dist/cjs/query/table-client.d.ts +101 -0
  114. package/dist/cjs/query/table-client.js +74 -0
  115. package/dist/cjs/query/types.d.ts +677 -298
  116. package/dist/cjs/query/utils.d.ts +105 -6
  117. package/dist/cjs/query/utils.js +426 -19
  118. package/dist/cjs/query/warn-registry.d.ts +18 -0
  119. package/dist/cjs/query/warn-registry.js +18 -0
  120. package/dist/cjs/query/where-compile.d.ts +138 -14
  121. package/dist/cjs/query/where-compile.js +513 -29
  122. package/dist/cjs/query/where.d.ts +159 -59
  123. package/dist/cjs/query/where.js +716 -246
  124. package/dist/cjs/query/writes.d.ts +112 -50
  125. package/dist/cjs/query/writes.js +1498 -200
  126. package/dist/cjs/query-events.d.ts +4 -2
  127. package/dist/cjs/query-events.js +3 -1
  128. package/dist/cjs/realtime.d.ts +7 -3
  129. package/dist/cjs/realtime.js +13 -2
  130. package/dist/cjs/schema-builder.d.ts +55 -4
  131. package/dist/cjs/schema-builder.js +382 -18
  132. package/dist/cjs/schema-metadata.d.ts +21 -1
  133. package/dist/cjs/schema-metadata.js +29 -7
  134. package/dist/cjs/schema-sql.d.ts +75 -5
  135. package/dist/cjs/schema-sql.js +497 -45
  136. package/dist/cjs/schema.d.ts +26 -5
  137. package/dist/cjs/schema.js +110 -13
  138. package/dist/cjs/sql-placeholders.d.ts +56 -0
  139. package/dist/cjs/sql-placeholders.js +204 -0
  140. package/dist/cjs/sqlite.d.ts +75 -20
  141. package/dist/cjs/sqlite.js +593 -95
  142. package/dist/cjs/supplied-pool-types.d.ts +48 -0
  143. package/dist/cjs/supplied-pool-types.js +256 -0
  144. package/dist/cjs/transaction-control.d.ts +51 -0
  145. package/dist/cjs/transaction-control.js +316 -0
  146. package/dist/cjs/transaction-state.d.ts +342 -0
  147. package/dist/cjs/transaction-state.js +707 -0
  148. package/dist/cjs/typed-sql.d.ts +37 -3
  149. package/dist/cjs/typed-sql.js +102 -8
  150. package/dist/cli/code-first-tags.d.ts +62 -0
  151. package/dist/cli/code-first-tags.js +120 -0
  152. package/dist/cli/compile-query.js +9 -0
  153. package/dist/cli/config.d.ts +35 -6
  154. package/dist/cli/config.js +16 -1
  155. package/dist/cli/destructive.d.ts +7 -4
  156. package/dist/cli/destructive.js +224 -20
  157. package/dist/cli/error-catalog.js +36 -0
  158. package/dist/cli/error-text.d.ts +15 -0
  159. package/dist/cli/error-text.js +37 -0
  160. package/dist/cli/index.d.ts +62 -4
  161. package/dist/cli/index.js +569 -112
  162. package/dist/cli/local-server-guard.d.ts +53 -0
  163. package/dist/cli/local-server-guard.js +113 -0
  164. package/dist/cli/local-server-output.d.ts +41 -0
  165. package/dist/cli/local-server-output.js +98 -0
  166. package/dist/cli/mcp.d.ts +8 -0
  167. package/dist/cli/mcp.js +121 -34
  168. package/dist/cli/migrate.d.ts +113 -5
  169. package/dist/cli/migrate.js +261 -21
  170. package/dist/cli/observe.d.ts +6 -1
  171. package/dist/cli/observe.js +84 -54
  172. package/dist/cli/pii-predicate-guard.d.ts +43 -23
  173. package/dist/cli/pii-predicate-guard.js +100 -35
  174. package/dist/cli/prisma-resolve.js +15 -11
  175. package/dist/cli/prisma-schema.js +12 -4
  176. package/dist/cli/studio-ui.generated.js +1 -1
  177. package/dist/cli/studio.d.ts +14 -2
  178. package/dist/cli/studio.js +246 -71
  179. package/dist/cli/ui.d.ts +1 -1
  180. package/dist/client.d.ts +558 -85
  181. package/dist/client.js +1275 -400
  182. package/dist/connection-guard.d.ts +13 -0
  183. package/dist/connection-guard.js +27 -0
  184. package/dist/connection-url.d.ts +51 -16
  185. package/dist/connection-url.js +63 -34
  186. package/dist/diagnostics.d.ts +81 -0
  187. package/dist/diagnostics.js +144 -0
  188. package/dist/dialect.d.ts +449 -20
  189. package/dist/dialect.js +115 -43
  190. package/dist/engine-config.d.ts +7 -6
  191. package/dist/errors.d.ts +126 -6
  192. package/dist/errors.js +252 -12
  193. package/dist/generate.d.ts +57 -7
  194. package/dist/generate.js +635 -281
  195. package/dist/global-filters-config.d.ts +59 -0
  196. package/dist/global-filters-config.js +361 -0
  197. package/dist/index.d.ts +9 -4
  198. package/dist/index.js +4 -2
  199. package/dist/introspect.d.ts +31 -6
  200. package/dist/introspect.js +106 -50
  201. package/dist/log-codes.d.ts +12 -0
  202. package/dist/log-codes.js +74 -0
  203. package/dist/logger.d.ts +87 -0
  204. package/dist/logger.js +93 -0
  205. package/dist/mssql.d.ts +8 -5
  206. package/dist/mssql.js +354 -67
  207. package/dist/mysql.d.ts +3 -5
  208. package/dist/mysql.js +423 -73
  209. package/dist/nested-write.d.ts +3 -0
  210. package/dist/nested-write.js +177 -112
  211. package/dist/pipeline-submittable.d.ts +12 -0
  212. package/dist/pipeline-submittable.js +117 -44
  213. package/dist/pipeline.js +21 -26
  214. package/dist/powdb-introspect.d.ts +5 -2
  215. package/dist/powdb-introspect.js +21 -16
  216. package/dist/powdb-shared.d.ts +36 -5
  217. package/dist/powdb-shared.js +65 -8
  218. package/dist/powdb.d.ts +4 -0
  219. package/dist/powdb.js +183 -51
  220. package/dist/powql.d.ts +394 -51
  221. package/dist/powql.js +1836 -291
  222. package/dist/prisma-compat.d.ts +23 -15
  223. package/dist/prisma-compat.js +508 -373
  224. package/dist/query/aggregates.d.ts +17 -1
  225. package/dist/query/aggregates.js +263 -32
  226. package/dist/query/batched-loader.d.ts +25 -6
  227. package/dist/query/batched-loader.js +101 -21
  228. package/dist/query/builder.d.ts +325 -184
  229. package/dist/query/builder.js +1458 -495
  230. package/dist/query/column-ref.d.ts +61 -0
  231. package/dist/query/column-ref.js +66 -0
  232. package/dist/query/compound-unique.d.ts +21 -0
  233. package/dist/query/compound-unique.js +97 -19
  234. package/dist/query/deferred.d.ts +53 -16
  235. package/dist/query/expr.d.ts +217 -0
  236. package/dist/query/expr.js +848 -0
  237. package/dist/query/filters.d.ts +155 -53
  238. package/dist/query/filters.js +597 -72
  239. package/dist/query/index.d.ts +2 -0
  240. package/dist/query/index.js +4 -0
  241. package/dist/query/json-value.d.ts +43 -0
  242. package/dist/query/json-value.js +54 -0
  243. package/dist/query/normalize-args.d.ts +87 -0
  244. package/dist/query/normalize-args.js +517 -0
  245. package/dist/query/option-surface.d.ts +16 -4
  246. package/dist/query/option-surface.js +56 -13
  247. package/dist/query/relation-names.d.ts +14 -0
  248. package/dist/query/relation-names.js +77 -19
  249. package/dist/query/relations.d.ts +30 -3
  250. package/dist/query/relations.js +220 -67
  251. package/dist/query/row-lock.d.ts +49 -0
  252. package/dist/query/row-lock.js +131 -0
  253. package/dist/query/sql-comment.d.ts +161 -0
  254. package/dist/query/sql-comment.js +374 -0
  255. package/dist/query/stream-idle.d.ts +132 -0
  256. package/dist/query/stream-idle.js +203 -0
  257. package/dist/query/table-client.d.ts +101 -0
  258. package/dist/query/table-client.js +70 -0
  259. package/dist/query/types.d.ts +677 -298
  260. package/dist/query/utils.d.ts +105 -6
  261. package/dist/query/utils.js +414 -18
  262. package/dist/query/warn-registry.d.ts +18 -0
  263. package/dist/query/warn-registry.js +18 -0
  264. package/dist/query/where-compile.d.ts +138 -14
  265. package/dist/query/where-compile.js +508 -31
  266. package/dist/query/where.d.ts +159 -59
  267. package/dist/query/where.js +711 -248
  268. package/dist/query/writes.d.ts +112 -50
  269. package/dist/query/writes.js +1496 -202
  270. package/dist/query-events.d.ts +4 -2
  271. package/dist/query-events.js +3 -1
  272. package/dist/realtime.d.ts +7 -3
  273. package/dist/realtime.js +13 -2
  274. package/dist/schema-builder.d.ts +55 -4
  275. package/dist/schema-builder.js +381 -18
  276. package/dist/schema-metadata.d.ts +21 -1
  277. package/dist/schema-metadata.js +28 -7
  278. package/dist/schema-sql.d.ts +75 -5
  279. package/dist/schema-sql.js +494 -44
  280. package/dist/schema.d.ts +26 -5
  281. package/dist/schema.js +109 -13
  282. package/dist/sql-placeholders.d.ts +56 -0
  283. package/dist/sql-placeholders.js +200 -0
  284. package/dist/sqlite.d.ts +75 -20
  285. package/dist/sqlite.js +597 -99
  286. package/dist/supplied-pool-types.d.ts +48 -0
  287. package/dist/supplied-pool-types.js +247 -0
  288. package/dist/transaction-control.d.ts +51 -0
  289. package/dist/transaction-control.js +311 -0
  290. package/dist/transaction-state.d.ts +342 -0
  291. package/dist/transaction-state.js +696 -0
  292. package/dist/typed-sql.d.ts +37 -3
  293. package/dist/typed-sql.js +100 -8
  294. package/package.json +7 -4
  295. package/skills/turbine-orm/SKILL.md +56 -6
@@ -16,10 +16,11 @@
16
16
  *
17
17
  * This module removes the drift BY CONSTRUCTION:
18
18
  * - {@link walkWhere} is the ONE enumeration. It sorts keys canonically,
19
- * skips `undefined`, dispatches the `OR`/`AND`/`NOT` combinators and
20
- * relation filters, and yields a flat, ordered {@link WhereEvent} stream.
21
- * All three consumers iterate this same stream, so their key order and
22
- * combinator structure can never diverge again.
19
+ * skips `undefined` values, applies the one combinator-shape rule
20
+ * ({@link combinatorBranches}), dispatches relation filters, and yields a
21
+ * flat, ordered {@link WhereEvent} stream. All three consumers iterate this
22
+ * same stream, so their key order, combinator structure and view of
23
+ * `undefined` can never diverge.
23
24
  * - {@link classifyScalarForSql} is the ONE scalar-shape decision the SQL
24
25
  * paths use. `buildWhereClause` and `collectWhereParams` BOTH call it with
25
26
  * the same `(rawColumn, value)`, so they always take the same branch and
@@ -37,10 +38,19 @@
37
38
  Object.defineProperty(exports, "__esModule", { value: true });
38
39
  exports.MAX_WHERE_DEPTH = void 0;
39
40
  exports.assertWhereDepth = assertWhereDepth;
41
+ exports.relationFilterValueProblem = relationFilterValueProblem;
42
+ exports.relationCardinalityProblem = relationCardinalityProblem;
43
+ exports.normalizeToOneRelationFilter = normalizeToOneRelationFilter;
44
+ exports.combinatorBranches = combinatorBranches;
40
45
  exports.walkWhere = walkWhere;
46
+ exports.foldWhere = foldWhere;
47
+ exports.metadataWhereHost = metadataWhereHost;
48
+ exports.foldWhereFor = foldWhereFor;
41
49
  exports.classifyScalarForSql = classifyScalarForSql;
42
50
  exports.fingerprintScalarToken = fingerprintScalarToken;
43
51
  const errors_js_1 = require("../errors.js");
52
+ const schema_js_1 = require("../schema.js");
53
+ const column_ref_js_1 = require("./column-ref.js");
44
54
  const filters_js_1 = require("./filters.js");
45
55
  const utils_js_1 = require("./utils.js");
46
56
  /**
@@ -83,18 +93,246 @@ function assertWhereDepth(depth, clause = 'where') {
83
93
  }
84
94
  /** True when a normalized relation filter carries at least one cardinality key. */
85
95
  function isRelationFilterObj(filterObj) {
86
- return ('some' in filterObj || 'every' in filterObj || 'none' in filterObj || 'is' in filterObj || 'isNot' in filterObj);
96
+ return filters_js_1.RELATION_FILTER_WRAPPERS.some((wrapper) => wrapper in filterObj);
87
97
  }
88
98
  /**
89
- * THE canonical WHERE enumeration. Yields events in sorted-key order (skipping
90
- * `undefined`), dispatching combinators and relation filters, so every consumer
99
+ * The refusal for a relation filter not made of the relation-filter keys. The
100
+ * SQL is built from those keys alone, so anything else beside them (a typo such
101
+ * as `sum` for `some`, or `iz` for `is`) would be dropped and the filter would
102
+ * match more rows than written; inside a global filter that is a tenancy
103
+ * condition silently not applied. Same rule and wording as the PowDB engine.
104
+ */
105
+ function relationFilterShapeError(table, key, rel, detail) {
106
+ return new errors_js_1.ValidationError(`Invalid relation filter "${key}" on "${table}": ${detail}. ` +
107
+ (rel.type === 'belongsTo' || rel.type === 'hasOne'
108
+ ? 'A to-one relation takes `{ is: {...} }`, `{ isNot: {...} }`, a bare where object, or `is: null`.'
109
+ : 'A to-many relation takes `{ some: {...} }`, `{ none: {...} }` or `{ every: {...} }`.'));
110
+ }
111
+ function unrecognizedKeys(keys) {
112
+ return `unrecognized key ${keys.map((k) => `"${k}"`).join(', ')}`;
113
+ }
114
+ /** Throws unless every key of a relation filter is a relation-filter key. */
115
+ function assertRelationFilterKeys(table, key, rel, filterObj) {
116
+ for (const k in filterObj) {
117
+ if (filters_js_1.RELATION_FILTER_WRAPPER_SET.has(k))
118
+ continue;
119
+ const unknown = Object.keys(filterObj).filter((name) => !filters_js_1.RELATION_FILTER_WRAPPER_SET.has(name));
120
+ throw relationFilterShapeError(table, key, rel, unrecognizedKeys(unknown));
121
+ }
122
+ }
123
+ /**
124
+ * Why a relation-filter key's value cannot be read, or `null` when it can.
125
+ * `some` / `none` / `every` take a where object; `is` / `isNot` take a where
126
+ * object or `null`. Any other value (`null` on a quantifier, a scalar, an
127
+ * array) used to compile to no condition at all, so a filter, or a tenant
128
+ * global filter, built from a missing value matched every row. Shared with the
129
+ * PowDB engine so both refuse the same values in the same words.
130
+ */
131
+ function relationFilterValueProblem(wrapper, value) {
132
+ if (value === undefined || (0, filters_js_1.isPlainRecord)(value))
133
+ return null;
134
+ const toOne = wrapper === 'is' || wrapper === 'isNot';
135
+ if (toOne && value === null)
136
+ return null;
137
+ return (`\`${wrapper}\` takes a where object${toOne ? ' or null' : ''}, got ${(0, filters_js_1.describeOperand)(value)}` +
138
+ (value === null ? '; `some: {}` matches rows with a related row, `none: {}` rows with none' : ''));
139
+ }
140
+ /** Throws unless every relation-filter key holds a value the compiler reads (see {@link relationFilterValueProblem}). */
141
+ function assertRelationFilterValues(table, key, rel, filterObj) {
142
+ for (const wrapper of filters_js_1.RELATION_FILTER_WRAPPERS) {
143
+ const problem = relationFilterValueProblem(wrapper, filterObj[wrapper]);
144
+ if (problem !== null)
145
+ throw relationFilterShapeError(table, key, rel, problem);
146
+ }
147
+ }
148
+ /**
149
+ * Why a relation filter reads a to-one key on a to-many relation, or `null`
150
+ * when it does not. `posts: { is: {...} }` compiled to "at least one post
151
+ * matches" and `isNot` to "no post matches", which is not what either word says
152
+ * about a list of rows, and the typed API never offers them there. The
153
+ * quantifiers on a to-one relation are left alone: with at most one related
154
+ * row, `some` / `none` / `every` each have one reading. Shared with the PowDB
155
+ * engine so both refuse the same filters in the same words.
156
+ */
157
+ function relationCardinalityProblem(relDef, filterObj) {
158
+ if (relDef.type !== 'hasMany' && relDef.type !== 'manyToMany')
159
+ return null;
160
+ for (const wrapper of ['is', 'isNot']) {
161
+ const value = filterObj[wrapper];
162
+ if (value === undefined)
163
+ continue;
164
+ const instead = value === null
165
+ ? wrapper === 'is'
166
+ ? '`none: {}` for rows with no related row'
167
+ : '`some: {}` for rows with at least one'
168
+ : wrapper === 'is'
169
+ ? '`some` (at least one related row matches) or `every` (all of them do)'
170
+ : '`none` (no related row matches)';
171
+ return `\`${wrapper}\` applies to a to-one relation and this one is to-many; write ${instead}`;
172
+ }
173
+ return null;
174
+ }
175
+ /**
176
+ * A bare where object on a to-one relation is an implicit `is`
177
+ * (`{ author: { name: 'x' } }` is `{ author: { is: { name: 'x' } } }`). To-many
178
+ * relations still require a quantifier. The one rule, used by the SQL engines'
179
+ * hosts and by PowDB.
180
+ */
181
+ function normalizeToOneRelationFilter(relDef, filterObj) {
182
+ if ((relDef.type === 'belongsTo' || relDef.type === 'hasOne') &&
183
+ !filters_js_1.RELATION_FILTER_WRAPPERS.some((wrapper) => wrapper in filterObj)) {
184
+ return { is: filterObj };
185
+ }
186
+ return filterObj;
187
+ }
188
+ /**
189
+ * THE combinator-shape rule, shared by every walker over a where (or having)
190
+ * clause: the SQL walk below, `having`, and the `globalFilters` check at
191
+ * construction. Returns the branches `value` stands for, or throws
192
+ * {@link ValidationError} (E003).
193
+ *
194
+ * - `AND` and `NOT` take one where object (a single branch) or an array of
195
+ * them; `OR` takes an array. This is Prisma's shape.
196
+ * - Anything else is refused by name: `OR: { ... }`, a string, a number,
197
+ * `null`, and an array holding one of those. A combinator the compiler
198
+ * cannot read would compile to nothing, so a mistyped filter, or a
199
+ * tenant filter built as `{ OR: { orgId } }`, would match every row.
200
+ * - An `undefined` member is absent and is dropped. When every member was
201
+ * `undefined` the combinator itself is absent and this returns `null`, the
202
+ * same reading the args seam gives it.
203
+ *
204
+ * An array written empty is returned empty; what that means is the caller's
205
+ * rule (in a where clause: `OR: []` matches nothing, `AND: []` and `NOT: []`
206
+ * apply no condition). Returns the input array by reference when it holds no
207
+ * `undefined`, so the common path allocates nothing.
208
+ */
209
+ function combinatorBranches(key, value, table, clause = 'where') {
210
+ if (value === undefined)
211
+ return null;
212
+ if (Array.isArray(value)) {
213
+ let holes = 0;
214
+ for (let i = 0; i < value.length; i++) {
215
+ const branch = value[i];
216
+ if (branch === undefined)
217
+ holes++;
218
+ else if (!(0, filters_js_1.isPlainRecord)(branch))
219
+ throw combinatorShapeError(key, table, clause, branch, i);
220
+ }
221
+ if (holes === 0)
222
+ return value;
223
+ if (holes === value.length)
224
+ return null;
225
+ return value.filter((branch) => branch !== undefined);
226
+ }
227
+ if (key !== 'OR' && (0, filters_js_1.isPlainRecord)(value))
228
+ return [value];
229
+ throw combinatorShapeError(key, table, clause, value);
230
+ }
231
+ function combinatorShapeError(key, table, clause, received, index) {
232
+ const takes = key === 'OR' ? `an array of ${clause} objects` : `a ${clause} object or an array of them`;
233
+ const got = index === undefined ? `received ${(0, filters_js_1.describeOperand)(received)}` : `element ${index} is ${(0, filters_js_1.describeOperand)(received)}`;
234
+ const hint = key === 'OR' && index === undefined && (0, filters_js_1.isPlainRecord)(received)
235
+ ? ' Wrap the object in an array: `OR: [{ ... }]`.'
236
+ : ' A combinator the query cannot read would otherwise apply no condition at all.';
237
+ return new errors_js_1.ValidationError(`\`${key}\` in the ${clause} clause on table "${table}" takes ${takes}; ${got}.${hint}`);
238
+ }
239
+ /**
240
+ * The event `OR: []` compiles to: a predicate on the table's own key that no
241
+ * row satisfies (`<pk> IN ()`, bound as an empty list), so an `OR` built from
242
+ * an empty list matches nothing, as in Prisma, instead of everything. It
243
+ * reuses the `in` operator every engine already binds as one length-independent
244
+ * placeholder, so the statement keeps one fixed shape and the three consumers
245
+ * stay in step with no special case.
246
+ */
247
+ function matchNothingEvent(host) {
248
+ const meta = host.tableMeta;
249
+ for (const column of [...(meta.primaryKey ?? []), ...(meta.allColumns ?? [])]) {
250
+ if ((0, filters_js_1.operandColumnKind)(host.getColumnPgType(column)) !== 'scalar')
251
+ continue;
252
+ return { kind: 'scalar', key: meta.reverseColumnMap?.[column] ?? column, value: { in: [] } };
253
+ }
254
+ throw new errors_js_1.ValidationError(`\`OR: []\` on table "${meta.name}" matches no row, and expressing that needs one plain column of the ` +
255
+ 'table, but its schema metadata lists none. Regenerate the client (`npx turbine generate`), or leave ' +
256
+ 'the empty `OR` out of the query.');
257
+ }
258
+ /**
259
+ * The members of an `OR` that are not absent (see {@link isAbsentBranch}),
260
+ * by reference when none is.
261
+ */
262
+ function presentOrBranches(host, branches) {
263
+ for (let i = 0; i < branches.length; i++) {
264
+ if (isAbsentBranch(host, branches[i], 0))
265
+ return branches.filter((branch) => !isAbsentBranch(host, branch, 0));
266
+ }
267
+ return branches;
268
+ }
269
+ /**
270
+ * Whether an `OR` member is absent below the args seam: it names keys, and
271
+ * every one holds `undefined`, a combinator made only of such members, or a
272
+ * relation filter whose keys all hold `undefined`. The seam removes such a
273
+ * member, so the walk drops it too and an internal caller that skips the seam
274
+ * gets the same `OR`. A member written empty (`{}`) is not absent: it applies
275
+ * no condition, which makes its `OR` TRUE.
276
+ */
277
+ function isAbsentBranch(host, branch, depth) {
278
+ if (depth > exports.MAX_WHERE_DEPTH)
279
+ return false;
280
+ let named = false;
281
+ for (const key of Object.keys(branch)) {
282
+ named = true;
283
+ const value = branch[key];
284
+ if (value === undefined)
285
+ continue;
286
+ if (key === 'OR' || key === 'AND' || key === 'NOT') {
287
+ if (Array.isArray(value)) {
288
+ if (value.length > 0 &&
289
+ value.every((m) => m === undefined || ((0, filters_js_1.isPlainRecord)(m) && isAbsentBranch(host, m, depth + 1)))) {
290
+ continue;
291
+ }
292
+ }
293
+ else if ((0, filters_js_1.isPlainRecord)(value) && isAbsentBranch(host, value, depth + 1)) {
294
+ continue;
295
+ }
296
+ return false;
297
+ }
298
+ if ((0, filters_js_1.isPlainRecord)(value) && (0, utils_js_1.resolveRelation)(host.tableMeta.relations, key) && holdsOnlyUndefined(value))
299
+ continue;
300
+ return false;
301
+ }
302
+ return named;
303
+ }
304
+ /** True for an object that names keys and holds `undefined` under every one. */
305
+ function holdsOnlyUndefined(value) {
306
+ let named = false;
307
+ for (const key of Object.keys(value)) {
308
+ if (value[key] !== undefined)
309
+ return false;
310
+ named = true;
311
+ }
312
+ return named;
313
+ }
314
+ /**
315
+ * THE canonical WHERE enumeration. Yields events in sorted-key order,
316
+ * dispatching combinators and relation filters, so every consumer
91
317
  * (fingerprint, SQL build, param collect) walks identically.
92
318
  *
93
- * Combinator SKIP rules match the historical code exactly: an `OR`/`AND` whose
94
- * value is a non-array or an empty array is skipped entirely (it contributes
95
- * neither SQL, params, nor a fingerprint token); `NOT` is always emitted.
96
- * A key that names a relation but whose value is not a `{ some/every/none/is/
97
- * isNot }` filter falls through to the scalar path, exactly as before.
319
+ * `undefined`: the args seam is the authority and removes it, with every
320
+ * object it emptied, before any of this runs. This walk is the backstop for
321
+ * callers below the seam and reads only `undefined` itself: a key holding it
322
+ * is skipped, a combinator member holding it is dropped, and a relation filter
323
+ * whose wrapper keys all hold it is absent. Values are never looked inside, so
324
+ * a JSON operand is data whatever its shape.
325
+ *
326
+ * Combinators follow {@link combinatorBranches}. `AND` and `NOT` with no
327
+ * branches apply no condition; `OR` with none matches no row
328
+ * ({@link matchNothingEvent}), and an `OR` member made only of `undefined` is
329
+ * dropped ({@link isAbsentBranch}). A `NOT` array negates each branch (`NOT a AND
330
+ * NOT b`), as Prisma does; a `NOT` object negates the object as a whole. A key
331
+ * that names a relation takes only the relation-filter keys (a to-one relation
332
+ * also takes a bare where object, read as `is`), each holding a value the
333
+ * compiler reads ({@link relationFilterValueProblem}), and anything else is
334
+ * refused (E003). Only when the key also names a column, and its object names
335
+ * no relation-filter key, is it that column's filter.
98
336
  */
99
337
  function walkWhere(host, where) {
100
338
  const events = [];
@@ -102,22 +340,35 @@ function walkWhere(host, where) {
102
340
  const value = where[key];
103
341
  if (value === undefined)
104
342
  continue;
105
- if (key === 'OR') {
106
- const arr = value;
107
- if (!Array.isArray(arr) || arr.length === 0)
343
+ if (key === 'OR' || key === 'AND' || key === 'NOT') {
344
+ const branches = combinatorBranches(key, value, host.tableMeta.name);
345
+ if (branches === null)
108
346
  continue;
109
- events.push({ kind: 'or', conditions: arr });
110
- continue;
111
- }
112
- if (key === 'AND') {
113
- const arr = value;
114
- if (!Array.isArray(arr) || arr.length === 0)
115
- continue;
116
- events.push({ kind: 'and', conditions: arr });
117
- continue;
118
- }
119
- if (key === 'NOT') {
120
- events.push({ kind: 'not', condition: value });
347
+ if (key === 'OR') {
348
+ if (branches.length === 0) {
349
+ events.push(matchNothingEvent(host));
350
+ }
351
+ else {
352
+ const present = presentOrBranches(host, branches);
353
+ // Every member held only `undefined`: the `OR` itself is absent.
354
+ if (present.length > 0)
355
+ events.push({ kind: 'or', conditions: present });
356
+ }
357
+ }
358
+ else if (branches.length === 0) {
359
+ // `AND: []` and `NOT: []` apply no condition.
360
+ }
361
+ else if (key === 'AND') {
362
+ events.push(Array.isArray(value)
363
+ ? { kind: 'and', conditions: branches }
364
+ : { kind: 'and', conditions: branches, fixedArity: true });
365
+ }
366
+ else if (!Array.isArray(value)) {
367
+ events.push({ kind: 'not', condition: branches[0] });
368
+ }
369
+ else {
370
+ events.push({ kind: 'and', conditions: branches.map((branch) => ({ NOT: branch })) });
371
+ }
121
372
  continue;
122
373
  }
123
374
  // Resolved rather than looked up, so a relation filter accepts the
@@ -126,17 +377,250 @@ function walkWhere(host, where) {
126
377
  // these events), so resolving here cannot drift between them; the emitted
127
378
  // event carries the DECLARED name, which is what reaches the fingerprint.
128
379
  const resolvedRel = (0, utils_js_1.resolveRelation)(host.tableMeta.relations, key);
129
- if (resolvedRel && typeof value === 'object' && value !== null && !Array.isArray(value)) {
130
- const filterObj = host.normalizeRelationFilter(resolvedRel.def, value);
380
+ if (resolvedRel && (0, filters_js_1.isPlainRecord)(value)) {
381
+ const present = (0, filters_js_1.dropUndefinedKeys)(value);
382
+ if (present !== value && Object.keys(present).length === 0)
383
+ continue;
384
+ const filterObj = host.normalizeRelationFilter(resolvedRel.def, present);
131
385
  if (isRelationFilterObj(filterObj)) {
386
+ assertRelationFilterKeys(host.tableMeta.name, key, resolvedRel.def, filterObj);
387
+ assertRelationFilterValues(host.tableMeta.name, key, resolvedRel.def, filterObj);
388
+ const cardinality = relationCardinalityProblem(resolvedRel.def, filterObj);
389
+ if (cardinality !== null)
390
+ throw relationFilterShapeError(host.tableMeta.name, key, resolvedRel.def, cardinality);
132
391
  events.push({ kind: 'relation', key: resolvedRel.name, relDef: resolvedRel.def, filterObj });
133
392
  continue;
134
393
  }
394
+ // A to-many relation's object names none of its quantifiers. It is a
395
+ // column filter only when the key also names a column.
396
+ if ((0, utils_js_1.resolveColumnName)(host.tableMeta, key) === undefined) {
397
+ const keys = Object.keys(filterObj);
398
+ const detail = keys.length === 0 ? 'the filter object is empty' : unrecognizedKeys(keys);
399
+ throw relationFilterShapeError(host.tableMeta.name, key, resolvedRel.def, detail);
400
+ }
135
401
  }
136
402
  events.push({ kind: 'scalar', key, value });
137
403
  }
138
404
  return events;
139
405
  }
406
+ function foldWhere(host, where, hostFor, depth = 0, options) {
407
+ assertWhereDepth(depth);
408
+ const parts = [];
409
+ for (const event of walkWhere(host, where)) {
410
+ switch (event.kind) {
411
+ case 'or':
412
+ parts.push(foldAny(event.conditions.map((cond) => foldWhere(host, cond, hostFor, depth + 1, options))));
413
+ break;
414
+ case 'and':
415
+ for (const cond of event.conditions)
416
+ parts.push(foldWhere(host, cond, hostFor, depth + 1, options));
417
+ break;
418
+ case 'not':
419
+ parts.push(negateFold(foldWhere(host, event.condition, hostFor, depth + 1, options)));
420
+ break;
421
+ case 'relation':
422
+ parts.push(foldRelationFilter(host, event.relDef, event.filterObj, hostFor, depth + 1, options));
423
+ break;
424
+ case 'scalar':
425
+ parts.push(foldScalar(host, event.key, event.value, options));
426
+ break;
427
+ }
428
+ }
429
+ return foldAll(parts);
430
+ }
431
+ /** AND of folded parts; an `'empty'` part contributes no SQL and is skipped. */
432
+ function foldAll(parts) {
433
+ let out = 'empty';
434
+ for (const part of parts) {
435
+ if (part === 'false')
436
+ return 'false';
437
+ if (part === 'row')
438
+ out = 'row';
439
+ else if (part === 'true' && out === 'empty')
440
+ out = 'true';
441
+ }
442
+ return out;
443
+ }
444
+ /**
445
+ * OR of folded branches. A branch that compiles to nothing applies no
446
+ * condition, so it is TRUE for every row and so is the `OR` (the compiler
447
+ * writes it as a constant TRUE).
448
+ */
449
+ function foldAny(parts) {
450
+ let out = 'false';
451
+ for (const part of parts) {
452
+ if (part === 'true' || part === 'empty')
453
+ return 'true';
454
+ if (part === 'row')
455
+ out = 'row';
456
+ }
457
+ return out;
458
+ }
459
+ function negateFold(fold) {
460
+ return fold === 'true' ? 'false' : fold === 'false' ? 'true' : fold;
461
+ }
462
+ /**
463
+ * A relation filter's branches, as the compiler emits them: `some` / `is` is
464
+ * EXISTS over the sub-where, `none` / `isNot` NOT EXISTS, and `every` NOT
465
+ * EXISTS over its negation (nothing at all when its sub-where is empty).
466
+ */
467
+ function foldRelationFilter(host, relDef, filterObj, hostFor, depth, options) {
468
+ const target = hostFor(relDef.to);
469
+ if (!target)
470
+ return 'empty';
471
+ const sub = (wrapper) => foldWhere(target, filterObj[wrapper], hostFor, depth, options);
472
+ // Every row has exactly one related row: a NOT NULL foreign key the engine
473
+ // enforces, to a target whose global filter cannot hide that row.
474
+ const notNull = options?.notNull;
475
+ const alwaysRelated = notNull?.foreignKeys === true &&
476
+ relDef.type === 'belongsTo' &&
477
+ !notNull.hasGlobalFilter(relDef.to) &&
478
+ (0, schema_js_1.normalizeKeyColumns)(relDef.foreignKey).every((column) => columnIsNotNull(host.tableMeta, column));
479
+ const parts = [];
480
+ for (const wrapper of filters_js_1.RELATION_FILTER_WRAPPERS) {
481
+ const value = filterObj[wrapper];
482
+ if (value === undefined)
483
+ continue;
484
+ const exists = wrapper === 'some' || wrapper === 'is';
485
+ const notExists = wrapper === 'none' || wrapper === 'isNot';
486
+ if (value === null) {
487
+ // `is: null` is NOT EXISTS and `isNot: null` EXISTS.
488
+ parts.push(alwaysRelated ? (wrapper === 'is' ? 'false' : 'true') : 'row');
489
+ continue;
490
+ }
491
+ const inner = sub(wrapper);
492
+ const matchesAll = inner === 'empty' || inner === 'true';
493
+ if (exists)
494
+ parts.push(inner === 'false' ? 'false' : alwaysRelated && matchesAll ? 'true' : 'row');
495
+ else if (notExists)
496
+ parts.push(inner === 'false' ? 'true' : alwaysRelated && matchesAll ? 'false' : 'row');
497
+ else
498
+ parts.push(matchesAll ? inner : 'row');
499
+ }
500
+ return foldAll(parts);
501
+ }
502
+ /** Whether the schema says `column` of `meta` never holds NULL. Unknown nullability is not NOT NULL. */
503
+ function columnIsNotNull(meta, column) {
504
+ for (const col of meta.columns) {
505
+ if (col.name === column)
506
+ return col.nullable === false;
507
+ }
508
+ return false;
509
+ }
510
+ /**
511
+ * A column's filter folds as an operator object holding an empty `in` /
512
+ * `notIn` list, and, with {@link WhereFoldOptions.notNull} on a NOT NULL
513
+ * column, as the shapes listed on {@link foldWhere}.
514
+ */
515
+ function foldScalar(host, key, value, options) {
516
+ if (value !== null && !(0, filters_js_1.isPlainRecord)(value))
517
+ return 'row';
518
+ const column = (0, utils_js_1.resolveColumnName)(host.tableMeta, key);
519
+ if (column === undefined)
520
+ return 'row';
521
+ const notNull = options?.notNull !== undefined && columnIsNotNull(host.tableMeta, column);
522
+ if (value === null)
523
+ return notNull ? 'false' : 'row';
524
+ const kind = classifyScalarForSql(host, column, value).kind;
525
+ if (kind === 'array')
526
+ return notNull ? foldArrayFilter(value) : 'row';
527
+ if (kind !== 'operator')
528
+ return 'row';
529
+ const json = (0, filters_js_1.operandColumnKind)(host.getColumnPgType(column)) === 'json';
530
+ return foldOperator(value, json, 0, notNull && !json ? { host, column } : undefined);
531
+ }
532
+ /** On a NOT NULL array column: `hasEvery: []` holds for every row and `hasSome: []` for none. */
533
+ function foldArrayFilter(filter) {
534
+ const parts = [];
535
+ for (const key of Object.keys(filter)) {
536
+ const value = filter[key];
537
+ if (value === undefined)
538
+ continue;
539
+ if ((key === 'hasEvery' || key === 'hasSome') && Array.isArray(value) && value.length === 0) {
540
+ parts.push(key === 'hasEvery' ? 'true' : 'false');
541
+ }
542
+ else {
543
+ parts.push('row');
544
+ }
545
+ }
546
+ return foldAll(parts);
547
+ }
548
+ /**
549
+ * The operator object's clauses, ANDed: `in: []` is FALSE and `notIn: []` TRUE
550
+ * on every engine (an empty `= ANY` / `IN (subquery)` is false even for a NULL
551
+ * column), and a `not` operator object (non-JSON columns) negates its fold.
552
+ * With `notNull` (a NOT NULL, non-JSON column): `not: null` is TRUE and
553
+ * `equals: null` FALSE, an empty `contains` / `startsWith` / `endsWith` is
554
+ * TRUE (`LIKE '%%'`), and a comparison with the column itself is TRUE for
555
+ * `equals` / `gte` / `lte` and FALSE for `not` / `gt` / `lt`.
556
+ */
557
+ function foldOperator(op, json, depth, notNull) {
558
+ const parts = [];
559
+ for (const key of Object.keys(op)) {
560
+ const value = op[key];
561
+ if (value === undefined || key === 'mode')
562
+ continue;
563
+ if ((key === 'in' || key === 'notIn') && Array.isArray(value) && value.length === 0) {
564
+ parts.push(key === 'in' ? 'false' : 'true');
565
+ }
566
+ else if (key === 'not' && !json && depth < exports.MAX_WHERE_DEPTH && (0, filters_js_1.isWhereOperator)(value)) {
567
+ parts.push(negateFold(foldOperator(value, json, depth + 1, notNull)));
568
+ }
569
+ else if (notNull) {
570
+ parts.push(foldNotNullComparison(key, value, notNull));
571
+ }
572
+ else {
573
+ parts.push('row');
574
+ }
575
+ }
576
+ return foldAll(parts);
577
+ }
578
+ const SELF_COMPARISON_TRUE = new Set(['equals', 'gte', 'lte']);
579
+ const SELF_COMPARISON_FALSE = new Set(['not', 'gt', 'lt']);
580
+ function foldNotNullComparison(key, value, at) {
581
+ if (value === null) {
582
+ if (key === 'not')
583
+ return 'true';
584
+ if (key === 'equals')
585
+ return 'false';
586
+ return 'row';
587
+ }
588
+ if ((key === 'contains' || key === 'startsWith' || key === 'endsWith') && value === '')
589
+ return 'true';
590
+ if ((0, column_ref_js_1.isColumnRef)(value) && (0, utils_js_1.resolveColumnName)(at.host.tableMeta, value.col) === at.column) {
591
+ if (SELF_COMPARISON_TRUE.has(key))
592
+ return 'true';
593
+ if (SELF_COMPARISON_FALSE.has(key))
594
+ return 'false';
595
+ }
596
+ return 'row';
597
+ }
598
+ /**
599
+ * A {@link WhereHost} over plain table metadata, for callers with no SQL
600
+ * builder (PowDB, construction-time checks). Same column-type and to-one rules
601
+ * as the SQL engines' scoped hosts.
602
+ */
603
+ function metadataWhereHost(meta) {
604
+ return {
605
+ tableMeta: meta,
606
+ normalizeRelationFilter: normalizeToOneRelationFilter,
607
+ getColumnPgType: (column) => meta.dialectTypes?.[column] ?? meta.pgTypes?.[column] ?? 'text',
608
+ isJsonColumnType: (colType) => (0, filters_js_1.operandColumnKind)(colType) === 'json',
609
+ };
610
+ }
611
+ /**
612
+ * {@link foldWhere} for `table` in `tables`, with plain metadata hosts. Also
613
+ * validates the whole clause shape at every depth (combinators, relation-filter
614
+ * keys and values), so a construction-time check can run it on a static filter.
615
+ */
616
+ function foldWhereFor(tables, table, where, options) {
617
+ const hostFor = (name) => {
618
+ const meta = Object.hasOwn(tables, name) ? tables[name] : undefined;
619
+ return meta ? metadataWhereHost(meta) : undefined;
620
+ };
621
+ const host = hostFor(table);
622
+ return host ? foldWhere(host, where, hostFor, 0, options) : 'row';
623
+ }
140
624
  /**
141
625
  * Column-aware SQL classification of a scalar WHERE value. Reproduces the SQL
142
626
  * builder's linear fall-through: a JSON/array-shaped value on a non-JSON/array