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
@@ -19,7 +19,9 @@
19
19
  * impossible to string-concatenate a value into the query through this API.
20
20
  * This is the whole point of the tagged-template shape: the literal segments
21
21
  * are frozen by the compiler (`TemplateStringsArray`), and the only way to
22
- * get a runtime value into the query is via `${...}`, which we bind.
22
+ * get a runtime value into the query is via `${...}`, which we bind. A
23
+ * plain array passed where the template belongs is refused at runtime
24
+ * ({@link assertTemplateStrings}).
23
25
  *
24
26
  * 3. **Rows are returned as-is (no snake→camel mapping).** This matches the
25
27
  * existing `client.raw()` behavior: a typed raw query is a literal escape
@@ -49,6 +51,7 @@
49
51
  */
50
52
  import type { PgCompatPool } from './client.js';
51
53
  import { type Dialect } from './dialect.js';
54
+ import { type TurbineLogger } from './logger.js';
52
55
  import type { QueryEvent } from './query/deferred.js';
53
56
  /**
54
57
  * Build a `(sql, params)` pair from a tagged-template invocation.
@@ -61,10 +64,37 @@ import type { QueryEvent } from './query/deferred.js';
61
64
  *
62
65
  * Exported for unit testing the parameterization invariant without a database.
63
66
  */
64
- export declare function buildTypedSql(strings: TemplateStringsArray, values: readonly unknown[], dialect?: Pick<Dialect, 'paramPlaceholder'>): {
67
+ export declare function buildTypedSql(strings: TemplateStringsArray, values: readonly unknown[], dialect?: Pick<Dialect, 'paramPlaceholder'>, api?: string): {
65
68
  sql: string;
66
69
  params: unknown[];
67
70
  };
71
+ /**
72
+ * Refuse a "template" that did not come from a tagged-template call.
73
+ *
74
+ * Only the static segments of a template reach the SQL text, and that is safe
75
+ * because the compiler wrote them. A plain array passed in their place (a
76
+ * JSON-parsed request field, a string split by hand) puts its text into the
77
+ * SQL with nothing escaped. A real `TemplateStringsArray` is frozen and carries
78
+ * a frozen `raw` array of the same length; `JSON.parse` and array literals
79
+ * produce neither.
80
+ *
81
+ * `unsafeApi` names the method that takes statement text instead, for the
82
+ * error message.
83
+ */
84
+ export declare function assertTemplateStrings(strings: unknown, api: string, unsafeApi?: string): asserts strings is TemplateStringsArray;
85
+ /**
86
+ * Raw-statement params with every `Date` (in arrays too) bound as UTC text,
87
+ * for a client reading zone-less temporal types as UTC (`utcTimestamps`).
88
+ *
89
+ * node-postgres renders a Date in the process's zone with its offset. A
90
+ * `timestamptz` keeps the instant either way, but a `timestamp` or `date`
91
+ * drops the offset and stores the local wall time, which then reads back
92
+ * shifted by the process offset. UTC text stores the instant's UTC fields,
93
+ * which is what a UTC read returns. Returns `params` itself when it holds no
94
+ * Date. An Invalid Date is refused (E003) instead of reaching the server as
95
+ * unparseable text.
96
+ */
97
+ export declare function utcDateParams(params: unknown[], api: string): unknown[];
68
98
  /**
69
99
  * A pending typed raw SQL query. Implements the thenable contract, so it can be
70
100
  * `await`ed directly to get `T[]`, or refined via `.one()` / `.scalar()` first.
@@ -100,6 +130,8 @@ export declare class TypedSqlQuery<T extends Record<string, unknown>> implements
100
130
  * built outside a client, which then emits nothing, as before.
101
131
  */
102
132
  private readonly onQuery?;
133
+ /** Where the `logging` preview line goes. Defaults to the console logger. */
134
+ private readonly logger;
103
135
  constructor(pool: PgCompatPool, sql: string, params: unknown[], logging: boolean,
104
136
  /**
105
137
  * Runs the execution under the owning client's `errorMessages` mode. Passed
@@ -120,7 +152,9 @@ export declare class TypedSqlQuery<T extends Record<string, unknown>> implements
120
152
  * with model `'$raw'` and action `'sql'`. Absent for a `TypedSqlQuery`
121
153
  * built outside a client, which then emits nothing, as before.
122
154
  */
123
- onQuery?: ((event: QueryEvent) => void) | undefined);
155
+ onQuery?: ((event: QueryEvent) => void) | undefined,
156
+ /** Where the `logging` preview line goes. Defaults to the console logger. */
157
+ logger?: TurbineLogger);
124
158
  /** Execute and return all rows. Internal; powers `then`, `one`, and `scalar`. */
