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
@@ -0,0 +1,61 @@
1
+ /**
2
+ * turbine-orm: branded column references
3
+ *
4
+ * `{ equals: col('publishedVersionId') }` compares a column against another
5
+ * column of the same row instead of against a bound value. The reference is
6
+ * written into the SQL TEXT (`"a" = "b"`), so it is the one operand that
7
+ * removes the bound value entirely.
8
+ *
9
+ * That is why it carries a brand. A plain `{ col: 'id' }` object is exactly
10
+ * what `JSON.parse` produces from a request body: were that shape a reference,
11
+ * `delete({ where: { id: body.id } })` with a body of
12
+ * `{"id":{"equals":{"col":"id"}}}` would compile to `WHERE "id" = "id"` and
13
+ * delete every row. A symbol cannot come out of `JSON.parse`, a query string, a
14
+ * form body or a `structuredClone` of parsed input, so only code can build one.
15
+ * Same reasoning as the `UNSAFE` sentinel.
16
+ *
17
+ * `Symbol.for` rather than `Symbol()` because the package ships dual ESM and
18
+ * CJS builds: a reference built by one copy must be recognized by the other.
19
+ */
20
+ /** The brand every {@link ColumnRef} carries. Registered, see the module note. */
21
+ export declare const COLUMN_REF: unique symbol;
22
+ /**
23
+ * A column-to-column comparison operand, built only by {@link col}.
24
+ *
25
+ * Accepted by `equals`, `not`, `gt`, `gte`, `lt` and `lte` on a non-JSON
26
+ * column. The referenced field resolves through the table's column map
27
+ * (camelCase or snake_case) and compiles to a quoted identifier, so no
28
+ * parameter is bound. An unknown field throws `ValidationError` (E003), and
29
+ * `mode: 'insensitive'` cannot be combined with a reference.
30
+ *
31
+ * `F` narrows the referenced name to the table's field names when the
32
+ * surrounding where clause knows the entity type.
33
+ *
34
+ * ```ts
35
+ * import { col } from 'turbine-orm';
36
+ * where: { currentVersionId: { equals: col('publishedVersionId') } }
37
+ * // → WHERE "current_version_id" = "published_version_id"
38
+ * ```
39
+ */
40
+ export interface ColumnRef<F extends string = string> {
41
+ readonly col: F;
42
+ readonly [COLUMN_REF]: true;
43
+ }
44
+ /**
45
+ * Build a column reference: `{ gt: col('startsAt') }` compares against the
46
+ * `startsAt` column of the same row.
47
+ */
48
+ export declare function col<F extends string>(name: F): ColumnRef<F>;
49
+ /** True only for a reference built by {@link col}. */
50
+ export declare function isColumnRef(value: unknown): value is ColumnRef;
51
+ /**
52
+ * True for the UNBRANDED `{ col: '<name>' }` shape, which is what parsed
53
+ * request data produces. Callers refuse it with {@link plainColumnRefMessage}
54
+ * rather than reading it as a value, because whoever wrote it almost certainly
55
+ * meant a reference.
56
+ */
57
+ export declare function isPlainColumnRefShape(value: unknown): boolean;
58
+ /** The refusal for a plain `{ col }` object at an operand position. */
59
+ export declare function plainColumnRefMessage(value: {
60
+ col: string;
61
+ }, operator: string, column: string, table: string): string;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * turbine-orm: branded column references
3
+ *
4
+ * `{ equals: col('publishedVersionId') }` compares a column against another
5
+ * column of the same row instead of against a bound value. The reference is
6
+ * written into the SQL TEXT (`"a" = "b"`), so it is the one operand that
7
+ * removes the bound value entirely.
8
+ *
9
+ * That is why it carries a brand. A plain `{ col: 'id' }` object is exactly
10
+ * what `JSON.parse` produces from a request body: were that shape a reference,
11
+ * `delete({ where: { id: body.id } })` with a body of
12
+ * `{"id":{"equals":{"col":"id"}}}` would compile to `WHERE "id" = "id"` and
13
+ * delete every row. A symbol cannot come out of `JSON.parse`, a query string, a
14
+ * form body or a `structuredClone` of parsed input, so only code can build one.
15
+ * Same reasoning as the `UNSAFE` sentinel.
16
+ *
17
+ * `Symbol.for` rather than `Symbol()` because the package ships dual ESM and
18
+ * CJS builds: a reference built by one copy must be recognized by the other.
19
+ */
20
+ import { ValidationError } from '../errors.js';
21
+ /** The brand every {@link ColumnRef} carries. Registered, see the module note. */
22
+ export const COLUMN_REF = Symbol.for('turbine-orm.columnRef');
23
+ /**
24
+ * Build a column reference: `{ gt: col('startsAt') }` compares against the
25
+ * `startsAt` column of the same row.
26
+ */
27
+ export function col(name) {
28
+ if (typeof name !== 'string' || name.length === 0) {
29
+ throw new ValidationError('col() takes the name of a field on the table, as a non-empty string.');
30
+ }
31
+ const ref = { col: name };
32
+ // Non-enumerable so a reference prints and compares as `{ col }`; a symbol
33
+ // key is never visited by `Object.keys`, so no where walker sees it either.
34
+ Object.defineProperty(ref, COLUMN_REF, { value: true, enumerable: false });
35
+ return Object.freeze(ref);
36
+ }
37
+ /** True only for a reference built by {@link col}. */
38
+ export function isColumnRef(value) {
39
+ return (typeof value === 'object' &&
40
+ value !== null &&
41
+ value[COLUMN_REF] === true &&
42
+ typeof value.col === 'string');
43
+ }
44
+ /**
45
+ * True for the UNBRANDED `{ col: '<name>' }` shape, which is what parsed
46
+ * request data produces. Callers refuse it with {@link plainColumnRefMessage}
47
+ * rather than reading it as a value, because whoever wrote it almost certainly
48
+ * meant a reference.
49
+ */
50
+ export function isPlainColumnRefShape(value) {
51
+ if (typeof value !== 'object' || value === null || Array.isArray(value) || isColumnRef(value))
52
+ return false;
53
+ const proto = Object.getPrototypeOf(value);
54
+ if (proto !== Object.prototype && proto !== null)
55
+ return false;
56
+ const keys = Object.keys(value);
57
+ return keys.length === 1 && keys[0] === 'col' && typeof value.col === 'string';
58
+ }
59
+ /** The refusal for a plain `{ col }` object at an operand position. */
60
+ export function plainColumnRefMessage(value, operator, column, table) {
61
+ const name = JSON.stringify(value.col);
62
+ return (`A plain { col: ${name} } object at '${operator}' on "${column}" for table "${table}" is not a column ` +
63
+ `reference. Build one with col(${name}) (import { col } from 'turbine-orm'): references carry a ` +
64
+ 'symbol brand so that parsed request data can never name a column. To compare against a value, pass a ' +
65
+ 'string, number, boolean, bigint, Date or Buffer.');
66
+ }
@@ -193,3 +193,24 @@ export declare function whereIdentifiesOneRow(meta: TableMetadata, where: Record
193
193
  * anything.
194
194
  */
195
195
  export declare function upsertWhereMatchesCreate(meta: TableMetadata, where: Record<string, unknown>, create: Record<string, unknown>): boolean;
196
+ /**
197
+ * A unique key, other than the ones in `pinned`, that a new row could collide
198
+ * on, or `undefined` when there is none.
199
+ *
200
+ * The question MySQL's `ON DUPLICATE KEY UPDATE` raises: it has no conflict
201
+ * target and updates the row whichever unique key collides first, so a row
202
+ * that also collides on another key can update a row the caller did not name.
203
+ * A key collides only when every member holds a value, so a key is ruled out
204
+ * when a member is NULL (written as null, or absent with no default), when
205
+ * none of its members is set (defaults are server-generated values in
206
+ * practice), or when it contains a pinned key (it then collides only where the
207
+ * pinned key does, on the same row). `undefined` is the safe answer only for
208
+ * shapes that can never collide; anything uncertain returns the key.
209
+ */
210
+ export declare function otherCollidingUniqueKey(meta: TableMetadata, pinned: readonly (readonly string[])[], stateOf: (column: string) => 'value' | 'null' | 'absent'): string[] | undefined;
211
+ /**
212
+ * {@link otherCollidingUniqueKey} for a single-row upsert: the keys `where`
213
+ * pins, against the values `create` inserts. Read only after
214
+ * {@link upsertWhereMatchesCreate} holds, so every `where` key is a column.
215
+ */
216
+ export declare function upsertCreateCollidingKey(meta: TableMetadata, where: Record<string, unknown>, create: Record<string, unknown>): string[] | undefined;
@@ -39,7 +39,7 @@
39
39
  * {@link TableMetadata} in a module-level `WeakMap`.
40
40
  */
41
41
  import { ValidationError } from '../errors.js';
42
- import { isArrayFilter, isJsonFilter, isVectorFilter, isWhereOperator } from './filters.js';
42
+ import { isArrayFilter, isJsonFilter, isPlainRecord, isVectorFilter, isWhereOperator } from './filters.js';
43
43
  import { isInternalCombinator, ownLookup, resolveColumnName, resolveRelationDef } from './utils.js';
44
44
  const syntheticKeyCache = new WeakMap();
45
45
  /**
@@ -466,32 +466,110 @@ export function upsertWhereMatchesCreate(meta, where, create) {
466
466
  }
467
467
  return keys > 0;
468
468
  }
469
+ /**
470
+ * A unique key, other than the ones in `pinned`, that a new row could collide
471
+ * on, or `undefined` when there is none.
472
+ *
473
+ * The question MySQL's `ON DUPLICATE KEY UPDATE` raises: it has no conflict
474
+ * target and updates the row whichever unique key collides first, so a row
475
+ * that also collides on another key can update a row the caller did not name.
476
+ * A key collides only when every member holds a value, so a key is ruled out
477
+ * when a member is NULL (written as null, or absent with no default), when
478
+ * none of its members is set (defaults are server-generated values in
479
+ * practice), or when it contains a pinned key (it then collides only where the
480
+ * pinned key does, on the same row). `undefined` is the safe answer only for
481
+ * shapes that can never collide; anything uncertain returns the key.
482
+ */
483
+ export function otherCollidingUniqueKey(meta, pinned, stateOf) {
484
+ for (const key of uniqueKeyNames(meta)) {
485
+ if (pinned.some((p) => p.every((c) => key.includes(c))))
486
+ continue;
487
+ let sets = false;
488
+ let nullMember = false;
489
+ for (const column of key) {
490
+ const state = stateOf(column);
491
+ if (state === 'value')
492
+ sets = true;
493
+ else if (state === 'null' || defaultsToNull(meta, column))
494
+ nullMember = true;
495
+ }
496
+ if (sets && !nullMember)
497
+ return key;
498
+ }
499
+ return undefined;
500
+ }
501
+ /** A nullable column with no default: absent from an INSERT, it is NULL. */
502
+ function defaultsToNull(meta, column) {
503
+ const col = meta.columns.find((c) => c.name === column);
504
+ return col?.nullable === true && !col.hasDefault;
505
+ }
506
+ /**
507
+ * {@link otherCollidingUniqueKey} for a single-row upsert: the keys `where`
508
+ * pins, against the values `create` inserts. Read only after
509
+ * {@link upsertWhereMatchesCreate} holds, so every `where` key is a column.
510
+ */
511
+ export function upsertCreateCollidingKey(meta, where, create) {
512
+ const whereColumns = new Set();
513
+ for (const [key, value] of Object.entries(expandCompoundUniqueWhere(meta, where))) {
514
+ if (value === undefined)
515
+ continue;
516
+ const column = resolveColumnName(meta, key);
517
+ if (column !== undefined)
518
+ whereColumns.add(column);
519
+ }
520
+ const pinned = uniqueKeyNames(meta).filter((key) => key.every((c) => whereColumns.has(c)));
521
+ const createState = new Map();
522
+ for (const [key, value] of Object.entries(create)) {
523
+ if (value === undefined)
524
+ continue;
525
+ const column = resolveColumnName(meta, key);
526
+ if (column !== undefined)
527
+ createState.set(column, value === null ? 'null' : 'value');
528
+ }
529
+ return otherCollidingUniqueKey(meta, pinned, (c) => createState.get(c) ?? 'absent');
530
+ }
469
531
  /** `{ equals: v }` and nothing else (a `mode` beside it changes the match). */
470
532
  function isBareEquals(value) {
471
- return isPlainObject(value) && Object.keys(value).length === 1 && 'equals' in value;
533
+ return isPlainRecord(value) && Object.keys(value).length === 1 && 'equals' in value;
472
534
  }
473
535
  /** Equal non-null key values; two Dates compare by instant, other objects by identity. */
474
536
  function sameKeyValue(a, b) {
475
537
  return a != null && (a instanceof Date && b instanceof Date ? a.getTime() === b.getTime() : a === b);
476
538
  }
477
- /** A bare value, or an operator object whose `equals` is a value. */
539
+ /**
540
+ * A bare scalar, or `{ equals: <scalar> }` (optionally with `mode: 'default'`).
541
+ *
542
+ * Nothing else pins a column, and the refusals are the point. A column
543
+ * reference (`{ equals: col('id') }`, or the plain `{ col }` a request body
544
+ * carries) compiles to `"id" = "id"`, which every row satisfies, so a
545
+ * `delete({ where: { id: body.id } })` would delete the table. A plain object
546
+ * is a relation filter or a sub-where, `mode: 'insensitive'` matches every
547
+ * casing of the value, and a JSON / array / vector filter narrows rather than
548
+ * identifies.
549
+ */
478
550
  function isPinnedToOneValue(value) {
479
- if (value === undefined || value === null)
480
- return false;
481
- if (isWhereOperator(value)) {
482
- const eq = value.equals;
483
- return eq !== undefined && eq !== null;
484
- }
485
- // A JSON / array / vector filter narrows, it does not identify.
486
- if (isJsonFilter(value) || isArrayFilter(value) || isVectorFilter(value))
551
+ if (isIdentityScalar(value))
552
+ return true;
553
+ if (!isPlainRecord(value))
487
554
  return false;
488
- // Anything else that is a plain object is a relation filter or a sub-where,
489
- // neither of which pins a column. Dates, Buffers and primitives are values.
490
- return !isPlainObject(value);
555
+ const keys = Object.keys(value).filter((k) => value[k] !== undefined);
556
+ const op = value;
557
+ return (keys.every((k) => k === 'equals' || (k === 'mode' && op.mode === 'default')) &&
558
+ keys.includes('equals') &&
559
+ isIdentityScalar(op.equals));
491
560
  }
492
- function isPlainObject(value) {
493
- if (typeof value !== 'object' || value === null || Array.isArray(value))
494
- return false;
495
- const proto = Object.getPrototypeOf(value);
496
- return proto === Object.prototype || proto === null;
561
+ /** A non-null scalar a unique index compares by value. */
562
+ function isIdentityScalar(value) {
563
+ switch (typeof value) {
564
+ case 'string':
565
+ case 'bigint':
566
+ case 'boolean':
567
+ return true;
568
+ case 'number':
569
+ return !Number.isNaN(value);
570
+ case 'object':
571
+ return value instanceof Date || ArrayBuffer.isView(value);
572
+ default:
573
+ return false;
574
+ }
497
575
  }
@@ -3,10 +3,13 @@
3
3
  *
4
4
  * Split from builder.ts so the class file focuses on SQL assembly / execution.
5
5
  */
6
+ import type { DiagnosticsConfig } from '../diagnostics.js';
6
7
  import type { Dialect } from '../dialect.js';
8
+ import type { TurbineLogger } from '../logger.js';
7
9
  import type { PgCompatPool, PgCompatQueryResult } from '../pg-types.js';
8
10
  import type { SchemaMetadata } from '../schema.js';
9
- import type { QueryInterface } from './builder.js';
11
+ import type { SqlCommenter, SqlCommentOptions } from './sql-comment.js';
12
+ import type { TableClient } from './table-client.js';
10
13
  import type { GlobalFilters, JsonEncoding, RelationLoadStrategy } from './types.js';
11
14
  /**
12
15
  * Runs a SQL statement and resolves its raw result. Passed to a
@@ -34,6 +37,13 @@ export interface DeferredQuery<T> {
34
37
  * rows), so the PostgreSQL path never allocates or consults it.
35
38
  */
36
39
  reselect?: (exec: ReselectExecutor) => Promise<PgCompatQueryResult>;
40
+ /**
41
+ * Set when the statement is a locking read (`lock`). Its locks last until
42
+ * the transaction ends, so a runner that executes it outside a transaction
43
+ * (an autocommit pipeline) must refuse it: the rows would be unlocked again
44
+ * before the caller saw them.
45
+ */
46
+ rowLock?: true;
37
47
  }
38
48
  /**
39
49
  * How the ORM hands back a Postgres temporal `infinity` / `-infinity`.
@@ -108,6 +118,14 @@ export interface QueryEvent {
108
118
  * batch statements are timed individually. Absent for everything else.
109
119
  */
110
120
  batch?: 'pipeline' | 'transaction';
121
+ /**
122
+ * The `sqlComment` this statement was sent with, exactly as appended
123
+ * (`/*action='findMany',model='users'*\/`). `sql` stays the commentless
124
+ * text, so events for one statement shape compare equal whatever the
125
+ * comment; the text on the wire was `${sql} ${comment}`. Absent when the
126
+ * statement carried none (no `sqlComment`, raw SQL, transaction control).
127
+ */
128
+ comment?: string;
111
129
  /**
112
130
  * Set on a READ that failed because its connection had already been closed
113
131
  * by the server, and that Turbine therefore sent once more on a fresh one.
@@ -167,21 +185,15 @@ export interface QueryInterfaceOptions {
167
185
  *
168
186
  * Default: `true`. Set to `false` as a nuclear kill switch.
169
187
  *
170
- * Dev-mode safety net: when `NODE_ENV !== 'production'`, every cache HIT is
188
+ * Safety net: {@link diagnostics}' `cacheCheck` decides whether a cache HIT is
171
189
  * cross-checked by rebuilding the SQL + params fresh and comparing them
172
190
  * against the cache-hit result, catching any drift between the fingerprint,
173
191
  * SQL-build, and param-collect paths (which has silently corrupted results
174
- * before). A mismatch throws a `ValidationError` (E003). This runs only
175
- * outside production, so it never touches the production hot path. Set the
176
- * env var `TURBINE_DISABLE_CACHE_CHECK=1` to opt out when dev traffic is
177
- * perf-sensitive.
178
- *
179
- * Production sampling: the check is off in production by default. Set
180
- * `TURBINE_CACHE_CHECK_SAMPLE` to a float in `(0,1]` to re-verify that
181
- * fraction of cache hits under real load (e.g. `0.001` for one in a
182
- * thousand). A sampled mismatch logs `console.error` once per distinct
183
- * fingerprint AND throws the same `ValidationError` (E003). `0`, unset, or an
184
- * unparseable value keeps the check fully off.
192
+ * before). Every hit in development, none in production, or a sampled
193
+ * fraction (`{ sample: 0.001 }`); a sampled mismatch is logged once per
194
+ * distinct fingerprint as well as thrown. Independently of that, every MISS
195
+ * compares the built and collected params and every HIT checks the param
196
+ * count, in every mode; any mismatch is an `InvariantError` (E020).
185
197
  */
186
198
  sqlCache?: boolean;
187
199
  /**
@@ -195,6 +207,12 @@ export interface QueryInterfaceOptions {
195
207
  * per table).
196
208
  */
197
209
  sqlCacheSize?: number;
210
+ /**
211
+ * The client's `streamIdleTimeoutMs`: how long a cursor stream may wait for
212
+ * its consumer before Turbine closes it. Default 300000; `0` disables. See
213
+ * `FindManyStreamArgs.idleTimeoutMs`, which overrides it per call.
214
+ */
215
+ streamIdleTimeoutMs?: number;
198
216
  /** SQL dialect implementation. Defaults to PostgreSQL. */
199
217
  dialect?: Dialect;
200
218
  /**
@@ -288,6 +306,25 @@ export interface QueryInterfaceOptions {
288
306
  * {@link GlobalFilters}.
289
307
  */
290
308
  globalFilters?: GlobalFilters;
309
+ /**
310
+ * `diagnostics`: per-query advice and the SQL-cache self-check. TurbineClient
311
+ * resolves it once and hands the same resolved object to every interface it
312
+ * builds (resolving that again returns it by reference). An interface
313
+ * constructed directly may pass any `diagnostics` config, or none, and
314
+ * resolves it at construction, never per call.
315
+ */
316
+ diagnostics?: DiagnosticsConfig;
317
+ /** Where this interface's log lines go. Defaults to the console logger. */
318
+ logger?: TurbineLogger;
319
+ /**
320
+ * `sqlComment`: sqlcommenter key/values appended to every statement this
321
+ * interface sends (see `TurbineConfig.sqlComment`), resolved at
322
+ * construction. TurbineClient resolves it once instead and hands every
323
+ * interface the same commenter as `_sqlCommenter`.
324
+ */
325
+ sqlComment?: SqlCommentOptions;
326
+ /** @internal The client's resolved `sqlComment`, shared by its accessors; wins over `sqlComment`. */
327
+ _sqlCommenter?: SqlCommenter;
291
328
  /** @internal Set by TransactionClient, signals that this QI runs inside an active transaction. */
292
329
  _txScoped?: boolean;
293
330
  /** @internal Callback from TurbineClient for query event emission. */
@@ -295,9 +332,9 @@ export interface QueryInterfaceOptions {
295
332
  /**
296
333
  * @internal Factory that builds the per-table query interface. Defaults to
297
334
  * `new QueryInterface` (the SQL path). Non-SQL backends (PowDB) supply a
298
- * factory returning a structurally-compatible interface that generates their
299
- * own query language instead of SQL. The SQL dialects never set this, so their
335
+ * factory returning a {@link TableClient}, which generates their own query
336
+ * language instead of SQL. The SQL dialects never set this, so their
300
337
  * `table()` behavior is byte-identical.
301
338
  */
302
- queryInterfaceFactory?: (pool: PgCompatPool, table: string, schema: SchemaMetadata, middlewares: MiddlewareFn[], options: QueryInterfaceOptions) => QueryInterface<object>;
339
+ queryInterfaceFactory?: (pool: PgCompatPool, table: string, schema: SchemaMetadata, middlewares: MiddlewareFn[], options: QueryInterfaceOptions) => TableClient<object>;
303
340
  }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * turbine-orm: SQL expressions in update data.
3
+ *
4
+ * `expr.greatest(col('viewCount'), 100)` and friends are branded node objects,
5
+ * and `expr.excluded('events')` names the incoming row of an upsert. Both are
6
+ * written into the SQL TEXT, so both carry a symbol brand: `JSON.parse` cannot
7
+ * produce one, so request data can never become an expression (the same
8
+ * reasoning as `col()` and `UNSAFE`). `Symbol.for` so the ESM and CJS copies of
9
+ * the package recognize each other's nodes. A plain look-alike object
10
+ * (`{ kind: 'greatest', args: [...] }`) is a VALUE, and on a non-JSON column
11
+ * the write path refuses it.
12
+ *
13
+ * The public types live in `query/types.ts` (`SqlExpr`, `ExcludedRef`). This
14
+ * module holds the node constructors, their validation, and
15
+ * {@link compileSetExpr}: the ONE traversal that produces an expression's SQL,
16
+ * its bound parameters and its cache fingerprint together, so the SET build,
17
+ * the cache-hit param collector and the fingerprint cannot disagree (the
18
+ * failure class behind the where-clause cache bugs).
19
+ *
20
+ * Where expressions are accepted: `update`, `updateMany`, `upsert`'s `update`,
21
+ * `createMany`'s `onConflict.update`, and nested update data (which runs
22
+ * through `update()`). Not in `create` / `createMany` data, where the write
23
+ * bind seam refuses a node.
24
+ */
25
+ import type { TableMetadata } from '../schema.js';
26
+ import { type ColumnRef } from './column-ref.js';
27
+ import type { ExcludedRef, SqlExpr } from './types.js';
28
+ import { type BuilderCtx } from './where.js';
29
+ /** The brand every {@link SqlExpr} node carries. */
30
+ export declare const SQL_EXPR: unique symbol;
31
+ /** The brand an {@link ExcludedRef} carries (the incoming row of an upsert). */
32
+ export declare const EXCLUDED_REF: unique symbol;
33
+ /**
34
+ * An operand of an expression: a JS scalar (string, number, bigint, boolean,
35
+ * Date, Buffer or null), `col('field')` for the current row's column, or
36
+ * another expression.
37
+ */
38
+ export type ExprOperand<V = unknown> = NotInferred<V> | null | ColumnRef | SqlExpr<V>;
39
+ /**
40
+ * `V` as a type the compiler does not infer from (the pre-TypeScript-5.4
41
+ * spelling of `NoInfer`, so the published declarations need no newer
42
+ * compiler). Without it a `col('x')` operand is itself a candidate for `V`,
43
+ * and `expr.greatest(col('a'), col('b'))` would be typed as producing a
44
+ * column reference. `V` comes from the expression operands and from where the
45
+ * result is assigned instead.
46
+ */
47
+ type NotInferred<V> = [V][V extends unknown ? 0 : never];
48
+ /**
49
+ * A numeric operand: a number or bigint, `col()` of a numeric column, or a
50
+ * numeric expression (`string` covers a numeric/decimal column typed as text).
51
+ * One union-typed `SqlExpr`, not a union of three, so a nested call such as
52
+ * `expr.add(col('a'), expr.mul(col('b'), 2))` infers without a type argument.
53
+ */
54
+ export type NumericOperand = number | bigint | null | ColumnRef | SqlExpr<number | bigint | string>;
55
+ /** One `expr.case` arm: when the current row matches `when`, the value is `then`. */
56
+ export interface CaseArm<V = unknown> {
57
+ /** A predicate on the current row's columns, written like `where`. No relation filters. */
58
+ readonly when: Record<string, unknown>;
59
+ readonly then: ExprOperand<V>;
60
+ }
61
+ /** The node an `expr.case` call builds. */
62
+ export interface CaseExpr<V = unknown> extends SqlExpr<V> {
63
+ readonly kind: 'case';
64
+ readonly args: readonly CaseArm<V>[];
65
+ /** The value when no arm matches; absent means NULL. */
66
+ readonly else?: ExprOperand<V>;
67
+ }
68
+ /**
69
+ * Builders for SQL expressions in update data. Each returns a frozen, branded
70
+ * node; the SQL is generated per engine when the update compiles.
71
+ *
72
+ * ```ts
73
+ * import { col, expr } from 'turbine-orm';
74
+ * await db.posts.update({
75
+ * where: { id },
76
+ * data: {
77
+ * viewCount: expr.greatest(col('viewCount'), 100),
78
+ * score: expr.add(col('score'), expr.mul(col('bonus'), 2)),
79
+ * editedAt: expr.now(),
80
+ * },
81
+ * });
82
+ * ```
83
+ */
84
+ export declare const expr: Readonly<{
85
+ /** `a + b`. Numbers, bigints, numeric columns and numeric expressions. */
86
+ add<V extends number | bigint | string = number>(a: NumericOperand, b: NumericOperand): SqlExpr<V>;
87
+ /** `a - b`. */
88
+ sub<V extends number | bigint | string = number>(a: NumericOperand, b: NumericOperand): SqlExpr<V>;
89
+ /** `a * b`. */
90
+ mul<V extends number | bigint | string = number>(a: NumericOperand, b: NumericOperand): SqlExpr<V>;
91
+ /**
92
+ * `a / b`. When both operands are integers (an integer column, an integer
93
+ * number or bigint, or an integer expression) the division truncates toward
94
+ * zero on every engine, as the `divide` update operator does on an integer
95
+ * column. With a non-integer operand it is a decimal division.
96
+ */
97
+ div<V extends number | bigint | string = number>(a: NumericOperand, b: NumericOperand): SqlExpr<V>;
98
+ /** The first non-NULL operand (2 to 8). */
99
+ coalesce<V>(a: ExprOperand<V>, b: ExprOperand<V>, ...rest: ExprOperand<V>[]): SqlExpr<V>;
100
+ /**
101
+ * The largest operand (2 to 8), IGNORING NULLs on every engine (PostgreSQL's
102
+ * meaning; MySQL and SQLite are rewritten to match). NULL only when every
103
+ * operand is NULL.
104
+ */
105
+ greatest<V>(a: ExprOperand<V>, b: ExprOperand<V>, ...rest: ExprOperand<V>[]): SqlExpr<V>;
106
+ /** The smallest operand (2 to 8), ignoring NULLs on every engine. */
107
+ least<V>(a: ExprOperand<V>, b: ExprOperand<V>, ...rest: ExprOperand<V>[]): SqlExpr<V>;
108
+ /**
109
+ * `CASE WHEN ... THEN ... ELSE ... END`. Each arm's `when` is a predicate on
110
+ * the current row's columns written like `where` (no relation filters); the
111
+ * first matching arm wins. Without `otherwise` an unmatched row gets NULL.
112
+ */
113
+ case<V>(arms: readonly CaseArm<V>[], otherwise?: ExprOperand<V>): SqlExpr<V>;
114
+ /**
115
+ * The database server's current time, stored in the zone convention Turbine
116
+ * reads with: a zone-less `timestamp` / `date` gets the UTC wall clock (unless
117
+ * the client sets `utcTimestamps: false`). On PostgreSQL it is the
118
+ * TRANSACTION's start time; on MySQL, the statement's. Temporal columns only.
119
+ */
120
+ now(): SqlExpr<Date>;
121
+ /**
122
+ * The incoming row's value for `field`: inside `upsert`'s `update` (the value
123
+ * `create` supplies) and `createMany`'s `onConflict.update` (each row's own
124
+ * value). Refused anywhere else.
125
+ */
126
+ excluded<F extends string>(field: F): ExcludedRef<F>;
127
+ }>;
128
+ /** True only for a node built by {@link expr} (any loaded copy of the package). */
129
+ export declare function isSqlExpr(value: unknown): value is SqlExpr;
130
+ /** True only for `expr.excluded(...)`. */
131
+ export declare function isExcludedRef(value: unknown): value is ExcludedRef;
132
+ /**
133
+ * True for an UNBRANDED object shaped like a node (`{ kind: 'greatest', args }`),
134
+ * which is what parsed request data produces. The write path refuses it on a
135
+ * non-JSON column rather than storing it as JSON text.
136
+ */
137
+ export declare function isPlainExprShape(value: unknown): boolean;
138
+ /** The refusal for {@link isPlainExprShape}. */
139
+ export declare function plainExprShapeMessage(table: string, key: string, kind: string): string;
140
+ /** The refusal for a node at a position that takes values only (create data, `{ set }`). */
141
+ export declare function exprNotAllowedMessage(table: string, key: string, value: SqlExpr | ColumnRef): string;
142
+ /**
143
+ * Refuse an expression assigned to a column it cannot target in this release
144
+ * (json/jsonb and array columns). Shared by the walker and the single-row
145
+ * upsert's `excluded` resolution, so both paths refuse the same columns.
146
+ */
147
+ export declare function assertExprTarget(qi: BuilderCtx, key: string, value: SqlExpr | ColumnRef, operation: string): string;
148
+ /** What {@link compileSetExpr} needs from its caller (the write module). */
149
+ export interface ExprCompileEnv {
150
+ readonly qi: BuilderCtx;
151
+ /** The `data` key being assigned. */
152
+ readonly targetKey: string;
153
+ /** The operation, for messages: `update`, `updateMany`, `upsert`, `createMany`. */
154
+ readonly operation: string;
155
+ /**
156
+ * Bind one value leaf (coerced for the target column) and return its
157
+ * placeholder, cast included. `arithmetic` is the leaf's class when it is an
158
+ * arithmetic operand, where PostgreSQL types it from its own value rather than
159
+ * from the target column.
160
+ */
161
+ bind(value: unknown, arithmetic?: 'int' | 'dec'): string;
162
+ /** Render a column of the target row. */
163
+ column(column: string): string;
164
+ /** Render the incoming row's column, or absent where `expr.excluded` has no meaning. */
165
+ readonly excluded?: ((column: string) => string) | undefined;
166
+ /** The columns the incoming rows supply; `expr.excluded` must name one. */
167
+ readonly insertColumns?: ReadonlySet<string> | undefined;
168
+ /** Compile a `case` arm's predicate (already pruned and validated) against the target row. */
169
+ predicate(when: Record<string, unknown>): string;
170
+ }
171
+ /** One compiled expression. */
172
+ export interface CompiledSetExpr {
173
+ /** The right-hand side of the assignment. */
174
+ readonly sql: string;
175
+ /**
176
+ * Value-free shape: node kinds, resolved column names, value-leaf positions,
177
+ * each arm's predicate fingerprint and the `now()` rendering. Never a value.
178
+ */
179
+ readonly fingerprint: string;
180
+ /** Columns of the target row the expression reads (MySQL assignment order). */
181
+ readonly reads: ReadonlySet<string>;
182
+ }
183
+ /**
184
+ * Compile one expression (or `col()` reference) assigned to `env.targetKey`.
185
+ * The single traversal: SQL, params (through `env.bind`, in emission order)
186
+ * and fingerprint come out of one walk.
187
+ *
188
+ * What an expression stores is the same on every engine, by one rule:
189
+ * arithmetic is integer arithmetic when both operands are integers (integer
190
+ * columns, integer numbers, bigints, integer expressions), and integer
191
+ * division truncates toward zero, as `{ divide }` does on an integer column.
192
+ * Any other numeric operand makes it decimal. A non-integer result is refused
193
+ * on an integer column, because the engines disagree on converting one (round
194
+ * half away from zero, round half to even, truncate, or store it unconverted).
195
+ * The class is decided from the operands' types and the bound values' kinds,
196
+ * both part of the fingerprint, so the SQL text never depends on anything the
197
+ * cache key does not capture.
198
+ */
199
+ export declare function compileSetExpr(env: ExprCompileEnv, value: SqlExpr | ColumnRef): CompiledSetExpr;
200
+ /**
201
+ * Rewrite every `expr.excluded(f)` in a single-row upsert's `update` to the
202
+ * value `create` supplies for `f`. The incoming row of a one-row upsert IS
203
+ * `create`, so the statement path, the lookup-first path (a plain update) and
204
+ * a nested upsert store the same row. A top-level reference becomes
205
+ * `{ set: value }`, which writes the value exactly as a plain value would be
206
+ * written, on every column type (json and array columns take `set` too). A
207
+ * reference inside an expression becomes a value operand, checked as the
208
+ * node's constructor checks one; the expression is then compiled by the
209
+ * update, with the column-type rules every expression gets. Needs only the
210
+ * table's name and metadata, so the nested-write engine shares it. Returns
211
+ * `update` by reference when it holds no reference.
212
+ */
213
+ export declare function resolveExcludedFromCreate(target: {
214
+ readonly table: string;
215
+ readonly tableMeta: TableMetadata;
216
+ }, update: Record<string, unknown> | undefined, create: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
217
+ export {};