turbine-orm 0.50.0 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +66 -66
  2. package/dist/adapters/cockroachdb.d.ts +5 -5
  3. package/dist/adapters/cockroachdb.js +10 -10
  4. package/dist/adapters/index.d.ts +5 -5
  5. package/dist/adapters/index.js +7 -7
  6. package/dist/adapters/yugabytedb.d.ts +7 -7
  7. package/dist/adapters/yugabytedb.js +10 -10
  8. package/dist/cjs/adapters/cockroachdb.d.ts +5 -5
  9. package/dist/cjs/adapters/cockroachdb.js +10 -10
  10. package/dist/cjs/adapters/index.d.ts +5 -5
  11. package/dist/cjs/adapters/index.js +7 -7
  12. package/dist/cjs/adapters/yugabytedb.d.ts +7 -7
  13. package/dist/cjs/adapters/yugabytedb.js +10 -10
  14. package/dist/cjs/cli/config.d.ts +13 -2
  15. package/dist/cjs/cli/config.js +3 -2
  16. package/dist/cjs/cli/destructive.d.ts +1 -1
  17. package/dist/cjs/cli/destructive.js +1 -1
  18. package/dist/cjs/cli/index.d.ts +10 -10
  19. package/dist/cjs/cli/index.js +49 -45
  20. package/dist/cjs/cli/loader.d.ts +7 -7
  21. package/dist/cjs/cli/loader.js +9 -9
  22. package/dist/cjs/cli/mcp.js +4 -4
  23. package/dist/cjs/cli/migrate.d.ts +5 -5
  24. package/dist/cjs/cli/migrate.js +11 -11
  25. package/dist/cjs/cli/studio-ui.generated.js +1 -1
  26. package/dist/cjs/cli/ui.d.ts +2 -2
  27. package/dist/cjs/cli/ui.js +2 -2
  28. package/dist/cjs/client.d.ts +49 -38
  29. package/dist/cjs/client.js +57 -56
  30. package/dist/cjs/dialect.d.ts +62 -18
  31. package/dist/cjs/dialect.js +40 -2
  32. package/dist/cjs/errors.d.ts +5 -5
  33. package/dist/cjs/errors.js +11 -11
  34. package/dist/cjs/generate.d.ts +6 -6
  35. package/dist/cjs/generate.js +31 -29
  36. package/dist/cjs/index-advisor.d.ts +5 -5
  37. package/dist/cjs/index-advisor.js +0 -0
  38. package/dist/cjs/index.d.ts +1 -1
  39. package/dist/cjs/index.js +7 -7
  40. package/dist/cjs/introspect.d.ts +35 -9
  41. package/dist/cjs/introspect.js +83 -32
  42. package/dist/cjs/mssql.d.ts +11 -11
  43. package/dist/cjs/mssql.js +64 -29
  44. package/dist/cjs/mysql.d.ts +8 -8
  45. package/dist/cjs/mysql.js +61 -23
  46. package/dist/cjs/nested-write.d.ts +21 -2
  47. package/dist/cjs/nested-write.js +51 -14
  48. package/dist/cjs/optional-peer-import.cjs +7 -7
  49. package/dist/cjs/optional-peer-import.d.cts +7 -7
  50. package/dist/cjs/pipeline-submittable.d.ts +2 -2
  51. package/dist/cjs/pipeline-submittable.js +6 -6
  52. package/dist/cjs/pipeline.d.ts +1 -1
  53. package/dist/cjs/pipeline.js +4 -4
  54. package/dist/cjs/powdb-introspect.d.ts +1 -1
  55. package/dist/cjs/powdb-introspect.js +1 -1
  56. package/dist/cjs/powdb.d.ts +28 -28
  57. package/dist/cjs/powdb.js +66 -66
  58. package/dist/cjs/powql.d.ts +27 -27
  59. package/dist/cjs/powql.js +73 -52
  60. package/dist/cjs/query/aggregates.d.ts +1 -1
  61. package/dist/cjs/query/aggregates.js +5 -5
  62. package/dist/cjs/query/batched-loader.d.ts +11 -11
  63. package/dist/cjs/query/batched-loader.js +24 -24
  64. package/dist/cjs/query/builder.d.ts +39 -21
  65. package/dist/cjs/query/builder.js +99 -57
  66. package/dist/cjs/query/compound-unique.d.ts +1 -1
  67. package/dist/cjs/query/compound-unique.js +0 -0
  68. package/dist/cjs/query/deferred.d.ts +12 -6
  69. package/dist/cjs/query/deferred.js +1 -1
  70. package/dist/cjs/query/filters.d.ts +31 -11
  71. package/dist/cjs/query/filters.js +67 -14
  72. package/dist/cjs/query/index.d.ts +1 -1
  73. package/dist/cjs/query/index.js +1 -1
  74. package/dist/cjs/query/relations.d.ts +9 -9
  75. package/dist/cjs/query/relations.js +164 -57
  76. package/dist/cjs/query/types.d.ts +86 -35
  77. package/dist/cjs/query/types.js +1 -1
  78. package/dist/cjs/query/utils.d.ts +27 -10
  79. package/dist/cjs/query/utils.js +86 -14
  80. package/dist/cjs/query/where.d.ts +47 -28
  81. package/dist/cjs/query/where.js +130 -31
  82. package/dist/cjs/query/writes.d.ts +24 -5
  83. package/dist/cjs/query/writes.js +102 -13
  84. package/dist/cjs/realtime.d.ts +7 -7
  85. package/dist/cjs/realtime.js +9 -9
  86. package/dist/cjs/schema-builder.d.ts +18 -7
  87. package/dist/cjs/schema-builder.js +17 -10
  88. package/dist/cjs/schema-metadata.d.ts +3 -3
  89. package/dist/cjs/schema-metadata.js +9 -9
  90. package/dist/cjs/schema-sql.d.ts +9 -9
  91. package/dist/cjs/schema-sql.js +20 -20
  92. package/dist/cjs/schema.d.ts +19 -9
  93. package/dist/cjs/schema.js +6 -6
  94. package/dist/cjs/serverless.d.ts +15 -15
  95. package/dist/cjs/serverless.js +16 -16
  96. package/dist/cjs/sqlite.d.ts +8 -8
  97. package/dist/cjs/sqlite.js +53 -22
  98. package/dist/cjs/typed-sql.d.ts +4 -4
  99. package/dist/cjs/typed-sql.js +5 -5
  100. package/dist/cli/config.d.ts +13 -2
  101. package/dist/cli/config.js +3 -2
  102. package/dist/cli/destructive.d.ts +1 -1
  103. package/dist/cli/destructive.js +1 -1
  104. package/dist/cli/index.d.ts +10 -10
  105. package/dist/cli/index.js +49 -45
  106. package/dist/cli/loader.d.ts +7 -7
  107. package/dist/cli/loader.js +9 -9
  108. package/dist/cli/mcp.js +4 -4
  109. package/dist/cli/migrate.d.ts +5 -5
  110. package/dist/cli/migrate.js +11 -11
  111. package/dist/cli/studio-ui.generated.js +1 -1
  112. package/dist/cli/ui.d.ts +2 -2
  113. package/dist/cli/ui.js +2 -2
  114. package/dist/client.d.ts +49 -38
  115. package/dist/client.js +57 -56
  116. package/dist/dialect.d.ts +62 -18
  117. package/dist/dialect.js +40 -2
  118. package/dist/errors.d.ts +5 -5
  119. package/dist/errors.js +11 -11
  120. package/dist/generate.d.ts +6 -6
  121. package/dist/generate.js +31 -29
  122. package/dist/index-advisor.d.ts +5 -5
  123. package/dist/index-advisor.js +0 -0
  124. package/dist/index.d.ts +1 -1
  125. package/dist/index.js +7 -7
  126. package/dist/introspect.d.ts +35 -9
  127. package/dist/introspect.js +82 -32
  128. package/dist/mssql.d.ts +11 -11
  129. package/dist/mssql.js +64 -29
  130. package/dist/mysql.d.ts +8 -8
  131. package/dist/mysql.js +61 -23
  132. package/dist/nested-write.d.ts +21 -2
  133. package/dist/nested-write.js +51 -14
  134. package/dist/optional-peer-import.cjs +7 -7
  135. package/dist/optional-peer-import.d.cts +7 -7
  136. package/dist/pipeline-submittable.d.ts +2 -2
  137. package/dist/pipeline-submittable.js +6 -6
  138. package/dist/pipeline.d.ts +1 -1
  139. package/dist/pipeline.js +4 -4
  140. package/dist/powdb-introspect.d.ts +1 -1
  141. package/dist/powdb-introspect.js +1 -1
  142. package/dist/powdb.d.ts +28 -28
  143. package/dist/powdb.js +66 -66
  144. package/dist/powql.d.ts +27 -27
  145. package/dist/powql.js +73 -52
  146. package/dist/query/aggregates.d.ts +1 -1
  147. package/dist/query/aggregates.js +5 -5
  148. package/dist/query/batched-loader.d.ts +11 -11
  149. package/dist/query/batched-loader.js +24 -24
  150. package/dist/query/builder.d.ts +39 -21
  151. package/dist/query/builder.js +100 -58
  152. package/dist/query/compound-unique.d.ts +1 -1
  153. package/dist/query/compound-unique.js +0 -0
  154. package/dist/query/deferred.d.ts +12 -6
  155. package/dist/query/deferred.js +1 -1
  156. package/dist/query/filters.d.ts +31 -11
  157. package/dist/query/filters.js +66 -13
  158. package/dist/query/index.d.ts +1 -1
  159. package/dist/query/index.js +1 -1
  160. package/dist/query/relations.d.ts +9 -9
  161. package/dist/query/relations.js +165 -58
  162. package/dist/query/types.d.ts +86 -35
  163. package/dist/query/types.js +1 -1
  164. package/dist/query/utils.d.ts +27 -10
  165. package/dist/query/utils.js +84 -14
  166. package/dist/query/where.d.ts +47 -28
  167. package/dist/query/where.js +129 -32
  168. package/dist/query/writes.d.ts +24 -5
  169. package/dist/query/writes.js +101 -13
  170. package/dist/realtime.d.ts +7 -7
  171. package/dist/realtime.js +9 -9
  172. package/dist/schema-builder.d.ts +18 -7
  173. package/dist/schema-builder.js +17 -10
  174. package/dist/schema-metadata.d.ts +3 -3
  175. package/dist/schema-metadata.js +9 -9
  176. package/dist/schema-sql.d.ts +9 -9
  177. package/dist/schema-sql.js +20 -20
  178. package/dist/schema.d.ts +19 -9
  179. package/dist/schema.js +6 -6
  180. package/dist/serverless.d.ts +15 -15
  181. package/dist/serverless.js +16 -16
  182. package/dist/sqlite.d.ts +8 -8
  183. package/dist/sqlite.js +53 -22
  184. package/dist/typed-sql.d.ts +4 -4
  185. package/dist/typed-sql.js +5 -5
  186. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * turbine-orm — TurbineClient