125
159
  private run;
126
160
  /**
@@ -20,7 +20,9 @@
20
20
  * impossible to string-concatenate a value into the query through this API.
21
21
  * This is the whole point of the tagged-template shape: the literal segments
22
22
  * are frozen by the compiler (`TemplateStringsArray`), and the only way to
23
- * get a runtime value into the query is via `${...}`, which we bind.
23
+ * get a runtime value into the query is via `${...}`, which we bind. A
24
+ * plain array passed where the template belongs is refused at runtime
25
+ * ({@link assertTemplateStrings}).
24
26
  *
25
27
  * 3. **Rows are returned as-is (no snake→camel mapping).** This matches the
26
28
  * existing `client.raw()` behavior: a typed raw query is a literal escape
@@ -51,8 +53,11 @@
51
53
  Object.defineProperty(exports, "__esModule", { value: true });
52
54
  exports.TypedSqlQuery = void 0;
53
55
  exports.buildTypedSql = buildTypedSql;
56
+ exports.assertTemplateStrings = assertTemplateStrings;
57
+ exports.utcDateParams = utcDateParams;
54
58
  const dialect_js_1 = require("./dialect.js");
55
59
  const errors_js_1 = require("./errors.js");
60
+ const logger_js_1 = require("./logger.js");
56
61
  const query_events_js_1 = require("./query-events.js");
57
62
  /**
58
63
  * Build a `(sql, params)` pair from a tagged-template invocation.
@@ -65,11 +70,10 @@ const query_events_js_1 = require("./query-events.js");
65
70
  *
66
71
  * Exported for unit testing the parameterization invariant without a database.
67
72
  */
68
- function buildTypedSql(strings, values, dialect = dialect_js_1.postgresDialect) {
69
- // The tagged-template API guarantees `strings.length === values.length + 1`.
70
- // Guard the directly-callable (exported) surface so a mismatched call can't
71
- // silently desync placeholders from params (pg would reject it, but fail
72
- // loudly here instead).
73
+ function buildTypedSql(strings, values, dialect = dialect_js_1.postgresDialect, api = 'sql') {
74
+ assertTemplateStrings(strings, api);
75
+ // A tagged template always has `strings.length === values.length + 1`; a
76
+ // hand-built frozen one may not, and would desync placeholders from params.
73
77
  if (strings.length !== values.length + 1) {
74
78
  throw new errors_js_1.ValidationError(`sql template segment/value count mismatch: ${strings.length} segments, ${values.length} values.`);
75
79
  }
@@ -82,6 +86,92 @@ function buildTypedSql(strings, values, dialect = dialect_js_1.postgresDialect)
82
86
  }
83
87
  return { sql, params: values.slice() };
84
88
  }
