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
@@ -220,16 +220,42 @@ function isTsSpace(code) {
220
220
  code === 0xfeff);
221
221
  }
222
222
  /**
223
- * Does this column map to PowDB's native `json` document type? A Postgres
223
+ * The labels of the enum type this column is declared as, or `undefined` when
224
+ * it is not an enum column of `enums`.
225
+ *
226
+ * An enum column carries its TYPE NAME as `dialectType` / `pgType` and a
227
+ * `tsType` that says nothing useful (`unknown` from `defineSchema`, a union of
228
+ * the labels from introspection), so the column's own metadata cannot answer
229
+ * the question: the schema's `enums` map is the only authority. PowDB has no
230
+ * enum type, so such a column is a `str` holding the label text, which is what
231
+ * PostgreSQL stores and returns. Without this the tsType heuristic in
232
+ * {@link isJsonColumn} declared a `defineSchema` enum column as `json`, and
233
+ * writing a label failed as invalid JSON.
234
+ */
235
+ export function powdbEnumLabels(col, enums) {
236
+ if (!enums || col.isArray)
237
+ return undefined;
238
+ const type = col.dialectType ?? col.pgType;
239
+ return type !== undefined && Object.hasOwn(enums, type) ? enums[type] : undefined;
240
+ }
241
+ /**
242
+ * Does this column map to PowDB's native `json` document type? An enum column
243
+ * of `enums` never does (see {@link powdbEnumLabels}). A Postgres
224
244
  * `json`/`jsonb` type (via `dialectType`/`pgType`) is authoritative; otherwise
225
245
  * the tsType heuristic (`Record<…>`, `object`, `unknown`, an object/array
226
246
  * literal) that the four scalar branches do not claim. Array columns never map
227
247
  * to json, a PowDB array only exists INSIDE a json document, so a Postgres
228
248
  * array column has no PowDB shape and still throws in {@link powqlColumnType}.
249
+ *
250
+ * `enums` is optional for callers outside Turbine; every call inside the
251
+ * engine passes the schema's map (a source scan in fx-powdb-r2.test.ts
252
+ * checks it).
229
253
  */