3
+ * turbine-orm, TurbineClient
4
4
  *
5
5
  * The main entry point for the Turbine TypeScript SDK.
6
6
  * Manages connection pooling and provides typed table accessors.
@@ -94,12 +94,12 @@ const READ_OPERATIONS = new Set([
94
94
  /**
95
95
  * Internal marker on the config object that tells the `TurbineClient`
96
96
  * constructor to build a lightweight "primary-only" view sharing an existing
97
- * client's primary pool, dialect, query options, and middleware — instead of
97
+ * client's primary pool, dialect, query options, and middleware, instead of
98
98
  * creating a fresh pool. Produced solely by `$primary()`; never public.
99
99
  */
100
100
  const PRIMARY_VIEW = Symbol('turbine.primaryView');
101
101
  // ---------------------------------------------------------------------------
102
- // TransactionClient — provides typed table accessors within a transaction
102
+ // TransactionClient, provides typed table accessors within a transaction
103
103
  // ---------------------------------------------------------------------------
104
104
  /**
105
105
  * A transaction-scoped client that provides the same table accessor API as TurbineClient.
@@ -114,7 +114,7 @@ class TransactionClient {
114
114
  sourcePool;
115
115
  tableCache = new Map();
116
116
  savepointCounter = 0;
117
- /** Active SQL dialect — owns savepoint keywords and raw-SQL placeholders. */
117
+ /** Active SQL dialect, owns savepoint keywords and raw-SQL placeholders. */
118
118
  dialect;
119
119
  constructor(client, schema, middlewares, queryOptions,
120
120
  /**
@@ -243,14 +243,14 @@ exports.TransactionClient = TransactionClient;
243
243
  // TurbineClient
244
244
  // ---------------------------------------------------------------------------
245
245
  class TurbineClient {
246
- /** The underlying pg.Pool — exposed for escape hatches */
246
+ /** The underlying pg.Pool, exposed for escape hatches */
247
247
  pool;
248
248
  /** The schema metadata this client was built from */
249
249
  schema;
250
250
  static int8ParserRegistered = false;
251
251
  static utcTimestampParserRegistered = false;
252
252
  logging;
253
- /** Active SQL dialect — owns transaction keywords, set_config, raw-SQL placeholders, capability flags. */
253
+ /** Active SQL dialect, owns transaction keywords, set_config, raw-SQL placeholders, capability flags. */
254
254
  dialect;
255
255
  tableCache = new Map();
256
256
  middlewares = [];
@@ -259,7 +259,7 @@ class TurbineClient {
259
259
  errorMessagesSafe;
260
260
  /** True when Turbine created the pool and is responsible for tearing it down */
261
261
  ownsPool = true;
262
- /** Active LISTEN subscriptions — torn down on disconnect() so it never hangs */
262
+ /** Active LISTEN subscriptions, torn down on disconnect() so it never hangs */
263
263
  activeSubscriptions = new Set();
264
264
  /**
265
265
  * Read-replica pools in round-robin order. Empty when no replicas are
@@ -355,7 +355,7 @@ class TurbineClient {
355
355
  */
356
356
  // Only register the int8 parser when the PRIMARY pool is Turbine-owned.
357
357
  // External pools (Neon HTTP, Vercel Postgres) may ship their own pg-types
358
- // fork and rely on their own parser configuration — registration is
358
+ // fork and rely on their own parser configuration, registration is
359
359
  // process-global, so flipping it because a string replica exists alongside
360
360
  // an external primary would silently change the external primary's parsing
361
361
  // too. String replicas configured next to an external primary therefore
@@ -372,8 +372,8 @@ class TurbineClient {
372
372
  // Parse `timestamp` (OID 1114) as UTC instead of server-local time. The
373
373
  // pg driver's default hands back a Date built in the process's local zone,
374
374
  // so the same row yields a different instant per deployment region. The
375
- // ORM convention (Prisma, Rails, Django) — and the only interpretation
376
- // that round-trips what Postgres stores — is UTC. Same ownership rule as
375
+ // ORM convention (Prisma, Rails, Django), and the only interpretation
376
+ // that round-trips what Postgres stores, is UTC. Same ownership rule as
377
377
  // the int8 parser: never mutate parser state on external pools.
378
378
  if (ownsAnyPool && config.utcTimestamps !== false && !TurbineClient.utcTimestampParserRegistered) {
379
379
  pg_1.default.types.setTypeParser(1114, (val) => new Date(`${val.replace(' ', 'T')}Z`));
@@ -389,6 +389,7 @@ class TurbineClient {
389
389
  defaultLimit: config.defaultLimit,
390
390
  warnOnUnlimited: config.warnOnUnlimited,
391
391
  utcTimestamps: config.utcTimestamps,
392
+ scopedConnect: config.scopedConnect,
392
393
  relationLoadStrategy: config.relationLoadStrategy,
393
394
  stableRelationOrder: config.stableRelationOrder,
394
395
  implicitPkOrdering: config.implicitPkOrdering,
@@ -420,17 +421,17 @@ class TurbineClient {
420
421
  }
421
422
  },
422
423
  };
423
- // Apply NotFoundError message redaction mode (default: safe — values are
424
+ // Apply NotFoundError message redaction mode (default: safe, values are
424
425
  // stripped from messages to avoid leaking PII into error logs).
425
426
  if (config.errorMessages) {
426
427
  (0, errors_js_1.setErrorMessageMode)(config.errorMessages);
427
428
  }
428
429
  if (config.pool) {
429
- // External pool — use directly. Turbine doesn't manage its lifecycle.
430
+ // External pool, use directly. Turbine doesn't manage its lifecycle.
430
431
  this.pool = config.pool;
431
432
  this.ownsPool = false;
432
433
  if (this.logging) {
433
- console.log(`[turbine] Using external pool — ${Object.keys(schema.tables).length} tables`);
434
+ console.log(`[turbine] Using external pool, ${Object.keys(schema.tables).length} tables`);
434
435
  }
435
436
  }
436
437
  else {
@@ -474,7 +475,7 @@ class TurbineClient {
474
475
  console.error('[turbine] Unexpected pool error:', err.message);
475
476
  });
476
477
  if (this.logging) {
477
- console.log(`[turbine] Pool created — max ${poolConfig.max} connections, ${Object.keys(schema.tables).length} tables`);
478
+ console.log(`[turbine] Pool created, max ${poolConfig.max} connections, ${Object.keys(schema.tables).length} tables`);
478
479
  }
479
480
  }
480
481
  // Build read-replica pools (if any). String entries become owned pg.Pools
@@ -522,14 +523,14 @@ class TurbineClient {
522
523
  }
523
524
  }
524
525
  // -------------------------------------------------------------------------
525
- // Middleware — intercept all queries
526
+ // Middleware, intercept all queries
526
527
  // -------------------------------------------------------------------------
527
528
  /**
528
529
  * Register a middleware function that runs around every query.
529
530
  *
530
531
  * Middleware can inspect and log query parameters, measure timing, and
531
532
  * transform the result returned by `next()`. Note: query SQL is generated
532
- * BEFORE middleware runs — `params.args` is a read-only snapshot, and
533
+ * BEFORE middleware runs, `params.args` is a read-only snapshot, and
533
534
  * mutating it does NOT change the executed SQL. Cross-cutting filters
534
535
  * (e.g. soft deletes) belong in the query itself: pass an explicit
535
536
  * `where: { deletedAt: null }` or wrap the table accessor in a small helper.
@@ -544,7 +545,7 @@ class TurbineClient {
544
545
  * return result;
545
546
  * });
546
547
  *
547
- * // Result transformation middleware — redact a field on the way out
548
+ * // Result transformation middleware, redact a field on the way out
548
549
  * db.$use(async (params, next) => {
549
550
  * const result = await next(params);
550
551
  * if (params.model === 'users' && Array.isArray(result)) {
@@ -566,7 +567,7 @@ class TurbineClient {
566
567
  cache.clear();
567
568
  }
568
569
  // -------------------------------------------------------------------------
569
- // Event emitter — subscribe to query lifecycle events
570
+ // Event emitter, subscribe to query lifecycle events
570
571
  // -------------------------------------------------------------------------
571
572
  $on(_event, listener) {
572
573
  this.queryListeners.add(listener);
@@ -575,7 +576,7 @@ class TurbineClient {
575
576
  this.queryListeners.delete(listener);
576
577
  }
577
578
  // -------------------------------------------------------------------------
578
- // Observability — automatic metrics collection
579
+ // Observability, automatic metrics collection
579
580
  // -------------------------------------------------------------------------
580
581
  observeEngine;
581
582
  async $observe(config) {
@@ -597,16 +598,16 @@ class TurbineClient {
597
598
  };
598
599
  }
599
600
  // -------------------------------------------------------------------------
600
- // Table accessor — creates QueryInterface for any table
601
+ // Table accessor, creates QueryInterface for any table
601
602
  // -------------------------------------------------------------------------
602
603
  /**
603
604
  * Get a QueryInterface for a table.
604
- * Results are cached — calling `table('users')` twice returns the same instance.
605
+ * Results are cached, calling `table('users')` twice returns the same instance.
605
606
  *
606
607
  * When read replicas are configured, this returns a thin routing proxy: the
607
608
  * read-only operations in {@link READ_OPERATIONS} are dispatched to a
608
- * round-robin replica-bound QueryInterface (so an entire read — base rows and
609
- * any batched sub-queries — runs against a single consistent replica), while
609
+ * round-robin replica-bound QueryInterface (so an entire read, base rows and
610
+ * any batched sub-queries, runs against a single consistent replica), while
610
611
  * writes and every other member fall through to the primary-bound instance.
611
612
  * With no replicas the original single-pool instance is returned directly.
612
613
  */
@@ -681,7 +682,7 @@ class TurbineClient {
681
682
  });
682
683
  }
683
684
  /**
684
- * Return a view of this client that pins EVERY operation — reads included —
685
+ * Return a view of this client that pins EVERY operation, reads included -
685
686
  * to the primary pool, bypassing replica routing. Use it to read your own
686
687
  * write without replication lag, or for any read that must see the latest
687
688
  * committed data.
@@ -689,7 +690,7 @@ class TurbineClient {
689
690
  * The view shares the primary pool, schema, dialect, query options, and
690
691
  * middleware; it owns nothing, so its `disconnect()` is a no-op. When no
691
692
  * replicas are configured this simply returns the client itself (already
692
- * primary-only). The view is cached — repeated calls return the same instance.
693
+ * primary-only). The view is cached, repeated calls return the same instance.
693
694
  *
694
695
  * @example
695
696
  * ```ts
@@ -707,14 +708,14 @@ class TurbineClient {
707
708
  return this.primaryView;
708
709
  }
709
710
  // -------------------------------------------------------------------------
710
- // Pipeline — batch multiple queries into one round-trip
711
+ // Pipeline, batch multiple queries into one round-trip
711
712
  // -------------------------------------------------------------------------
712
713
  /**
713
714
  * Execute multiple queries in a single database round-trip.
714
715
  *
715
716
  * Two call styles:
716
- * - `db.pipeline(q1, q2, q3)` — rest params (backward-compatible)
717
- * - `db.pipeline([q1, q2, q3], { transactional: false })` — array + options
717
+ * - `db.pipeline(q1, q2, q3)`, rest params (backward-compatible)
718
+ * - `db.pipeline([q1, q2, q3], { transactional: false })`, array + options
718
719
  *
719
720
  * On pg.Pool-backed connections with TCP, this uses the real Postgres
720
721
  * extended-query pipeline protocol (one TCP flush, one round-trip).
@@ -736,7 +737,7 @@ class TurbineClient {
736
737
  queries = args;
737
738
  }
738
739
  if (this.logging) {
739
- console.log(`[turbine] Pipeline: ${queries.length} queries — ${queries.map((q) => q.tag).join(', ')}`);
740
+ console.log(`[turbine] Pipeline: ${queries.length} queries, ${queries.map((q) => q.tag).join(', ')}`);
740
741
  }
741
742
  return (0, pipeline_js_1.executePipeline)(this.pool, queries, options);
742
743
  }
@@ -749,7 +750,7 @@ class TurbineClient {
749
750
  return (0, pipeline_js_1.pipelineSupported)(this.pool);
750
751
  }
751
752
  // -------------------------------------------------------------------------
752
- // Raw SQL — tagged template literal escape hatch
753
+ // Raw SQL, tagged template literal escape hatch
753
754
  // -------------------------------------------------------------------------
754
755
  /**
755
756
  * Execute a raw SQL query with parameter interpolation via tagged templates.
@@ -783,7 +784,7 @@ class TurbineClient {
783
784
  }
784
785
  }
785
786
  /**
786
- * Execute a **typed** raw SQL query — Turbine's answer to Prisma's TypedSQL.
787
+ * Execute a **typed** raw SQL query, Turbine's answer to Prisma's TypedSQL.
787
788
  *
788
789
  * Like {@link raw}, every interpolated `${value}` becomes a `$N` parameter
789
790
  * (never string-concatenated), so it is injection-safe by construction. The
@@ -791,7 +792,7 @@ class TurbineClient {
791
792
  * returned {@link TypedSqlQuery} can be `await`ed directly for `T[]`, or
792
793
  * refined with `.one()` (→ `T | null`) or `.scalar<V>()` (→ `V | null`).
793
794
  *
794
- * Rows are returned as-is — no snake→camel mapping (matching `raw()`). Alias
795
+ * Rows are returned as-is, no snake→camel mapping (matching `raw()`). Alias
795
796
  * columns in SQL if you want camelCase keys.
796
797
  *
797
798
  * @example
@@ -817,7 +818,7 @@ class TurbineClient {
817
818
  return new typed_sql_js_1.TypedSqlQuery(this.pool, sql, params, this.logging);
818
819
  }
819
820
  // -------------------------------------------------------------------------
820
- // Transaction support (raw — legacy)
821
+ // Transaction support (raw, legacy)
821
822
  // -------------------------------------------------------------------------
822
823
  /**
823
824
  * Execute a function within a database transaction (raw pg.PoolClient).
@@ -836,7 +837,7 @@ class TurbineClient {
836
837
  * Only true once BEGIN has actually succeeded. If BEGIN itself throws
837
838
  * (e.g. a single-writer engine's transaction gate times out or rejects a
838
839
  * re-entrant begin), issuing a "best-effort" ROLLBACK would be a stray
839
- * statement from a context that never opened a transaction — on a driver
840
+ * statement from a context that never opened a transaction, on a driver
840
841
  * with one shared engine handle (PowDB embedded) it would roll back a
841
842
  * DIFFERENT caller's open transaction.
842
843
  */
@@ -859,7 +860,7 @@ class TurbineClient {
859
860
  await client.query(this.dialect.rollbackStatement());
860
861
  }
861
862
  catch {
862
- // Best-effort rollback — the connection may have died mid-query.
863
+ // Best-effort rollback, the connection may have died mid-query.
863
864
  }
864
865
  }