89
+ /**
90
+ * Refuse a "template" that did not come from a tagged-template call.
91
+ *
92
+ * Only the static segments of a template reach the SQL text, and that is safe
93
+ * because the compiler wrote them. A plain array passed in their place (a
94
+ * JSON-parsed request field, a string split by hand) puts its text into the
95
+ * SQL with nothing escaped. A real `TemplateStringsArray` is frozen and carries
96
+ * a frozen `raw` array of the same length; `JSON.parse` and array literals
97
+ * produce neither.
98
+ *
99
+ * `unsafeApi` names the method that takes statement text instead, for the
100
+ * error message.
101
+ */
102
+ function assertTemplateStrings(strings, api, unsafeApi = 'rawUnsafe') {
103
+ if (Array.isArray(strings) && Object.isFrozen(strings)) {
104
+ const raw = strings.raw;
105
+ if (Array.isArray(raw) && Object.isFrozen(raw) && raw.length === strings.length) {
106
+ if (strings.every((s) => typeof s === 'string'))
107
+ return;
108
+ throw new errors_js_1.ValidationError(`${api}: a segment of the template contains an invalid escape sequence, so it has no text. ` +
109
+ 'Fix the escape (write a literal backslash as `\\\\`).');
110
+ }
111
+ }
112
+ const received = Array.isArray(strings) ? 'a plain array' : strings === null ? 'null' : typeof strings;
113
+ throw new errors_js_1.ValidationError(`${api} must be called as a tagged template, for example ${api}\`SELECT * FROM users WHERE id = \${id}\`, ` +
114
+ `so that every interpolated value is sent as a bound parameter. It received ${received} instead, whose ` +
115
+ 'text would reach the SQL unescaped. To run statement text built at runtime (DDL, a migration file), use ' +
116
+ `\`${unsafeApi}(text, ...values)\` and keep untrusted input out of the text.`);
117
+ }
118
+ /**
119
+ * A `Date` in the form node-postgres sends under `parseInputDatesAsUTC`:
120
+ * `YYYY-MM-DDTHH:MM:SS.mmm+00:00`, with ` BC` for years before 1.
121
+ */
122
+ function utcDateText(date) {
123
+ const pad = (n, width = 2) => String(n).padStart(width, '0');
124
+ let year = date.getUTCFullYear();
125
+ const bc = year < 1;
126
+ if (bc)
127
+ year = 1 - year;
128
+ return (`${pad(year, 4)}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}` +
129
+ `T${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}` +
130
+ `.${pad(date.getUTCMilliseconds(), 3)}+00:00${bc ? ' BC' : ''}`);
131
+ }
132
+ function utcDateValue(value, api, position, nested) {
133
+ if (value instanceof Date) {
134
+ if (Number.isNaN(value.getTime())) {
135
+ throw new errors_js_1.ValidationError(`${api}: parameter $${position} ${nested ? 'contains' : 'is'} an Invalid Date, which has no SQL value. ` +
136
+ 'Pass a valid Date, or null for SQL NULL.');
137
+ }
138
+ return utcDateText(value);
139
+ }
140
+ if (!Array.isArray(value))
141
+ return value;
142
+ let out;
143
+ for (let i = 0; i < value.length; i++) {
144
+ const converted = utcDateValue(value[i], api, position, true);
145
+ if (converted !== value[i]) {
146
+ out ??= value.slice();
147
+ out[i] = converted;
148
+ }
149
+ }
150
+ return out ?? value;
151
+ }
152
+ /**
153
+ * Raw-statement params with every `Date` (in arrays too) bound as UTC text,
154
+ * for a client reading zone-less temporal types as UTC (`utcTimestamps`).
155
+ *
156
+ * node-postgres renders a Date in the process's zone with its offset. A
157
+ * `timestamptz` keeps the instant either way, but a `timestamp` or `date`
158
+ * drops the offset and stores the local wall time, which then reads back
159
+ * shifted by the process offset. UTC text stores the instant's UTC fields,
160
+ * which is what a UTC read returns. Returns `params` itself when it holds no
161
+ * Date. An Invalid Date is refused (E003) instead of reaching the server as
162
+ * unparseable text.
163
+ */
164
+ function utcDateParams(params, api) {
165
+ let out;
166
+ for (let i = 0; i < params.length; i++) {
167
+ const converted = utcDateValue(params[i], api, i + 1, false);
168
+ if (converted !== params[i]) {
169
+ out ??= params.slice();
170
+ out[i] = converted;
171
+ }
172
+ }
173
+ return out ?? params;
174
+ }
85
175
  /**
86
176
  * A pending typed raw SQL query. Implements the thenable contract, so it can be
87
177
  * `await`ed directly to get `T[]`, or refined via `.one()` / `.scalar()` first.
@@ -99,6 +189,7 @@ class TypedSqlQuery {
99
189
  logging;
100
190
  runScoped;
101
191
  onQuery;
192
+ logger;
102
193
  constructor(pool, sql, params, logging,
103
194
  /**
104
195
  * Runs the execution under the owning client's `errorMessages` mode. Passed
@@ -119,19 +210,22 @@ class TypedSqlQuery {
119
210
  * with model `'$raw'` and action `'sql'`. Absent for a `TypedSqlQuery`
120
211
  * built outside a client, which then emits nothing, as before.
121
212
  */