230
- export function isJsonColumn(col) {
254
+ export function isJsonColumn(col, enums) {
231
255
  if (col.isArray)
232
256
  return false;
257
+ if (powdbEnumLabels(col, enums))
258
+ return false;
233
259
  const dbType = (col.dialectType ?? col.pgType ?? '').toLowerCase();
234
260
  if (dbType === 'json' || dbType === 'jsonb')
235
261
  return true;
@@ -247,14 +273,17 @@ export function isJsonColumn(col) {
247
273
  * - `Date` → `int` (epoch micros) - `boolean` → `bool`
248
274
  * - integral `number`/`bigint` → `int` - fractional `number` → `float`
249
275
  * - JSON / object columns → `json` (native PowDB document type, ≥ 0.12)
276
+ * - an enum column of `enums` → `str` (the label text)
250
277
  * - everything else (incl. UUID/PK strings) → `str`
251
278
  * Array (non-json) and bytes columns throw, they have no PowDB equivalent.
252
279
  */
253
- export function powqlColumnType(col) {
280
+ export function powqlColumnType(col, enums) {
254
281
  if (col.isArray) {
255
282
  throw new ValidationError(`Column "${col.name}" is an array, PowDB has no array type. Arrays are unsupported on the PowDB backend.`);
256
283
  }
257
- if (isJsonColumn(col))
284
+ if (powdbEnumLabels(col, enums))
285
+ return 'str';
286
+ if (isJsonColumn(col, enums))
258
287
  return 'json';
259
288
  const ts = baseTsType(col.tsType);
260
289
  if (ts === 'Date')
@@ -272,6 +301,23 @@ export function powqlColumnType(col) {
272
301
  }
273
302
  return 'str';
274
303
  }
304
+ /**
305
+ * Is this a `numeric` / `decimal` / `money` column that PowDB stores as `str`?
306
+ *
307
+ * A Postgres-sourced decimal surfaces as a JS string (so its digits survive),
308
+ * and PowDB has no decimal type, so the column is DDL'd as PowQL `str`. Every
309
+ * comparison, ordering and aggregate the engine runs on it is then a TEXT
310
+ * operation, which is why the query generator refuses those on such a column.
311
+ */
312
+ export function isPowdbDecimalTextColumn(col, enums) {
313
+ // An enum type's NAME is free text (`money_kind`), so an enum column is
314
+ // answered before the type-name match below can mistake it for a decimal.
315
+ if (powdbEnumLabels(col, enums))
316
+ return false;
317
+ if (col.isArray || isJsonColumn(col, enums) || baseTsType(col.tsType) !== 'string')
318
+ return false;
319
+ return /numeric|decimal|money/.test((col.dialectType ?? col.pgType ?? '').toLowerCase());
320
+ }
275
321
  /** Heuristic: does this numeric column hold fractional values (→ PowQL `float`)? */
276
322
  function isFloatColumn(col) {
277
323
  const t = (col.dialectType ?? col.pgType ?? '').toLowerCase();
@@ -467,9 +513,12 @@ export function quotePowqlDotted(name) {
467
513
  * Every PowDB value arrives as a string; NULL arrives as the bareword `"null"`.
468
514
  * Metadata resolves the `"null"` ambiguity for nullable non-string columns.
469
515
  */
470
- export function coerceValue(raw, col) {
516
+ export function coerceValue(raw, col, enums) {
517
+ // An enum column holds its label as text, read as a string column is.
518
+ if (powdbEnumLabels(col, enums))
519
+ return raw === 'null' && col.nullable ? null : raw;
471
520
  const ts = baseTsType(col.tsType);
472
- const json = isJsonColumn(col);
521
+ const json = isJsonColumn(col, enums);
473
522
  // NULL bareword: unambiguous for non-string columns; for `str` we cannot tell a
474
523
  // literal "null" from SQL NULL, so a nullable str of value "null" reads as null.
475
524
  // For a `json` column the bareword `null` (a legacy-wire rendering shared by an
@@ -552,7 +601,7 @@ export function coerceNativeValue(value, col) {
552
601
  * cells arrive pre-typed and go through {@link coerceNativeValue} (see F3).
553
602
  * Callers on the native transport pass `this.pool.capabilities.nativeRaw`.
554
603
  */
555
- export function rowToEntity(raw, meta, native = false) {
604
+ export function rowToEntity(raw, meta, native = false, enums) {
556
605
  const byName = new Map(meta.columns.map((c) => [c.name, c]));
557
606
  const out = {};
558
607
  for (const snake of Object.keys(raw)) {
@@ -565,8 +614,16 @@ export function rowToEntity(raw, meta, native = false) {
565
614
  else if (native) {
566
615
  out[field] = coerceNativeValue(value, col);
567
616
  }
617
+ else if (typeof value === 'string') {
618
+ out[field] = coerceValue(value, col, enums);
619
+ }
620
+ else if ((typeof value === 'number' || typeof value === 'bigint') && isDateColumn(col)) {
621
+ // A Date column's micros as a number cell: the Date it stores, as on the
622
+ // other wires, never the raw count (which a filter would then refuse).
623
+ out[field] = new Date(Number(value) / 1000);
624
+ }
568
625
  else {
569
- out[field] = typeof value === 'string' ? coerceValue(value, col) : value;
626
+ out[field] = value;
570
627
  }
571
628
  }
572
629
  return out;
package/dist/powdb.d.ts CHANGED
@@ -55,6 +55,7 @@
55
55
  import { type PgCompatPool, type PgCompatPoolClient, TurbineClient, type TurbineConfig } from './client.js';
56
56
  import { type Dialect } from './dialect.js';
57
57
  import type { EngineClientConfig } from './engine-config.js';
58
+ import { type TurbineLogger } from './logger.js';
58
59
  import type { PowdbExec } from './powdb-introspect.js';
59
60
  import { type PowdbCapabilities } from './powdb-shared.js';
60
61
  import { type SchemaMetadata } from './schema.js';
@@ -261,6 +262,8 @@ export declare function capabilitiesFromVersion(version: string | undefined | nu
261
262
  */
262
263
  export interface PowqlSchemaDDLOptions {
263
264
  capabilities?: PowdbCapabilities;
265
+ /** Where a skipped-link warning goes. Defaults to the console logger. */
266
+ logger?: TurbineLogger;
264
267
  /**
265
268
  * Emit `link Owner.name -> Target on local = target` declarations (PowDB entity
266
269
  * links, >= 0.19) for every single-column hasMany / hasOne / belongsTo relation
@@ -340,6 +343,7 @@ export declare function powqlSchemaDDL(schema: SchemaMetadata, opts?: PowqlSchem
340
343
  */
341
344
  export declare function applyPowdbLinks(exec: PowdbExec, schema: SchemaMetadata, options?: {
342
345
  capabilities?: PowdbCapabilities;
346
+ logger?: TurbineLogger;
343
347
  }): Promise<string[]>;
344
348
  /**
345
349
  * Translate a PowDB error into a typed Turbine error. Handles BOTH transports,
package/dist/powdb.js CHANGED
@@ -56,6 +56,7 @@ import { AsyncLocalStorage } from 'node:async_hooks';
56
56
  import { TurbineClient, } from './client.js';
57
57
  import { postgresDialect } from './dialect.js';
58
58
  import { ConnectionError, malformedConnectionStringMessage, NotNullViolationError, ReadOnlyError, TimeoutError, UniqueConstraintError, UnsupportedFeatureError, ValidationError, } from './errors.js';
59
+ import { consoleLogger, logTo } from './logger.js';
59
60
  import importOptionalPeer from './optional-peer-import.cjs';
60
61
  import { ALL_POWDB_CAPABILITIES, atLeastVersion, isDateColumn, PowdbFloatParam, PowdbJsonParam, parsePowdbSemver, powqlColumnType, quotePowqlIdent, requireCapability, } from './powdb-shared.js';
61
62
  import { shouldWarnOnce, WARN_NS } from './query/warn-registry.js';
@@ -91,6 +92,48 @@ export { ALL_POWDB_CAPABILITIES, coerceNativeValue, coerceValue, isJsonColumn, i
91
92
  export const powdbDialect = {
92
93
  ...postgresDialect,
93
94
  name: 'powdb',
95
+ // PowqlInterface compiles PowQL itself and refuses what PowDB lacks; these
96
+ // answer for the client paths a PowDB client shares (transactions, raw
97
+ // queries, `tx.$connection()`) and keep every SQL-only feature off.
98
+ capabilities: Object.freeze({
99
+ distinctOn: false,
100
+ positionalJson: false,
101
+ nullsOrderingClause: false,
102
+ nullsSortHigh: false,
103
+ insensitiveInList: false,
104
+ jsonDocumentEquality: 'text',
105
+ jsonExtractTyped: false,
106
+ jsonExactNumericCast: false,
107
+ numericTextComparison: false,
108
+ // PowDB has no foreign keys: declared relations are not enforced, and its
109
+ // own mass-mutation guard already folds with foreignKeys: false.
110
+ enforcesForeignKeys: false,
111
+ partitionLimitPushdown: false,
112
+ utcTimestampRewrite: false,
113
+ paramTypeCasts: false,
114
+ namedEnumTypes: false,
115
+ structuredValueColumns: 'scalars',
116
+ maxBindParameters: Number.POSITIVE_INFINITY,
117
+ maxInsertRows: Number.POSITIVE_INFINITY,
118
+ upsertConflictTarget: 'explicit',
119
+ setClauseOrder: 'snapshot',
120
+ reselectLocksRows: false,
121
+ integerDivision: '/',
122
+ updateExpressions: false,
123
+ bulkUpsertAppliesRepeatedKeys: false,
124
+ readOnlyTransactions: false,
125
+ connectionParameters: false,
126
+ backendCancel: false,
127
+ idleTransactionTimeout: false,
128
+ abortsTransactionOnError: false,
129
+ connectionDriver: 'none',
130
+ preparesEveryStatement: false,
131
+ statementSyntax: null,
132
+ // No foreign keys to declare, inline or otherwise; PowQL DDL comes from
133
+ // powqlSchemaDDL, which never reads this.
134
+ inlineForeignKeys: false,
135
+ unboundedTextKeys: true,
136
+ }),
94
137
  // `resultStrategy` is decorative for PowDB, PowqlInterface owns its own write
95
138
  // path and never reads it. Set to 'returning' for honesty: writes use PowDB
96
139
  // 0.7.0's trailing `returning` keyword (upsert excepted, see PowqlInterface).
@@ -108,6 +151,11 @@ export const powdbDialect = {
108
151
  // earlier, this override keeps the flag truthful if a future path consults it.
109
152
  supportsLateralJoin: false,
110
153
  supportsPlanCacheMode: false,
154
+ // PowqlInterface reads none of these (it refuses each feature itself); they
155
+ // are set so the flags stay truthful if a shared path consults them.
156
+ rowLockModes: [],
157
+ supportsPercentileAggregates: false,
158
+ supportsBulkUpsert: false,
111
159
  beginStatement: () => 'begin',
112
160
  commitStatement: () => 'commit',
113
161
  rollbackStatement: () => 'rollback',
@@ -324,7 +372,7 @@ export function powqlSchemaDDL(schema, opts = {}) {
324
372
  new Set(idx.columns).size === pkSet.size &&
325
373
  idx.columns.every((c) => pkSet.has(c));
326
374
  const fields = meta.columns.map((col) => {
327
- const powqlType = powqlColumnType(col);
375
+ const powqlType = powqlColumnType(col, schema.enums);
328
376
  // Gate `json` columns behind the engine's jsonDocs capability when a
329
377
  // caller supplied one, an old engine has no `json` type and would reject
330
378
  // the DDL. Pure-function callers (no opts) emit unconditionally.
@@ -409,8 +457,8 @@ export function powqlSchemaDDL(schema, opts = {}) {
409
457
  requireCapability(caps, 'entityLinks', 'entity link DDL (`emitLinks`)');
410
458
  const links = deriveDesiredLinks(schema, (owner, name) => {
411
459
  if (shouldWarnOnce(WARN_NS.powdbLinks, `collide:${owner}.${name}`)) {
412
- console.warn(`[turbine] powqlSchemaDDL(emitLinks): relation "${name}" on "${owner}" collides with a column of the ` +
413
- 'same name; skipping its link declaration (PowDB hard-errors on a link that shadows a column).');
460
+ logTo(opts.logger ?? consoleLogger, 'warn', `powqlSchemaDDL(emitLinks): relation "${name}" on "${owner}" collides with a column of the ` +
461
+ 'same name; skipping its link declaration (PowDB hard-errors on a link that shadows a column).', { code: 'powdb-link-collision', table: owner, relation: name, operation: 'powqlSchemaDDL' });
414
462
  }
415
463
  });
416
464
  for (const link of links)
@@ -438,6 +486,7 @@ export function powqlSchemaDDL(schema, opts = {}) {
438
486
  */
439
487
  export async function applyPowdbLinks(exec, schema, options = {}) {
440
488
  const caps = options.capabilities;
489
+ const logger = options.logger ?? consoleLogger;
441
490
  if (caps) {
442
491
  requireCapability(caps, 'entityLinks', 'entity link DDL (`applyPowdbLinks`)');
443
492
  requireCapability(caps, 'linkIntrospection', 'PowDB `schema links` introspection');
@@ -446,8 +495,8 @@ export async function applyPowdbLinks(exec, schema, options = {}) {
446
495
  const byOwnerName = new Map(existing.map((l) => [`${l.owner}\u0000${l.name}`, l]));
447
496
  const desired = deriveDesiredLinks(schema, (owner, name) => {
448
497
  if (shouldWarnOnce(WARN_NS.powdbLinks, `collide:${owner}.${name}`)) {
449
- console.warn(`[turbine] applyPowdbLinks: relation "${name}" on "${owner}" collides with a column of the same name; ` +
450
- 'skipping its link (PowDB hard-errors on a link that shadows a column).');
498
+ logTo(logger, 'warn', `applyPowdbLinks: relation "${name}" on "${owner}" collides with a column of the same name; ` +
499
+ 'skipping its link (PowDB hard-errors on a link that shadows a column).', { code: 'powdb-link-collision', table: owner, relation: name, operation: 'applyPowdbLinks' });
451
500
  }
452
501
  });
453
502
  const executed = [];
@@ -456,9 +505,9 @@ export async function applyPowdbLinks(exec, schema, options = {}) {
456
505
  if (found) {
457
506
  const same = found.target === link.target && found.localKey === link.localKey && found.targetKey === link.targetKey;
458
507
  if (!same && shouldWarnOnce(WARN_NS.powdbLinks, `drift:${link.owner}.${link.name}`)) {
459
- console.warn(`[turbine] applyPowdbLinks: link "${link.name}" on "${link.owner}" is already declared with different ` +
508
+ logTo(logger, 'warn', `applyPowdbLinks: link "${link.name}" on "${link.owner}" is already declared with different ` +
460
509
  `endpoints (have ${found.target} on ${found.localKey} = ${found.targetKey}, want ${link.target} on ` +
461
- `${link.localKey} = ${link.targetKey}); leaving it untouched (PowDB has no drop-link DDL).`);
510
+ `${link.localKey} = ${link.targetKey}); leaving it untouched (PowDB has no drop-link DDL).`, { code: 'powdb-link-drift', table: link.owner, relation: link.name, operation: 'applyPowdbLinks' });
462
511
  }
463
512
  continue; // identical → idempotent skip; drift → warned skip
464
513
  }
@@ -704,7 +753,9 @@ export function wrapPowdbError(err) {
704
753
  // evaluate true for every row. Runs before the generic `type mismatch` regex
705
754
  // below so the column name and the fix survive into the message.
706
755
  {
707
- const m = /type mismatch for column '([^']+)': expected ([^,]+), got (\w+)/i.exec(msg);
756
+ // Every group is bounded: `[^,]+` retried from each "expected " in a long
757
+ // message made this quadratic in the message length.
758
+ const m = /type mismatch for column '([^']{1,256})': expected ([^,]{1,64}), got (\w{1,32})/i.exec(msg);
708
759
  if (m) {
709
760
  return new ValidationError(`PowDB rejected a comparison on column "${m[1]}": the column is ${m[2]} but the bound value is ` +
710
761
  `${m[3]}. PowQL never coerces across types in a comparison (before engine 0.20 this silently matched ` +
@@ -841,6 +892,19 @@ function reentrantTransactionError() {
841
892
  'on the write lock the open transaction holds. Use the `tx` client the callback receives, or start the ' +
842
893
  'second transaction after the first completes. (Independent concurrent transactions queue automatically.)');
843
894
  }
895
+ /**
896
+ * The error for a plain statement issued through the CLIENT (not the `tx`
897
+ * client) from inside that client's own open transaction callback, on the
898
+ * embedded engine. Its one handle carries one transaction, so the statement
899
+ * could only run inside the transaction (and be rolled back with it) or wait
900
+ * for the transaction to finish, which is itself waiting on this statement.
901
+ */
902
+ function statementInsideTransactionError() {
903
+ return new UnsupportedFeatureError('A statement on the client while its own transaction is open', 'powdb', 'This call was made through the client, not the `tx` client, from inside an open $transaction callback. The ' +
904
+ 'embedded engine has one handle and one transaction, so the call would either join that transaction (and be ' +
905
+ 'rolled back with it) or wait for it forever. Use the `tx` client the callback receives, or make the call ' +
906
+ 'after the transaction completes.');
907
+ }
844
908
  // ---------------------------------------------------------------------------
845
909
  // Single-writer transaction gate, FIFO queueing + re-entrancy detection
846
910
  // ---------------------------------------------------------------------------
@@ -911,9 +975,15 @@ class PowdbTxGate {
911
975
  queueTimeoutMs;
912
976
  /** Tail of the FIFO queue, resolves once every earlier transaction has finished. */
913
977
  tail = Promise.resolve();
978
+ /** Holds taken and not yet finished, queued ones included. */
979
+ outstanding = 0;
914
980
  constructor(queueTimeoutMs) {
915
981
  this.queueTimeoutMs = queueTimeoutMs;
916
982
  }
983
+ /** True while any transaction holds the gate or waits for it. */
984
+ get busy() {
985
+ return this.outstanding > 0;
986
+ }
917
987
  /**
918
988
  * Take a place in the transaction queue. The prologue walks the caller's
919
989
  * marker chain (planted around transaction callbacks by the pool's
@@ -922,7 +992,7 @@ class PowdbTxGate {
922
992
  * the returned hold carries the fresh marker (`hold.ctx`) for
923
993
  * `wrapTransactionCallback` to scope around the user callback.
924
994
  */
925
- async acquire() {
995
+ async acquire(reentrantError = reentrantTransactionError) {
926
996
  // Walk the WHOLE marker chain, not just the innermost marker: with two
927
997
  // pools, dbA-tx → dbB-tx → dbA-begin leaves dbB's marker innermost, but
928
998
  // the dbA ancestor is still open, queueing the inner dbA begin behind it
@@ -935,7 +1005,7 @@ class PowdbTxGate {
935
1005
  parent = parent.parent;
936
1006
  for (let c = parent; c !== undefined; c = c.parent) {
937
1007
  if (c.gate === this && !c.done) {
938
- throw reentrantTransactionError();
1008
+ throw reentrantError();
939
1009
  }
940
1010
  }
941
1011
  const ctx = { gate: this, done: false, parent };
@@ -945,12 +1015,14 @@ class PowdbTxGate {
945
1015
  });
946
1016
  const ahead = this.tail;
947
1017
  this.tail = ahead.then(() => finished);
1018
+ this.outstanding++;
948
1019
  const hold = {
949
1020
  ctx,
950
1021
  finish: () => {
951
1022
  if (ctx.done)
952
1023
  return;
953
1024
  ctx.done = true;
1025
+ this.outstanding--;
954
1026
  handOff();
955
1027
  },
956
1028
  };
@@ -1608,6 +1680,30 @@ export class PowdbEmbeddedPool {
1608
1680
  throw new ConnectionError('The PowDB embedded pool is closed: disconnect() was already called on this client.');
1609
1681
  }
1610
1682
  }
1683
+ if (ctl === null && holdRef.hold === null && this.txGate.busy) {
1684
+ // A statement from OUTSIDE the open transaction. The engine is one
1685
+ // handle, so running it now would put it inside that transaction: it
1686
+ // would read uncommitted rows, and a write would be rolled back with the
1687
+ // transaction after being reported as done. It takes its FIFO turn on the
1688
+ // gate instead, as a one-statement transaction of its own would. From
1689
+ // inside the transaction's own callback that wait could never end, so the
1690
+ // gate refuses it there.
1691
+ const hold = await this.txGate.acquire(statementInsideTransactionError);
1692
+ try {
1693
+ if (this.closed) {
1694
+ throw new ConnectionError('The PowDB embedded pool is closed: disconnect() was already called on this client.');
1695
+ }
1696
+ try {
1697
+ return this.exec(powql, params);
1698
+ }
1699
+ catch (err) {
1700
+ throw wrapPowdbError(err);
1701
+ }
1702
+ }
1703
+ finally {
1704
+ hold.finish();
1705
+ }
1706
+ }
1611
1707
  if ((ctl === 'commit' || ctl === 'rollback') && holdRef.hold === null) {
1612
1708
  // This context never acquired the gate, its `begin` never ran (the
1613
1709
  // gate timed out / threw re-entrant E017, or no begin was issued at
@@ -1799,25 +1895,52 @@ async function openEmbeddedPool(target, poolOptions = {}, assumeEngineVersion, i
1799
1895
  throw err;
1800
1896
  throw new ConnectionError(`PowDB embedded could not open data dir "${dir}": ${err.message}`);
1801
1897
  }
1802
- if (syncMode !== undefined) {
1803
- if (typeof db.setSyncMode !== 'function') {
1804
- throw new ConnectionError('embedded `syncMode` requires @zvndev/powdb-embedded ≥ 0.7.1 (the installed addon has no setSyncMode).');
1898
+ try {
1899
+ if (syncMode !== undefined) {
1900
+ if (typeof db.setSyncMode !== 'function') {
1901
+ throw new ConnectionError('embedded `syncMode` requires @zvndev/powdb-embedded ≥ 0.7.1 (the installed addon has no setSyncMode).');
1902
+ }
1903
+ db.setSyncMode(syncMode);
1904
+ }
1905
+ // Native typed wire is feature-detected on the OPENED handle: an addon ≥ 0.14
1906
+ // exposes `queryWithParams`, so nativeRaw turns on (server-gate ≥ 0.13 still
1907
+ // applies via the version); an older addon has no such method → false.
1908
+ const capabilities = capabilitiesFromVersion(assumeEngineVersion ?? resolveEmbeddedVersion(), {
1909
+ hasNativeRaw: typeof db.queryWithParams === 'function',
1910
+ });
1911
+ // A read-only target forces the pool's readonly flag; otherwise honor whatever
1912
+ // `poolOptions` (threaded from `options.readonly`) carried.
1913
+ return new PowdbEmbeddedPool(db, {
1914
+ ...poolOptions,
1915
+ capabilities,
1916
+ readonly: Boolean(readonly) || Boolean(poolOptions.readonly),
1917
+ });
1918
+ }
1919
+ catch (err) {
1920
+ // The caller never receives this handle, so nothing else can close it.
1921
+ try {
1922
+ db.close?.();
1923
+ }
1924
+ catch {
1925
+ // The failure being reported is the useful one.
1805
1926
  }
1806
- db.setSyncMode(syncMode);
1927
+ throw err;
1928
+ }
1929
+ }
1930
+ /**
1931
+ * Probe a networked pool Turbine just built and wrap it. The pool is closed
1932
+ * when the probe or the wrapper refuses, because the caller never sees it
1933
+ * and its idle sockets would otherwise keep the process alive.
1934
+ */
1935
+ async function wrapOwnedClientPool(clientPool, poolOptions, assumeEngineVersion) {
1936
+ try {
1937
+ const capabilities = await assertNetworkedVersion(clientPool, assumeEngineVersion);
1938
+ return new PowdbPool(clientPool, undefined, { ...poolOptions, capabilities });
1939
+ }
1940
+ catch (err) {
1941
+ await clientPool.close().catch(() => { });
1942
+ throw err;
1807
1943
  }
1808
- // Native typed wire is feature-detected on the OPENED handle: an addon ≥ 0.14
1809
- // exposes `queryWithParams`, so nativeRaw turns on (server-gate ≥ 0.13 still
1810
- // applies via the version); an older addon has no such method → false.
1811
- const capabilities = capabilitiesFromVersion(assumeEngineVersion ?? resolveEmbeddedVersion(), {
1812
- hasNativeRaw: typeof db.queryWithParams === 'function',
1813
- });
1814
- // A read-only target forces the pool's readonly flag; otherwise honor whatever
1815
- // `poolOptions` (threaded from `options.readonly`) carried.
1816
- return new PowdbEmbeddedPool(db, {
1817
- ...poolOptions,
1818
- capabilities,
1819
- readonly: Boolean(readonly) || Boolean(poolOptions.readonly),
1820
- });
1821
1944
  }
1822
1945
  /**
1823
1946
  * Bind Turbine to PowDB. `target` is one of:
@@ -1848,9 +1971,7 @@ export async function turbinePowDB(target, schema, options = {}) {
1848
1971
  const max = options.connectionLimit ?? 10;
1849
1972
  if (typeof target === 'string') {
1850
1973
  const mod = options.powdbClientModule ?? (await loadPowdb());
1851
- const clientPool = new mod.Pool({ ...parsePowdbUrl(target), max });
1852
- const capabilities = await assertNetworkedVersion(clientPool, options.assumeEngineVersion);
1853
- pool = new PowdbPool(clientPool, undefined, { ...poolOptions, capabilities });
1974
+ pool = await wrapOwnedClientPool(new mod.Pool({ ...parsePowdbUrl(target), max }), poolOptions, options.assumeEngineVersion);
1854
1975
  owns = true;
1855
1976
  }
1856
1977
  else if (target instanceof PowdbPool) {
@@ -1870,26 +1991,36 @@ export async function turbinePowDB(target, schema, options = {}) {
1870
1991
  }
1871
1992
  else {
1872
1993
  const mod = options.powdbClientModule ?? (await loadPowdb());
1873
- const clientPool = new mod.Pool({ ...target, max });
1874
- const capabilities = await assertNetworkedVersion(clientPool, options.assumeEngineVersion);
1875
- pool = new PowdbPool(clientPool, undefined, { ...poolOptions, capabilities });
1994
+ pool = await wrapOwnedClientPool(new mod.Pool({ ...target, max }), poolOptions, options.assumeEngineVersion);
1876
1995
  owns = true;
1877
1996
  }
1878
- // The PowQL generator is loaded here to keep client.ts free of any PowDB import.
1879
- const { PowqlInterface } = await import('./powql.js');
1880
- const queryInterfaceFactory = (p, table, sch, middlewares, opts) => new PowqlInterface(p, table, sch, middlewares, opts);
1881
- // Forward every client-level option EXCEPT the ones this factory owns (they
1882
- // configure the pool / transport above, and TurbineClient warns on a config
1883
- // key it does not recognize). Destructured rather than hand-listed on the way
1884
- // IN: an allowlist is how `globalFilters` came to be accepted and ignored.
1885
- const { connectionLimit: _connectionLimit, transactionQueueTimeoutMs: _transactionQueueTimeoutMs, retryStaleReads: _retryStaleReads, assumeEngineVersion: _assumeEngineVersion, readonly: _readonly, powdbClientModule: _powdbClientModule, powdbEmbeddedModule: _powdbEmbeddedModule, ...clientConfig } = options;
1886
- const client = new TurbineClient({
1887
- ...clientConfig,
1888
- pool,
1889
- preparedStatements: false,
1890
- dialect: powdbDialect,
1891
- queryInterfaceFactory,
1892
- }, schema);
1997
+ let client;
1998
+ try {
1999
+ // The PowQL generator is loaded here to keep client.ts free of any PowDB import.
2000
+ const { PowqlInterface } = await import('./powql.js');
2001
+ const queryInterfaceFactory = (p, table, sch, middlewares, opts) => new PowqlInterface(p, table, sch, middlewares, opts);
2002
+ // Forward every client-level option EXCEPT the ones this factory owns (they
2003
+ // configure the pool / transport above, and TurbineClient warns on a config
2004
+ // key it does not recognize). Destructured rather than hand-listed on the way
2005
+ // IN: an allowlist is how `globalFilters` came to be accepted and ignored.
2006
+ const { connectionLimit: _connectionLimit, transactionQueueTimeoutMs: _transactionQueueTimeoutMs, retryStaleReads: _retryStaleReads, assumeEngineVersion: _assumeEngineVersion, readonly: _readonly, powdbClientModule: _powdbClientModule, powdbEmbeddedModule: _powdbEmbeddedModule, ...clientConfig } = options;
2007
+ client = new TurbineClient({
2008
+ ...clientConfig,
2009
+ pool,
2010
+ preparedStatements: false,
2011
+ dialect: powdbDialect,
2012
+ queryInterfaceFactory,
2013
+ }, schema);
2014
+ }
2015
+ catch (err) {
2016
+ // A failed factory hands the caller nothing to close, so a pool or embedded
2017
+ // handle it BUILT is closed here (the client constructor refuses a bad
2018
+ // config, a static global filter naming no table for one). A pool the
2019
+ // caller supplied is the caller's and stays open.
2020
+ if (owns)
2021
+ await pool.end().catch(() => { });
2022
+ throw err;
2023
+ }
1893
2024
  if (owns) {
1894
2025
  // Turbine built this pool / embedded handle, so disconnect()/end() must
1895
2026
  // close it. client.ts sees TurbineConfig.pool as EXTERNAL (ownsPool =
@@ -1898,9 +2029,10 @@ export async function turbinePowDB(target, schema, options = {}) {
1898
2029
  // open until powdb-server's idle timeout (~300s) closed them. Consistent
1899
2030
  // with turbineMssql's owned-pool patch.
1900
2031
  const baseDisconnect = client.disconnect.bind(client);
1901
- const close = async () => {
1902
- await baseDisconnect();
2032
+ const close = async (options) => {
2033
+ const result = options === undefined ? await baseDisconnect() : await baseDisconnect(options);
1903
2034
  await pool.end();
2035
+ return result ?? undefined;
1904
2036
  };
1905
2037
  const patch = client;
1906
2038
  patch.disconnect = close;
@@ -1908,7 +2040,7 @@ export async function turbinePowDB(target, schema, options = {}) {
1908
2040
  }
1909
2041
  else {
1910
2042
  // Injected pool, the caller owns its lifecycle.
1911
- client.disconnect = async () => { };
2043
+ client.disconnect = async (options) => (options === undefined ? undefined : { timedOut: false, forcedConnections: 0 });
1912
2044
  }
1913
2045
  return client;
1914
2046
  }