865
866
  throw err;
@@ -891,21 +892,21 @@ class TurbineClient {
891
892
  client.release(err);
892
893
  }
893
894
  catch {
894
- // pg may throw if the client is already released — swallow.
895
+ // pg may throw if the client is already released, swallow.
895
896
  }
896
897
  };
897
898
  let timedOut = false;
898
899
  /**
899
- * Only true once BEGIN has actually succeeded. If BEGIN itself throws —
900
+ * Only true once BEGIN has actually succeeded. If BEGIN itself throws -
900
901
  * e.g. a single-writer engine's transaction gate times out in its FIFO
901
- * queue or rejects a re-entrant begin (PowDB, E002/E017) — this context
902
+ * queue or rejects a re-entrant begin (PowDB, E002/E017), this context
902
903
  * never opened a transaction, so the catch below must NOT issue its
903
904
  * best-effort ROLLBACK: on a driver with one shared engine handle that
904
905
  * stray ROLLBACK would tear down a DIFFERENT caller's open transaction.
905
906
  */
906
907
  let began = false;
907
908
  try {
908
- // BEGIN with optional isolation level — the dialect owns the keyword and
909
+ // BEGIN with optional isolation level, the dialect owns the keyword and
909
910
  // BEGIN+isolation composition (Postgres appends ` ISOLATION LEVEL …`).
910
911
  const isolationSql = options?.isolationLevel ? ISOLATION_LEVELS[options.isolationLevel] : undefined;
911
912
  await client.query(this.dialect.beginStatement(isolationSql));
@@ -914,15 +915,15 @@ class TurbineClient {
914
915
  // Order matters: BEGIN -> isolation level (above) -> set_config loop ->
915
916
  // user fn. Any error here propagates to the catch below and rolls back
916
917
  // like any other transaction failure. We use set_config(name, value,
917
- // is_local=true) — the parameterizable, transaction-scoped equivalent of
918
- // SET LOCAL — so both name and value are BOUND params, never interpolated.
918
+ // is_local=true), the parameterizable, transaction-scoped equivalent of
919
+ // SET LOCAL, so both name and value are BOUND params, never interpolated.
919
920
  if (options?.sessionContext) {
920
921
  if (!this.dialect.supportsRLS) {
921
922
  throw new errors_js_1.UnsupportedFeatureError('sessionContext (RLS session GUCs)', this.dialect.name, 'set_config-based row-level-security context requires PostgreSQL.');
922
923
  }
923
924
  for (const [name, value] of Object.entries(options.sessionContext)) {
924
925
  if (!GUC_NAME_REGEX.test(name)) {
925
- throw new errors_js_1.ValidationError(`[turbine] Invalid session-context GUC name "${name}" — must match ` +
926
+ throw new errors_js_1.ValidationError(`[turbine] Invalid session-context GUC name "${name}", must match ` +
926
927
  '/^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)?$/ (optionally namespaced, e.g. "app.current_tenant")');
927
928
  }
928
929
  const cfg = this.dialect.buildSetSessionConfig(name, String(value));
@@ -952,7 +953,7 @@ class TurbineClient {
952
953
  const runCallback = () => (wrap ? wrap.call(client, () => fn(tx)) : fn(tx));
953
954
  if (timeout) {
954
955
  // Race between the function and a timeout. If the timeout fires we
955
- // need to actually abort the in-flight query — otherwise the backend
956
+ // need to actually abort the in-flight query, otherwise the backend
956
957
  // keeps running until pg's own timeout, holding a pool slot the whole
957
958
  // time. The simplest reliable cancellation is to destroy the
958
959
  // connection: passing a truthy argument to client.release() tells the
@@ -966,7 +967,7 @@ class TurbineClient {
966
967
  // Destroy the connection to abort the in-flight backend query.
967
968
  // We do this BEFORE rejecting so the socket is gone by the time
968
969
  // the caller's catch block runs.
969
- releaseOnce(new Error('[turbine] Transaction timeout — connection destroyed'));
970
+ releaseOnce(new Error('[turbine] Transaction timeout, connection destroyed'));
970
971
  reject(new errors_js_1.TimeoutError(timeout, 'Transaction'));
971
972
  }, timeout);
972
973
  });
@@ -987,11 +988,11 @@ class TurbineClient {
987
988
  return result;
988
989
  }
989
990
  catch (err) {
990
- // If the timeout fired we already destroyed the connection — issuing a
991
+ // If the timeout fired we already destroyed the connection, issuing a
991
992
  // ROLLBACK on a released client would throw "Client has already been
992
993
  // released". Skip the rollback in that case (the backend rolled back
993
994
  // when its socket was closed). Likewise skip it when BEGIN never
994
- // succeeded (`began` false) — there is no transaction to roll back and
995
+ // succeeded (`began` false), there is no transaction to roll back and
995
996
  // the stray statement could hit another caller's transaction on a
996
997
  // shared-handle engine.
997
998
  if (began && !timedOut && !released) {
@@ -999,7 +1000,7 @@ class TurbineClient {
999
1000
  await client.query(this.dialect.rollbackStatement());
1000
1001
  }
1001
1002
  catch {
1002
- // Best-effort rollback — the connection may have died mid-query.
1003
+ // Best-effort rollback, the connection may have died mid-query.
1003
1004
  }
1004
1005
  }
1005
1006
  if (this.logging) {
@@ -1025,7 +1026,7 @@ class TurbineClient {
1025
1026
  * {@link PgCompatPoolClient.supportsPipelining} (its `query()` accepts
1026
1027
  * concurrent calls and completes them in FIFO submission order), all
1027
1028
  * statements are dispatched in one write burst and the replies are
1028
- * collected in order — ~1 round trip plus server time. Only taken when
1029
+ * collected in order, ~1 round trip plus server time. Only taken when
1029
1030
  * the dialect's writes surface rows directly (`resultStrategy` !==
1030
1031
  * 'reselect'): a reselect plan is itself a sequential write+read pair.
1031
1032
  *
@@ -1095,7 +1096,7 @@ class TurbineClient {
1095
1096
  return this.$transaction(fn, { sessionContext: context });
1096
1097
  }
1097
1098
  // -------------------------------------------------------------------------
1098
- // LISTEN / NOTIFY — Postgres realtime pub/sub
1099
+ // LISTEN / NOTIFY, Postgres realtime pub/sub
1099
1100
  // -------------------------------------------------------------------------
1100
1101
  /**
1101
1102
  * Subscribe to a Postgres NOTIFY channel. The handler fires with each
@@ -1110,12 +1111,12 @@ class TurbineClient {
1110
1111
  *
1111
1112
  * The channel name CANNOT be a bound parameter (`LISTEN $1` is a syntax
1112
1113
  * error), so it is validated against a strict identifier regex AND quoted via
1113
- * `quoteIdent` before interpolation — it is the only identifier this method
1114
+ * `quoteIdent` before interpolation, it is the only identifier this method
1114
1115
  * places into SQL text.
1115
1116
  *
1116
1117
  * **Serverless caveat:** LISTEN needs a persistent connection that can push
1117
1118
  * async notifications. Stateless HTTP drivers (Neon HTTP, Vercel Postgres)
1118
- * cannot do this — `$listen` throws a `ConnectionError` rather than hang.
1119
+ * cannot do this, `$listen` throws a `ConnectionError` rather than hang.
1119
1120
  * `$notify` works on every driver.
1120
1121
  *
1121
1122
  * @example
@@ -1146,7 +1147,7 @@ class TurbineClient {
1146
1147
  /**
1147
1148
  * Send a Postgres NOTIFY on `channel` with an optional payload string.
1148
1149
  *
1149
- * Issued as `SELECT pg_notify($1, $2)` — both the channel and payload are
1150
+ * Issued as `SELECT pg_notify($1, $2)`, both the channel and payload are
1150
1151
  * BOUND parameters (no quoting/injection concern). The channel is still
1151
1152
  * validated against the identifier regex for parity with `$listen` and to
1152
1153
  * catch typos loudly. Works on every driver, including serverless HTTP pools.
@@ -1172,7 +1173,7 @@ class TurbineClient {
1172
1173
  }
1173
1174
  }
1174
1175
  // -------------------------------------------------------------------------
1175
- // Retry — automatic retry for retryable errors (deadlock, serialization)
1176
+ // Retry, automatic retry for retryable errors (deadlock, serialization)
1176
1177
  // -------------------------------------------------------------------------
1177
1178
  /**
1178
1179
  * Execute an async function with automatic retry on retryable errors.
@@ -1215,7 +1216,7 @@ class TurbineClient {
1215
1216
  * Gracefully shut down the connection pool.
1216
1217
  *
1217
1218
  * If Turbine was given an external pool via `TurbineConfig.pool`, this
1218
- * method is a no-op — the caller is responsible for the pool's lifecycle.
1219
+ * method is a no-op, the caller is responsible for the pool's lifecycle.
1219
1220
  */
1220
1221
  async disconnect() {
1221
1222
  // Tear down any live LISTEN subscriptions first. Each holds a dedicated
@@ -1234,7 +1235,7 @@ class TurbineClient {
1234
1235
  this.activeSubscriptions.clear();
1235
1236
  }
1236
1237
  // Close owned (string-configured) replica pools regardless of whether the
1237
- // primary is owned — external replica pools are left untouched (caller owns
1238
+ // primary is owned, external replica pools are left untouched (caller owns
1238
1239
  // their lifecycle), same contract as an external primary.
1239
1240
  for (const replicaPool of this.ownedReplicaPools) {
1240
1241
  try {
@@ -1248,7 +1249,7 @@ class TurbineClient {
1248
1249
  }
1249
1250
  if (!this.ownsPool) {
1250
1251
  if (this.logging) {
1251
- console.log('[turbine] disconnect() skipped — external primary pool is not owned by Turbine');
1252
+ console.log('[turbine] disconnect() skipped, external primary pool is not owned by Turbine');
1252
1253
  }
1253
1254
  return;
1254
1255
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * turbine-orm — SQL dialect contract
2
+ * turbine-orm, SQL dialect contract
3
3
  *
4
4
  * Phase-1 seam for future database packages. The current package remains
5
5
  * PostgreSQL-native by default, but query generation now depends on this
@@ -40,6 +40,28 @@ export interface BulkInsertStatementInput {
40
40
  skipDuplicates?: boolean;
41
41
  /** Optional SQL-ready RETURNING selection. */
42
42
  returning?: ReturningSelection;
43
+ /**
44
+ * Force the row-major `VALUES (…), (…)` form even on a dialect that would
45
+ * otherwise batch column-major.
46
+ *
47
+ * Set when any target column is ARRAY-typed: PostgreSQL's `UNNEST` transpose
48
+ * flattens a nested array, so an array-valued column cannot survive it. The
49
+ * flag costs one placeholder per cell rather than per column, which is why
50
+ * it is opt-in rather than the default. Dialects that already emit `VALUES`
51
+ * ignore it.
52
+ */
53
+ requireRowValues?: boolean;
54
+ }
55
+ /**
56
+ * The cast/decode pair for one JSON-divergent column type. See
57
+ * {@link Dialect.jsonWireRule}. The two halves MUST agree: a cast with no
58
+ * matching decode silently returns text where a value is expected.
59
+ */
60
+ export interface JsonWireRule {
61
+ /** The SQL expression to place in the JSON builder, given the column reference. */
62
+ sql(columnRef: string): string;
63
+ /** Turn the JSON-carried value back into the driver's representation. */
64
+ decode(value: unknown): unknown;
43
65
  }
44
66
  export interface BuiltStatement {
45
67
  sql: string;
@@ -58,7 +80,7 @@ export interface UpsertStatementInput {
58
80
  updateSetClauses: string[];
59
81
  /**
60
82
  * Optional SQL-ready predicate (no `WHERE` keyword) restricting the
61
- * conflict-UPDATE to matching rows — used by global filters (soft-delete /
83
+ * conflict-UPDATE to matching rows, used by global filters (soft-delete /
62
84
  * multi-tenancy) so an upsert never resurrects/steals a row outside the
63
85
  * filter. Only honored by dialects that set
64
86
  * {@link Dialect.supportsUpsertUpdateWhere}; others must never receive it.
@@ -107,13 +129,13 @@ export interface CreateIndexStatementInput {
107
129
  /**
108
130
  * How a dialect surfaces the row(s) produced by an INSERT/UPDATE/DELETE/upsert.
109
131
  *
110
- * - `'returning'` — a trailing `RETURNING *` clause returns the affected rows
132
+ * - `'returning'`, a trailing `RETURNING *` clause returns the affected rows
111
133
  * in the same statement (PostgreSQL, SQLite ≥ 3.35). The executor reads them
112
134
  * directly from the statement result.
113
- * - `'output'` — the statement itself emits the rows in a non-RETURNING shape
135
+ * - `'output'`, the statement itself emits the rows in a non-RETURNING shape
114
136
  * (SQL Server `OUTPUT INSERTED.*`). Executed exactly like `'returning'`: the
115
137
  * rows come back on the statement result.
116
- * - `'reselect'` — the engine cannot return rows from a write (MySQL). The
138
+ * - `'reselect'`, the engine cannot return rows from a write (MySQL). The
117
139
  * executor runs the write, then issues a follow-up `SELECT` (by primary
118
140
  * key / unique / where predicate) to fetch the affected row(s). The build
119
141
  * method supplies the {@link DeferredQuery} `reselect` plan that owns the
@@ -144,7 +166,7 @@ export interface OpenStreamOptions {
144
166
  ambientTransaction?: boolean;
145
167
  }
146
168
  /**
147
- * Inputs for {@link Dialect.buildUpdateStatement} — full UPDATE assembly. Used by
169
+ * Inputs for {@link Dialect.buildUpdateStatement}, full UPDATE assembly. Used by
148
170
  * engines whose returning shape is injected MID-statement rather than as a trailing
149
171
  * clause (SQL Server `OUTPUT INSERTED.*` lands between `SET …` and `WHERE …`).
150
172
  */
@@ -159,7 +181,7 @@ export interface UpdateStatementInput {
159
181
  returning?: ReturningSelection;
160
182
  }
161
183
  /**
162
- * Inputs for {@link Dialect.buildDeleteStatement} — full DELETE assembly. SQL Server
184
+ * Inputs for {@link Dialect.buildDeleteStatement}, full DELETE assembly. SQL Server
163
185
  * injects `OUTPUT DELETED.*` between `DELETE FROM <t>` and `WHERE …`.
164
186
  */
165
187
  export interface DeleteStatementInput {
@@ -171,10 +193,10 @@ export interface DeleteStatementInput {
171
193
  returning?: ReturningSelection;
172
194
  }
173
195
  /**
174
- * Inputs for {@link Dialect.buildLimitOffset} — the trailing pagination clause of an
196
+ * Inputs for {@link Dialect.buildLimitOffset}, the trailing pagination clause of an
175
197
  * outer SELECT. PostgreSQL/MySQL/SQLite use `LIMIT x [OFFSET y]`; SQL Server has no
176
198
  * `LIMIT` and uses `[ORDER BY …] OFFSET y ROWS [FETCH NEXT x ROWS ONLY]` (which
177
- * requires an ORDER BY — a stable default is injected when {@link hasOrderBy} is false).
199
+ * requires an ORDER BY, a stable default is injected when {@link hasOrderBy} is false).
178
200
  */
179
201
  export interface LimitOffsetInput {
180
202
  /** SQL-ready placeholder/literal for the row LIMIT, or undefined for no limit. */
@@ -190,13 +212,13 @@ export interface LimitOffsetInput {
190
212
  * `json_agg(json_build_object(...))` (SQL Server's `FOR JSON PATH` expresses the
191
213
  * object shape through the child SELECT's column ALIASES rather than an explicit
192
214
  * `JSON_OBJECT`, so it cannot be assembled from {@link Dialect.buildJsonObject} /
193
- * {@link Dialect.buildJsonArrayAgg} primitives — see {@link Dialect.buildRelationSubquery}).
215
+ * {@link Dialect.buildJsonArrayAgg} primitives, see {@link Dialect.buildRelationSubquery}).
194
216
  *
195
217
  * The query builder pre-resolves the parts that are engine-independent (alias,
196
218
  * select/omit-resolved columns, recursion + param threading) and hands them to the
197
219
  * dialect. **Param-push ordering contract:** the override MUST push values to
198
220
  * {@link params} in the same order the builder's collect path expects so the SQL
199
- * cache and pipeline batching stay in sync —
221
+ * cache and pipeline batching stay in sync -
200
222
  * - to-many with `limit`/`orderBy` (the "wrap" path) and manyToMany:
201
223
  * `buildWhere(...)` → push `limit` → `recurse(...)` for each nested relation;
202
224
  * - everything else (to-one, unordered/unlimited to-many): `recurse(...)` for each
@@ -207,7 +229,7 @@ export interface RelationSubqueryContext {
207
229
  relDef: RelationDef;
208
230
  /** The `with` spec for this relation: `true`, or a {@link WithOptions} object. */
209
231
  spec: true | WithOptions;
210
- /** Shared parameter array — push BOUND values here in the order described above. */
232
+ /** Shared parameter array, push BOUND values here in the order described above. */
211
233
  params: unknown[];
212
234
  /** Parent alias or table name (RAW identifier, not quoted) to correlate against. */
213
235
  parentRef: string;
@@ -258,12 +280,12 @@ export interface Dialect {
258
280
  /** Build a JSON object expression from output keys and SQL expressions. */
259
281
  buildJsonObject(pairs: [key: string, expr: string][]): string;
260
282
  /**
261
- * Build a positional JSON ARRAY expression from ordered SQL expressions —
283
+ * Build a positional JSON ARRAY expression from ordered SQL expressions -
262
284
  * the key-less counterpart to {@link buildJsonObject} used by the opt-in
263
285
  * `jsonEncoding: 'positional'` mode. Emitting `json_build_array(v1, v2, …)`
264
286
  * instead of `json_build_object('k1', v1, …)` drops every repeated key name
265
287
  * from every nested object of every row (the decode side maps positions back
266
- * to keys via a build-time shape descriptor). Postgres-only in v1 — other
288
+ * to keys via a build-time shape descriptor). Postgres-only in v1, other
267
289
  * engines never reach this because the builder gates positional encoding to
268
290
  * `dialect.name === 'postgresql'`, so the method is optional on the contract.
269
291
  */
@@ -303,7 +325,7 @@ export interface Dialect {
303
325
  * (a predicate on the conflict-UPDATE, e.g. Postgres `ON CONFLICT … DO UPDATE
304
326
  * SET … WHERE …`). Global filters only push a conflict-UPDATE predicate when
305
327
  * this is true, so engines whose upsert cannot express one (MySQL
306
- * `ON DUPLICATE KEY UPDATE`) never receive an orphaned parameter. Optional —
328
+ * `ON DUPLICATE KEY UPDATE`) never receive an orphaned parameter. Optional -
307
329
  * absent is treated as `false`.
308
330
  */
309
331
  readonly supportsUpsertUpdateWhere?: boolean;
@@ -374,6 +396,28 @@ export interface Dialect {
374
396
  buildJsonPathExtract(column: string, pathParamRef: string): string;
375
397
  /** Build a correlation clause across single or composite keys. */
376
398
  buildCorrelation(leftRef: string, leftColumns: string | string[], rightRef: string, rightColumns: string | string[]): string;
399
+ /**
400
+ * How a column whose JSON rendering DIVERGES from the driver's own value
401
+ * should be carried through a `with` subquery, or `undefined` when the
402
+ * column's JSON form already matches.
403
+ *
404
+ * A nested-relation subquery builds JSON (`json_build_object`,
405
+ * `json_group_array`, …) and the JSON number grammar is not the wire
406
+ * grammar: PostgreSQL renders `numeric` as a JSON number, SQLite renders a
407
+ * 64-bit INTEGER as one, and a JSON number is an IEEE double. So the same
408
+ * column read at top level and read through a relation came back as
409
+ * different values, LOSSILY past 2^53, and because `auto` picks between the
410
+ * join and batched strategies by row count, the same query could return
411
+ * either. The fix is to carry the value as text and decode it back with the
412
+ * engine's own rule, which is what this hook pairs up: `sql` must emit an
413
+ * expression the JSON builder can hold, and `decode` must turn that back
414
+ * into exactly what the driver would have produced.
415
+ *
416
+ * Omitting the hook keeps a dialect on the old behavior (no cast, no
417
+ * decode), which is correct for any engine whose JSON rendering agrees with
418
+ * its wire format.
419
+ */
420
+ jsonWireRule?(columnType: string): JsonWireRule | undefined;
377
421
  /** Optional type mapping hook for code generation/introspection. */
378
422
  typeToTypeScript?(dialectType: string, nullable: boolean): string;
379
423
  /**
@@ -469,7 +513,7 @@ export interface Dialect {
469
513
  buildLimitOffset?(input: LimitOffsetInput): string;
470
514
  /**
471
515
  * Assemble a full UPDATE statement. Optional: omitted by engines whose returning
472
- * shape is a trailing clause (PG/SQLite `RETURNING`, MySQL none) — the builder
516
+ * shape is a trailing clause (PG/SQLite `RETURNING`, MySQL none), the builder
473
517
  * falls back to `UPDATE <t> SET <set> <where><returningClause>`. SQL Server
474
518
  * implements this to inject `OUTPUT INSERTED.*` between `SET …` and `WHERE …`.
475
519
  */
@@ -483,7 +527,7 @@ export interface Dialect {
483
527
  * OVERRIDE nested-relation subquery generation entirely. Optional: when a dialect
484
528
  * omits it, the builder uses its native `json_agg(json_build_object(...))` path
485
529
  * (PG) routed through {@link buildJsonObject} / {@link buildJsonArrayAgg} /
486
- * {@link wrapJsonSubresult} — so MySQL and SQLite, which only swap those
530
+ * {@link wrapJsonSubresult}, so MySQL and SQLite, which only swap those
487
531
  * primitives, produce identical output and never define this hook. SQL Server
488
532
  * defines it to emit `FOR JSON PATH` correlated subqueries (its JSON aggregate
489
533
  * is expressed through the child SELECT's column aliases, which does not map onto
@@ -503,7 +547,7 @@ export interface IntrospectOptions {
503
547
  }
504
548
  /**
505
549
  * @deprecated Migration locking is owned by the {@link DatabaseAdapter} seam
506
- * (`src/adapters/index.ts` — `acquireLock`/`releaseLock`/`statementTimeout`),
550
+ * (`src/adapters/index.ts`, `acquireLock`/`releaseLock`/`statementTimeout`),
507
551
  * which is the single canonical locking seam. This interface was never
508
552
  * implemented or wired and is retained only for type back-compat; new engines
509
553
  * MUST provide a `DatabaseAdapter` instead. Will be removed in a future major.