122
- onQuery) {
213
+ onQuery,
214
+ /** Where the `logging` preview line goes. Defaults to the console logger. */
215
+ logger = logger_js_1.consoleLogger) {
123
216
  this.pool = pool;
124
217
  this.sql = sql;
125
218
  this.params = params;
126
219
  this.logging = logging;
127
220
  this.runScoped = runScoped;
128
221
  this.onQuery = onQuery;
222
+ this.logger = logger;
129
223
  }
130
224
  /** Execute and return all rows. Internal; powers `then`, `one`, and `scalar`. */
131
225
  run() {
132
226
  return this.runScoped(async () => {
133
227
  if (this.logging) {
134
- console.log(`[turbine] Typed SQL: ${this.sql.trim().substring(0, 120)}...`);
228
+ (0, logger_js_1.logTo)(this.logger, 'debug', `Typed SQL: ${this.sql.trim().substring(0, 120)}...`, { code: 'typed-sql' });
135
229
  }
136
230
  const result = await (0, query_events_js_1.runReportedStatement)(this.onQuery, (0, query_events_js_1.rawIdentity)('sql'), this.sql, this.params, () => this.pool.query(this.sql, this.params), errors_js_1.wrapPgError);
137
231
  return result.rows;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Code-first column tags for `turbine generate`.
3
+ *
4
+ * `pii`, `updatedAt`, `maxLength` and `tsType` exist only in the `defineSchema()`
5
+ * file: the database has no column for them, so a client generated from
6
+ * introspection alone returns PII by default, never stamps `updatedAt`, and
7
+ * types a `jsonb` column as `unknown`. `generate` reads the schema file and
8
+ * overlays its tags onto the introspected schema with the two functions here.
9
+ *
10
+ * Lives under `cli/` because only the CLI uses it. `schemaDefToMetadata` (the
11
+ * library path) shares the tag list through `declaredTagsOf`.
12
+ */
13
+ import { type SchemaMetadata } from '../schema.js';
14
+ import type { SchemaDef } from '../schema-builder.js';
15
+ import { type CodeFirstColumnTags } from '../schema-metadata.js';
16
+ /** Declared tags per table (database name), per column (database name). */
17
+ export type CodeFirstTagMap = Map<string, Map<string, CodeFirstColumnTags>>;
18
+ /**
19
+ * Collect the code-first-only tags a {@link SchemaDef} declares, keyed by the
20
+ * database table and column they land on (the same `camelToSnake(field)` rule
21
+ * {@link schemaDefToMetadata} uses). Tables and columns that declare no tag are
22
+ * absent from the result.
23
+ *
24
+ * Introspection can not see these flags, because the database has no column for
25
+ * them. `turbine generate` overlays them onto the introspected schema with
26
+ * {@link applyCodeFirstColumnTags}.
27
+ */
28
+ export declare function extractCodeFirstColumnTags(def: SchemaDef): CodeFirstTagMap;
29
+ /** What {@link applyCodeFirstColumnTags} changed. */
30
+ export interface AppliedColumnTags {
31
+ /** Tables that received at least one tag. */
32
+ tables: number;
33
+ pii: number;
34
+ updatedAt: number;
35
+ maxLength: number;
36
+ /**
37
+ * Declared `tsType`s, as generator `columnTypes` entries
38
+ * (`'<table>.<column>'` by database name). Returned rather than written onto
39
+ * the schema so they go through the generator's own key and type-text
40
+ * validation, the same as a `columnTypes` entry in the config file.
41
+ */
42
+ columnTypes: Record<string, string>;
43
+ }
44
+ /**
45
+ * Overlay declared tags onto an introspected schema, in place, matching tables
46
+ * and columns by their database names.
47
+ *
48
+ * `pii` and `updatedAt` are set wherever declared. `maxLength` only fills a gap:
49
+ * the live column's own length wins when introspection read one.
50
+ *
51
+ * A `pii` or `updatedAt` tag that names a table or column the database does not
52
+ * have is an error, never a skip: a typo or an un-pushed schema would otherwise
53
+ * generate a client that silently returns the column it was tagged to hide.
54
+ * Every problem is reported at once. `maxLength` is also in the DDL, so a
55
+ * missing table or column only costs that fill-in. `inScope` lets the caller say
56
+ * that a missing table was deliberately left out of the client (an `exclude`
57
+ * filter), which is not an error either.
58
+ */
59
+ export declare function applyCodeFirstColumnTags(schema: SchemaMetadata, tags: CodeFirstTagMap, options?: {
60
+ source?: string;
61
+ inScope?: (table: string) => boolean;
62
+ }): AppliedColumnTags;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Code-first column tags for `turbine generate`.
3
+ *
4
+ * `pii`, `updatedAt`, `maxLength` and `tsType` exist only in the `defineSchema()`
5
+ * file: the database has no column for them, so a client generated from
6
+ * introspection alone returns PII by default, never stamps `updatedAt`, and
7
+ * types a `jsonb` column as `unknown`. `generate` reads the schema file and
8
+ * overlays its tags onto the introspected schema with the two functions here.
9
+ *
10
+ * Lives under `cli/` because only the CLI uses it. `schemaDefToMetadata` (the
11
+ * library path) shares the tag list through `declaredTagsOf`.
12
+ */
13
+ import { ValidationError } from '../errors.js';
14
+ import { printableCatalogName } from '../introspect.js';
15
+ import { closestName } from '../query/utils.js';
16
+ import { camelToSnake } from '../schema.js';
17
+ import { declaredTagsOf } from '../schema-metadata.js';
18
+ /**
19
+ * Collect the code-first-only tags a {@link SchemaDef} declares, keyed by the
20
+ * database table and column they land on (the same `camelToSnake(field)` rule
21
+ * {@link schemaDefToMetadata} uses). Tables and columns that declare no tag are
22
+ * absent from the result.
23
+ *
24
+ * Introspection can not see these flags, because the database has no column for
25
+ * them. `turbine generate` overlays them onto the introspected schema with
26
+ * {@link applyCodeFirstColumnTags}.
27
+ */
28
+ export function extractCodeFirstColumnTags(def) {
29
+ const out = new Map();
30
+ for (const tableDef of Object.values(def.tables)) {
31
+ for (const [field, config] of Object.entries(tableDef.columns)) {
32
+ const tags = declaredTagsOf(config);
33
+ if (Object.keys(tags).length === 0)
34
+ continue;
35
+ let columns = out.get(tableDef.name);
36
+ if (!columns) {
37
+ columns = new Map();
38
+ out.set(tableDef.name, columns);
39
+ }
40
+ columns.set(camelToSnake(field), tags);
41
+ }
42
+ }
43
+ return out;
44
+ }
45
+ /** A tag the database has no column for, so only the schema file can supply it. */
46
+ function isCodeFirstOnly(tags) {
47
+ return tags.pii === true || tags.updatedAt === true;
48
+ }
49
+ /**
50
+ * Overlay declared tags onto an introspected schema, in place, matching tables
51
+ * and columns by their database names.
52
+ *
53
+ * `pii` and `updatedAt` are set wherever declared. `maxLength` only fills a gap:
54
+ * the live column's own length wins when introspection read one.
55
+ *
56
+ * A `pii` or `updatedAt` tag that names a table or column the database does not
57
+ * have is an error, never a skip: a typo or an un-pushed schema would otherwise
58
+ * generate a client that silently returns the column it was tagged to hide.
59
+ * Every problem is reported at once. `maxLength` is also in the DDL, so a
60
+ * missing table or column only costs that fill-in. `inScope` lets the caller say
61
+ * that a missing table was deliberately left out of the client (an `exclude`
62
+ * filter), which is not an error either.
63
+ */
64
+ export function applyCodeFirstColumnTags(schema, tags, options = {}) {
65
+ const applied = { tables: 0, pii: 0, updatedAt: 0, maxLength: 0, columnTypes: {} };
66
+ const problems = [];
67
+ for (const [tableName, columns] of tags) {
68
+ const shown = printableCatalogName(tableName);
69
+ if (!Object.hasOwn(schema.tables, tableName)) {
70
+ if (options.inScope && !options.inScope(tableName))
71
+ continue;
72
+ if (![...columns.values()].some(isCodeFirstOnly))
73
+ continue;
74
+ const near = closestName(tableName, Object.keys(schema.tables));
75
+ problems.push(`table "${shown}" is not in the database${near ? ` (did you mean "${printableCatalogName(near)}"?)` : ''}`);
76
+ continue;
77
+ }
78
+ const table = schema.tables[tableName];
79
+ let touched = false;
80
+ for (const [columnName, declared] of columns) {
81
+ const column = table.columns.find((c) => c.name === columnName);
82
+ if (!column) {
83
+ if (!isCodeFirstOnly(declared))
84
+ continue;
85
+ const near = closestName(columnName, table.columns.map((c) => c.name));
86
+ problems.push(`column "${printableCatalogName(columnName)}" on table "${shown}" is not in the database` +
87
+ `${near ? ` (did you mean "${printableCatalogName(near)}"?)` : ''}`);
88
+ continue;
89
+ }
90
+ if (declared.pii) {
91
+ column.pii = true;
92
+ applied.pii++;
93
+ touched = true;
94
+ }
95
+ if (declared.updatedAt) {
96
+ column.updatedAt = true;
97
+ applied.updatedAt++;
98
+ touched = true;
99
+ }
100
+ if (declared.maxLength !== undefined && column.maxLength === undefined) {
101
+ column.maxLength = declared.maxLength;
102
+ applied.maxLength++;
103
+ touched = true;
104
+ }
105
+ if (declared.tsType !== undefined) {
106
+ applied.columnTypes[`${tableName}.${columnName}`] = declared.tsType;
107
+ touched = true;
108
+ }
109
+ }
110
+ if (touched)
111
+ applied.tables++;
112
+ }
113
+ if (problems.length > 0) {
114
+ const from = options.source ? ` in ${options.source}` : '';
115
+ throw new ValidationError(`Cannot apply the column tags declared${from}: ${problems.join('; ')}. ` +
116
+ 'Run `turbine push` so the database has them, or fix the name in the schema file. ' +
117
+ 'Generating without the tags would return the columns they mark as PII.');
118
+ }
119
+ return applied;
120
+ }
@@ -101,6 +101,9 @@ export const COLUMN_NAMING_ARG_KEYS = [
101
101
  '_avg',
102
102
  '_min',
103
103
  '_max',
104
+ // Keys are column names and the result is a stored value (or one equal to it),
105
+ // the same reason `_min` / `_max` are listed.
106
+ '_percentile',
104
107
  ];
105
108
  /**
106
109
  * Does this args object carry a key that names a column, AT ANY DEPTH?
@@ -147,6 +150,12 @@ export function carriesColumnNamingArg(args, depth = 0) {
147
150
  // ---------------------------------------------------------------------------
148
151
  // Compile
149
152
  // ---------------------------------------------------------------------------
153
+ /**
154
+ * Any non-array object, class instances included. Deliberately NOT the shared
155
+ * `isPlainRecord`: the builder walks a `with` (and a `where`) by its own keys
156
+ * whatever its prototype, so a report that skipped a class-instance `with`
157
+ * would list no relation while the SQL loads one.
158
+ */
150
159
  function isPlainObject(value) {
151
160
  return typeof value === 'object' && value !== null && !Array.isArray(value);
152
161
  }
@@ -24,12 +24,22 @@ export interface TurbineCliConfig {
24
24
  * to hand-edit every call site. A typo here is an error, not a silent no-op.
25
25
  */
26
26
  relationNames?: Record<string, Record<string, string>>;
27
+ /**
28
+ * TypeScript type overrides for generated row and input types, as
29
+ * `{ '<table>.<column>': '<type>' }` with database names. The main use is a
30
+ * `json` / `jsonb` column, which otherwise generates as `unknown`:
31
+ * `{ 'users.settings': '{ theme: string; beta?: boolean }' }`. The type text
32
+ * is emitted verbatim. A key naming no table or column is an error, not a
33
+ * silent no-op. A code-first column's `tsType` feeds the same map.
34
+ */
35
+ columnTypes?: Record<string, string>;
27
36
  /**
28
37
  * Extension for the generated `index.ts` sibling imports (F3):
29
- * `'js'` (`./types.js`), `'none'` (`./types`), or `'auto'` (default:
30
- * tsconfig-detected, falling back to `'js'`).
38
+ * `'js'` (`./types.js`), `'ts'` (`./types.ts`, for Node's native type
39
+ * stripping), `'none'` (`./types`), or `'auto'` (default: tsconfig-detected,
40
+ * falling back to `'js'`; never picks `'ts'`).
31
41
  */
32
- importExtension?: 'js' | 'none' | 'auto';
42
+ importExtension?: 'js' | 'ts' | 'none' | 'auto';
33
43
  /**
34
44
  * Keep raw database column names as generated field names (snake_case)
35
45
  * instead of camelCase (F4). Opt-in; default `false`.
@@ -54,7 +64,11 @@ export interface TurbineCliConfig {
54
64
  * are set.
55
65
  */
56
66
  seed?: string;
57
- /** Schema builder file path (for push command) */
67
+ /**
68
+ * The `defineSchema()` file (default `./turbine/schema.ts`): what `push` applies,
69
+ * and where `generate` reads the `pii` / `updatedAt` / `maxLength` tags the
70
+ * database cannot store.
71
+ */
58
72
  schemaFile?: string;
59
73
  /**
60
74
  * Database adapter for PostgreSQL-compatible databases that need
@@ -159,14 +173,22 @@ export interface ResolvedConfig {
159
173
  migrationsDir: string;
160
174
  seedFile?: string;
161
175
  schemaFile: string;
176
+ /**
177
+ * True when `--schema-file` or the config `schemaFile` key named the file, as
178
+ * opposed to the `./turbine/schema.ts` default. A named file that is missing is
179
+ * an error; a missing default just means a database-first project.
180
+ */
181
+ schemaFileConfigured?: boolean;
162
182
  /** Resolved generator import-extension mode (F3). */
163
- importExtension: 'js' | 'none' | 'auto';
183
+ importExtension: 'js' | 'ts' | 'none' | 'auto';
164
184
  /** Resolved keep-column-names generator flag (F4). */
165
185
  keepColumnNames: boolean;
166
186
  /** Resolved opt-out of the unique-FK → hasOne introspection flip (F2). */
167
187
  legacyToManyUniques: boolean;
168
188
  /** Resolved derived-relation rename map, or undefined when none is declared. */
169
189
  relationNames?: Record<string, Record<string, string>>;
190
+ /** Column type overrides for the generator, or undefined when none is declared. */
191
+ columnTypes?: Record<string, string>;
170
192
  }
171
193
  export interface CliOverrides {
172
194
  url?: string;
@@ -176,10 +198,17 @@ export interface CliOverrides {
176
198
  schemaFile?: string;
177
199
  include?: string[];
178
200
  exclude?: string[];
179
- importExtension?: 'js' | 'none' | 'auto';
201
+ importExtension?: 'js' | 'ts' | 'none' | 'auto';
180
202
  keepColumnNames?: boolean;
181
203
  legacyToManyUniques?: boolean;
182
204
  }
205
+ /**
206
+ * The first candidate that is a non-blank string. An empty or whitespace-only
207
+ * value counts as unset, because an exported-but-empty variable (`DATABASE_URL=`
208
+ * in a shell profile or a `.env` template) is how "not configured" is usually
209
+ * spelled, and `??` would let it beat a real value further down the chain.
210
+ */
211
+ export declare function firstNonEmpty(...candidates: Array<string | undefined>): string | undefined;
183
212
  /**
184
213
  * Merge config file values with CLI overrides and env vars.
185
214
  * Priority: CLI flags > env vars > config file > defaults.
@@ -149,23 +149,38 @@ export function findConfigFile(cwd) {
149
149
  }
150
150
  return null;
151
151
  }
152
+ /**
153
+ * The first candidate that is a non-blank string. An empty or whitespace-only
154
+ * value counts as unset, because an exported-but-empty variable (`DATABASE_URL=`
155
+ * in a shell profile or a `.env` template) is how "not configured" is usually
156
+ * spelled, and `??` would let it beat a real value further down the chain.
157
+ */
158
+ export function firstNonEmpty(...candidates) {
159
+ for (const value of candidates) {
160
+ if (typeof value === 'string' && value.trim() !== '')
161
+ return value;
162
+ }
163
+ return undefined;
164
+ }
152
165
  /**
153
166
  * Merge config file values with CLI overrides and env vars.
154
167
  * Priority: CLI flags > env vars > config file > defaults.
155
168
  */
156
169
  export function resolveConfig(fileConfig, overrides) {
157
170
  return {
158
- url: overrides.url ?? process.env.DATABASE_URL ?? fileConfig.url ?? '',
171
+ url: firstNonEmpty(overrides.url, process.env.DATABASE_URL, fileConfig.url) ?? '',
159
172
  out: overrides.out ?? fileConfig.out ?? './generated/turbine',
160
173
  schema: overrides.schema ?? fileConfig.schema ?? 'public',
161
174
  include: overrides.include ?? fileConfig.include ?? [],
162
175
  relationNames: fileConfig.relationNames,
176
+ columnTypes: fileConfig.columnTypes,
163
177
  exclude: overrides.exclude ?? fileConfig.exclude ?? [],
164
178
  migrationsDir: fileConfig.migrationsDir ?? './turbine/migrations',
165
179
  // `seedFile` is canonical (what the docs and `turbine init` use); `seed` is a
166
180
  // back-compat alias kept working for configs scaffolded before 0.50.
167
181
  seedFile: fileConfig.seedFile ?? fileConfig.seed,
168
182
  schemaFile: overrides.schemaFile ?? fileConfig.schemaFile ?? './turbine/schema.ts',
183
+ schemaFileConfigured: (overrides.schemaFile ?? fileConfig.schemaFile) !== undefined,
169
184
  importExtension: overrides.importExtension ?? fileConfig.importExtension ?? 'auto',
170
185
  keepColumnNames: overrides.keepColumnNames ?? fileConfig.keepColumnNames ?? false,
171
186
  legacyToManyUniques: overrides.legacyToManyUniques ?? fileConfig.legacyToManyUniques ?? false,
@@ -19,16 +19,19 @@
19
19
  * the optional-`COLUMN` shorthand (`ALTER TABLE t DROP email`), data-modifying
20
20
  * CTEs (`WITH d AS (DELETE ...) SELECT ...`), `MERGE ... THEN DELETE`, dynamic
21
21
  * SQL inside a `DO`/function body, an `UPDATE` whose only WHERE sits inside a
22
- * subquery (which restricts nothing), a `DROP ... CASCADE` of a non-table
23
- * object (which takes dependent COLUMNS with it), a detached partition, and an
24
- * `EXPLAIN ANALYZE` of any of the above (which really executes it).
22
+ * subquery (which restricts nothing) or is a constant-true predicate (`WHERE
23
+ * true`, `WHERE 1 = 1`), a `DROP ... CASCADE` of a non-table object (which takes
24
+ * dependent COLUMNS with it), a detached partition, an `ON CONFLICT DO UPDATE`
25
+ * with no `WHERE` (it rewrites every conflicting row), a `PREPARE`d destructive
26
+ * statement, `lo_unlink` (it deletes large objects), and an `EXPLAIN ANALYZE` of
27
+ * any of the above (which really executes it).
25
28
  *
26
29
  * The lexing, "where does a statement end" and "which characters are code",
27
30
  * is NOT done here: it lives in `sql-statements.ts` and is shared with the
28
31
  * migration runner. Two lexers is how this guard came to disagree with the
29
32
  * executor about what a file contained (see that module's header).
30
33
  */
31
- export type DestructiveKind = 'drop-table' | 'drop-schema' | 'drop-database' | 'drop-owned' | 'drop-matview' | 'drop-column' | 'drop-cascade' | 'detach-partition' | 'truncate' | 'delete' | 'update-without-where' | 'alter-column-type' | 'merge-delete' | 'rename' | 'rewrite-rule' | 'dynamic-destructive' | 'dynamic-unclassified';
34
+ export type DestructiveKind = 'drop-table' | 'drop-schema' | 'drop-database' | 'drop-owned' | 'drop-matview' | 'drop-column' | 'drop-cascade' | 'detach-partition' | 'truncate' | 'delete' | 'update-without-where' | 'overwrite-on-conflict' | 'large-object-unlink' | 'alter-column-type' | 'merge-delete' | 'rename' | 'rewrite-rule' | 'dynamic-destructive' | 'dynamic-unclassified';
32
35
  export interface DestructiveStatement {
33
36
  /** The offending SQL statement (trimmed, possibly long, display truncated) */
34
37
  statement: string;