litedbmodel 2.2.2 → 2.2.4

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 (180) hide show
  1. package/README.md +2 -1
  2. package/dist/DBModel.d.ts.map +1 -1
  3. package/dist/DBModel.js +3 -0
  4. package/dist/DBModel.js.map +1 -1
  5. package/dist/Middleware.d.ts +19 -2
  6. package/dist/Middleware.d.ts.map +1 -1
  7. package/dist/Middleware.js +1 -0
  8. package/dist/Middleware.js.map +1 -1
  9. package/dist/decorators.d.ts.map +1 -1
  10. package/dist/decorators.js +8 -0
  11. package/dist/decorators.js.map +1 -1
  12. package/dist/drivers/MysqlHelper.d.ts +1 -1
  13. package/dist/drivers/MysqlHelper.d.ts.map +1 -1
  14. package/dist/drivers/MysqlHelper.js.map +1 -1
  15. package/dist/drivers/PostgresHelper.d.ts +1 -1
  16. package/dist/drivers/PostgresHelper.d.ts.map +1 -1
  17. package/dist/drivers/PostgresHelper.js.map +1 -1
  18. package/dist/drivers/SqliteHelper.d.ts +1 -1
  19. package/dist/drivers/SqliteHelper.d.ts.map +1 -1
  20. package/dist/drivers/SqliteHelper.js.map +1 -1
  21. package/dist/index.cjs +105 -74
  22. package/dist/scp/connection-routing.d.ts +38 -1
  23. package/dist/scp/connection-routing.d.ts.map +1 -1
  24. package/dist/scp/connection-routing.js +64 -5
  25. package/dist/scp/connection-routing.js.map +1 -1
  26. package/dist/scp/dbmodel-runtime.d.ts +2 -1
  27. package/dist/scp/dbmodel-runtime.d.ts.map +1 -1
  28. package/dist/scp/dbmodel-runtime.js +2 -1
  29. package/dist/scp/dbmodel-runtime.js.map +1 -1
  30. package/dist/scp/decorator-adapter.d.ts +22 -0
  31. package/dist/scp/decorator-adapter.d.ts.map +1 -1
  32. package/dist/scp/decorator-adapter.js +26 -0
  33. package/dist/scp/decorator-adapter.js.map +1 -1
  34. package/dist/scp/emit/emitter.d.ts +21 -10
  35. package/dist/scp/emit/emitter.d.ts.map +1 -1
  36. package/dist/scp/emit/emitter.js +119 -51
  37. package/dist/scp/emit/emitter.js.map +1 -1
  38. package/dist/scp/emit/endpoint.d.ts +3 -8
  39. package/dist/scp/emit/endpoint.d.ts.map +1 -1
  40. package/dist/scp/exec-context.d.ts +39 -17
  41. package/dist/scp/exec-context.d.ts.map +1 -1
  42. package/dist/scp/exec-context.js +69 -35
  43. package/dist/scp/exec-context.js.map +1 -1
  44. package/dist/scp/grouping.d.ts.map +1 -1
  45. package/dist/scp/grouping.js +0 -0
  46. package/dist/scp/grouping.js.map +1 -1
  47. package/dist/scp/index.cjs +257 -670
  48. package/dist/scp/index.d.ts +6 -11
  49. package/dist/scp/index.d.ts.map +1 -1
  50. package/dist/scp/index.js +10 -28
  51. package/dist/scp/index.js.map +1 -1
  52. package/dist/scp/index.mjs +257 -658
  53. package/dist/scp/leaf-transport.d.ts +127 -39
  54. package/dist/scp/leaf-transport.d.ts.map +1 -1
  55. package/dist/scp/leaf-transport.js +30 -24
  56. package/dist/scp/leaf-transport.js.map +1 -1
  57. package/dist/scp/leaves.d.ts +57 -20
  58. package/dist/scp/leaves.d.ts.map +1 -1
  59. package/dist/scp/leaves.js +224 -65
  60. package/dist/scp/leaves.js.map +1 -1
  61. package/dist/scp/makesql/compile-select.d.ts +28 -1
  62. package/dist/scp/makesql/compile-select.d.ts.map +1 -1
  63. package/dist/scp/makesql/compile-select.js +19 -17
  64. package/dist/scp/makesql/compile-select.js.map +1 -1
  65. package/dist/scp/makesql/index.d.ts +2 -2
  66. package/dist/scp/makesql/index.d.ts.map +1 -1
  67. package/dist/scp/makesql/index.js +4 -6
  68. package/dist/scp/makesql/index.js.map +1 -1
  69. package/dist/scp/makesql/tx.d.ts +43 -89
  70. package/dist/scp/makesql/tx.d.ts.map +1 -1
  71. package/dist/scp/makesql/tx.js +45 -496
  72. package/dist/scp/makesql/tx.js.map +1 -1
  73. package/dist/scp/makesql/writeouttype.d.ts +5 -7
  74. package/dist/scp/makesql/writeouttype.d.ts.map +1 -1
  75. package/dist/scp/makesql/writeouttype.js +13 -19
  76. package/dist/scp/makesql/writeouttype.js.map +1 -1
  77. package/dist/scp/middleware.d.ts +4 -4
  78. package/dist/scp/middleware.d.ts.map +1 -1
  79. package/dist/scp/middleware.js +2 -2
  80. package/dist/scp/middleware.js.map +1 -1
  81. package/dist/scp/relation.d.ts +31 -12
  82. package/dist/scp/relation.d.ts.map +1 -1
  83. package/dist/scp/relation.js +16 -6
  84. package/dist/scp/relation.js.map +1 -1
  85. package/dist/scp/typed-object.d.ts +23 -19
  86. package/dist/scp/typed-object.d.ts.map +1 -1
  87. package/dist/scp/typed-object.js +24 -30
  88. package/dist/scp/typed-object.js.map +1 -1
  89. package/dist/scp/write-bundle.d.ts +21 -70
  90. package/dist/scp/write-bundle.d.ts.map +1 -1
  91. package/dist/scp/write-bundle.js +16 -80
  92. package/dist/scp/write-bundle.js.map +1 -1
  93. package/dist/types.d.ts +12 -0
  94. package/dist/types.d.ts.map +1 -1
  95. package/dist/types.js.map +1 -1
  96. package/docs/BENCHMARK.md +26 -22
  97. package/docs/COMPARISON.md +8 -4
  98. package/docs/api/README.md +1 -1
  99. package/docs/api/classes/Conditions.md +10 -10
  100. package/docs/api/classes/DBHandler.md +17 -17
  101. package/docs/api/classes/DBModel.md +111 -104
  102. package/docs/api/classes/LimitExceededError.md +8 -8
  103. package/docs/api/classes/Middleware.md +18 -18
  104. package/docs/api/classes/SqlRaw.md +6 -6
  105. package/docs/api/classes/SqlRef.md +7 -7
  106. package/docs/api/classes/Values.md +6 -6
  107. package/docs/api/classes/WriteInReadOnlyContextError.md +5 -5
  108. package/docs/api/classes/WriteOutsideTransactionError.md +5 -5
  109. package/docs/api/functions/belongsTo.md +2 -2
  110. package/docs/api/functions/closeAllPools.md +2 -2
  111. package/docs/api/functions/condsToRecord.md +2 -2
  112. package/docs/api/functions/createMiddleware.md +2 -2
  113. package/docs/api/functions/createPostgresDriver.md +2 -2
  114. package/docs/api/functions/createSqliteDriver.md +2 -2
  115. package/docs/api/functions/dbCast.md +3 -3
  116. package/docs/api/functions/dbCastIn.md +2 -2
  117. package/docs/api/functions/dbDynamic.md +3 -3
  118. package/docs/api/functions/dbFalse.md +2 -2
  119. package/docs/api/functions/dbImmediate.md +2 -2
  120. package/docs/api/functions/dbIn.md +2 -2
  121. package/docs/api/functions/dbNotNull.md +2 -2
  122. package/docs/api/functions/dbNow.md +2 -2
  123. package/docs/api/functions/dbNull.md +2 -2
  124. package/docs/api/functions/dbRaw.md +2 -2
  125. package/docs/api/functions/dbTrue.md +2 -2
  126. package/docs/api/functions/dbUuid.md +3 -3
  127. package/docs/api/functions/dbUuidIn.md +2 -2
  128. package/docs/api/functions/formatLocalDate.md +25 -0
  129. package/docs/api/functions/formatUTCDate.md +25 -0
  130. package/docs/api/functions/getDBConfig.md +2 -2
  131. package/docs/api/functions/getDBHandler.md +2 -2
  132. package/docs/api/functions/getTransactionConnection.md +2 -2
  133. package/docs/api/functions/getTransactionContext.md +2 -2
  134. package/docs/api/functions/hasMany.md +2 -2
  135. package/docs/api/functions/hasOne.md +2 -2
  136. package/docs/api/functions/initDBHandler.md +2 -2
  137. package/docs/api/functions/isAnySqlFragment.md +2 -2
  138. package/docs/api/functions/isConnectionError.md +26 -0
  139. package/docs/api/functions/isSqlCondition.md +2 -2
  140. package/docs/api/functions/isSqlFragment.md +2 -2
  141. package/docs/api/functions/isSqlRaw.md +2 -2
  142. package/docs/api/functions/isSqlRef.md +2 -2
  143. package/docs/api/functions/isSqlTypedFragment.md +2 -2
  144. package/docs/api/functions/model.md +6 -42
  145. package/docs/api/functions/parentRef.md +2 -2
  146. package/docs/api/functions/sql.md +7 -7
  147. package/docs/api/globals.md +57 -22
  148. package/docs/api/interfaces/Column.md +31 -31
  149. package/docs/api/interfaces/ColumnOptions.md +5 -4
  150. package/docs/api/interfaces/CreatedMiddlewareClass.md +7 -7
  151. package/docs/api/interfaces/DBConfig.md +13 -11
  152. package/docs/api/interfaces/DBConfigOptions.md +8 -8
  153. package/docs/api/interfaces/DeleteOptions.md +4 -4
  154. package/docs/api/interfaces/InsertOptions.md +8 -8
  155. package/docs/api/interfaces/LimitConfig.md +4 -4
  156. package/docs/api/interfaces/MiddlewareConfig.md +15 -15
  157. package/docs/api/interfaces/ModelOptions.md +8 -7
  158. package/docs/api/interfaces/PkeyResult.md +4 -4
  159. package/docs/api/interfaces/SelectOptions.md +17 -16
  160. package/docs/api/interfaces/SqlCondition.md +6 -6
  161. package/docs/api/interfaces/SqlFragment.md +5 -5
  162. package/docs/api/interfaces/SqlTypedFragment.md +7 -7
  163. package/docs/api/interfaces/TransactionOptions.md +7 -7
  164. package/docs/api/interfaces/UpdateManyOptions.md +4 -4
  165. package/docs/api/interfaces/UpdateOptions.md +4 -4
  166. package/docs/architecture.md +452 -0
  167. package/docs/benchmark-chart.svg +225 -249
  168. package/docs/design/phase-a-execution-context.md +2 -0
  169. package/package.json +20 -5
  170. package/dist/scp/writes.d.ts +0 -251
  171. package/dist/scp/writes.d.ts.map +0 -1
  172. package/dist/scp/writes.js +0 -176
  173. package/dist/scp/writes.js.map +0 -1
  174. package/docs/proposal/contracts-architecture-feasibility.md +0 -328
  175. package/docs/proposal/contracts-architecture.md +0 -579
  176. package/docs/proposal/litedbmodel-v2-scp-architecture.md +0 -433
  177. package/docs/proposal/sql-dynamic-expansion-spec.md +0 -175
  178. package/docs/proposal/v0-coverage-matrix.md +0 -157
  179. package/docs/proposal/v2-completeness-plan.md +0 -90
  180. package/docs/proposal/v2-sql-parity-checklist.md +0 -162
@@ -1137,7 +1137,6 @@ __export(index_exports, {
1137
1137
  DEFAULT_CONNECTION: () => DEFAULT_CONNECTION,
1138
1138
  DEFAULT_UNCAST_SQL_TYPE: () => DEFAULT_UNCAST_SQL_TYPE,
1139
1139
  Db: () => Db,
1140
- ENTITY_ROOT: () => ENTITY_ROOT,
1141
1140
  LEAF_TRANSPORT_SYMBOLS: () => LEAF_TRANSPORT_SYMBOLS,
1142
1141
  LITEDBMODEL_MAKESQL_CATALOG: () => LITEDBMODEL_MAKESQL_CATALOG,
1143
1142
  LimitExceededError: () => LimitExceededError,
@@ -1174,7 +1173,6 @@ __export(index_exports, {
1174
1173
  compileCompositeKeyLimited: () => compileCompositeKeyLimited,
1175
1174
  compileCompositeKeyStaticUnlimited: () => compileCompositeKeyStaticUnlimited,
1176
1175
  compileCompositeKeyUnlimited: () => compileCompositeKeyUnlimited,
1177
- compileCompositeWriteBundle: () => compileCompositeWriteBundle,
1178
1176
  compileCreateBundle: () => compileCreateBundle,
1179
1177
  compileCreateManyBundle: () => compileCreateManyBundle,
1180
1178
  compileDeleteBundle: () => compileDeleteBundle,
@@ -1193,7 +1191,6 @@ __export(index_exports, {
1193
1191
  compileUpdateManyBundle: () => compileUpdateManyBundle,
1194
1192
  compileUpdateSingle: () => compileUpdateSingle,
1195
1193
  compileWhere: () => compileWhere,
1196
- compileWriteBundle: () => compileWriteBundle,
1197
1194
  compileWriteNode: () => compileWriteNode,
1198
1195
  composeMakeSQL: () => composeMakeSQL,
1199
1196
  configurePgDeboxTypeParsers: () => configurePgDeboxTypeParsers,
@@ -1201,7 +1198,6 @@ __export(index_exports, {
1201
1198
  connectionForDriver: () => connectionForDriver,
1202
1199
  contextForConnection: () => contextForConnection,
1203
1200
  contextForDriver: () => contextForDriver,
1204
- countingDriver: () => countingDriver,
1205
1201
  createMiddleware: () => createMiddleware,
1206
1202
  currentPinnedAsyncConnection: () => currentPinnedAsyncConnection,
1207
1203
  currentRegistry: () => currentRegistry,
@@ -1209,20 +1205,15 @@ __export(index_exports, {
1209
1205
  dbCastIn: () => dbCastIn,
1210
1206
  deriveModelColumns: () => deriveModelColumns,
1211
1207
  deriveRelationDecls: () => deriveRelationDecls,
1212
- deriveTransactionPlan: () => deriveTransactionPlan,
1213
1208
  dialectFor: () => dialectFor,
1214
1209
  distributeToParent: () => distributeToParent,
1215
- edgeWrites: () => edgeWrites,
1216
1210
  emitBehaviorModule: () => emitBehaviorModule,
1217
- entityWrites: () => entityWrites,
1218
1211
  execute: () => execute,
1219
1212
  executeAsync: () => executeAsync,
1220
1213
  executeSQL: () => executeSQL,
1221
1214
  executeSQLAsync: () => executeSQLAsync,
1222
1215
  executeSafe: () => executeSafe,
1223
- executeTransaction: () => executeTransaction,
1224
1216
  executeTransactionAsync: () => executeTransactionAsync,
1225
- executeTransactionBundle: () => executeTransactionBundle,
1226
1217
  failClosedMaterializeResolverFromColumnMap: () => failClosedMaterializeResolverFromColumnMap,
1227
1218
  formatterFor: () => formatterFor,
1228
1219
  getLimitConfig: () => getLimitConfig,
@@ -1237,7 +1228,6 @@ __export(index_exports, {
1237
1228
  leaf: () => leaf,
1238
1229
  leafHandlers: () => leafHandlers,
1239
1230
  leafHandlersAsync: () => leafHandlersAsync,
1240
- lifecycleFor: () => lifecycleFor,
1241
1231
  makeSqlCatalogEntry: () => makeSqlCatalogEntry,
1242
1232
  makeSqlHandler: () => makeSqlHandler,
1243
1233
  makeSqlHandlerSync: () => makeSqlHandlerSync,
@@ -1252,7 +1242,6 @@ __export(index_exports, {
1252
1242
  mysqlPoolFactory: () => mysqlPoolFactory,
1253
1243
  noCastFormatter: () => noCastFormatter,
1254
1244
  opt: () => opt,
1255
- parseEffectPath: () => parseEffectPath,
1256
1245
  parseSchemaColumnTypes: () => parseSchemaColumnTypes,
1257
1246
  pgCastFormatter: () => pgCastFormatter,
1258
1247
  pgConnectionPool: () => pgConnectionPool,
@@ -1293,7 +1282,6 @@ __export(index_exports, {
1293
1282
  setLimitConfig: () => setLimitConfig,
1294
1283
  singlePoolPair: () => singlePoolPair,
1295
1284
  sleep: () => sleep,
1296
- spliceWhere: () => spliceWhere,
1297
1285
  sqlTypeToBcScalar: () => sqlTypeToBcScalar,
1298
1286
  sqlTypeToMaterializeClass: () => sqlTypeToMaterializeClass,
1299
1287
  stripMysqlPkHint: () => stripMysqlPkHint,
@@ -1956,30 +1944,32 @@ function lockTail(opts) {
1956
1944
  return "";
1957
1945
  }
1958
1946
  function compileSelect(desc) {
1959
- const params = [];
1947
+ const headParams = [];
1960
1948
  const selectCols = desc.select || (desc.selectColumn ?? "*");
1961
1949
  const formatter = formatterFor(desc.dialect);
1962
- if (desc.cte?.params && desc.cte.params.length > 0) params.push(...desc.cte.params);
1963
- if (desc.joinParams && desc.joinParams.length > 0) params.push(...desc.joinParams);
1964
- const whereClause2 = conditionsFor(desc.conditions ?? {}, desc.dialect).compile(params, formatter);
1965
- let sql2 = "";
1966
- if (desc.cte) sql2 = `WITH ${desc.cte.name} AS (${desc.cte.sql}) `;
1967
- sql2 += `SELECT ${selectCols} FROM ${desc.tableName}`;
1968
- if (desc.join) sql2 += ` ${desc.join}`;
1969
- if (whereClause2) sql2 += ` WHERE ${whereClause2}`;
1970
- if (desc.group) sql2 += ` GROUP BY ${desc.group}`;
1950
+ if (desc.cte?.params && desc.cte.params.length > 0) headParams.push(...desc.cte.params);
1951
+ if (desc.joinParams && desc.joinParams.length > 0) headParams.push(...desc.joinParams);
1952
+ const whereClause2 = conditionsFor(desc.conditions ?? {}, desc.dialect).compile(headParams, formatter);
1953
+ let head = "";
1954
+ if (desc.cte) head = `WITH ${desc.cte.name} AS (${desc.cte.sql}) `;
1955
+ head += `SELECT ${selectCols} FROM ${desc.tableName}`;
1956
+ if (desc.join) head += ` ${desc.join}`;
1957
+ if (whereClause2) head += ` WHERE ${whereClause2}`;
1958
+ let tail = "";
1959
+ if (desc.group) tail += ` GROUP BY ${desc.group}`;
1971
1960
  const orderClause = typeof desc.order === "string" ? desc.order : orderToString(desc.order);
1972
- if (orderClause) sql2 += ` ORDER BY ${orderClause}`;
1961
+ if (orderClause) tail += ` ORDER BY ${orderClause}`;
1962
+ const tailParams = [];
1973
1963
  const count = (c) => {
1974
1964
  if (!isBoundCount(c)) return String(c);
1975
- params.push(c.bind);
1965
+ tailParams.push(c.bind);
1976
1966
  return "?";
1977
1967
  };
1978
- if (desc.limit !== void 0) sql2 += ` LIMIT ${count(desc.limit)}`;
1979
- if (desc.offset !== void 0) sql2 += ` OFFSET ${count(desc.offset)}`;
1980
- sql2 += lockTail(desc);
1981
- if (desc.append) sql2 += ` ${desc.append}`;
1982
- return { sql: sql2, params };
1968
+ if (desc.limit !== void 0) tail += ` LIMIT ${count(desc.limit)}`;
1969
+ if (desc.offset !== void 0) tail += ` OFFSET ${count(desc.offset)}`;
1970
+ tail += lockTail(desc);
1971
+ if (desc.append) tail += ` ${desc.append}`;
1972
+ return { sql: head + tail, params: [...headParams, ...tailParams], head, headParams, tail, tailParams, lead: whereClause2 ? "AND" : "WHERE" };
1983
1973
  }
1984
1974
 
1985
1975
  // src/scp/makesql/compile-relation.ts
@@ -4507,6 +4497,24 @@ function readerWriterPair(reader, writer) {
4507
4497
  return { reader, writer };
4508
4498
  }
4509
4499
  var DEFAULT_CONNECTION = "default";
4500
+ function assertRoutableNamedDb(db, contextDescription) {
4501
+ if (effectiveConnection(db) === DEFAULT_CONNECTION) return;
4502
+ throw new Error(
4503
+ `scp connection routing: a statement names connection '${db}', but it is executing on ${contextDescription} \u2014 there is no connection registry to resolve the name against. Build the context from a RoutingConfig (setConfig/ConnectionRegistry), or drop the connection tag on the model.`
4504
+ );
4505
+ }
4506
+ function effectiveConnection(db) {
4507
+ return db === void 0 || db === null ? DEFAULT_CONNECTION : db;
4508
+ }
4509
+ function assertTxDbAgrees(db, txDb) {
4510
+ if (db === void 0 || db === null) return;
4511
+ const want = effectiveConnection(db);
4512
+ const open = effectiveConnection(txDb);
4513
+ if (want === open) return;
4514
+ throw new Error(
4515
+ `scp connection routing: a statement names connection '${want}', but it is executing inside a transaction opened on '${open}' \u2014 a transaction is ONE connection on ONE database, so the two cannot both be honored. Open the transaction on '${want}', or issue the statement outside it.`
4516
+ );
4517
+ }
4510
4518
  var ConnectionRegistry = class _ConnectionRegistry {
4511
4519
  /** name → reader/writer pools. */
4512
4520
  connections;
@@ -4527,7 +4535,7 @@ var ConnectionRegistry = class _ConnectionRegistry {
4527
4535
  }
4528
4536
  /** The reader/writer pair for `name` (or {@link DEFAULT_CONNECTION} when `undefined`). Loud on a missing name. */
4529
4537
  pairFor(name) {
4530
- const key = name ?? DEFAULT_CONNECTION;
4538
+ const key = effectiveConnection(name);
4531
4539
  const pair = this.connections.get(key);
4532
4540
  if (pair === void 0) {
4533
4541
  const known = [...this.connections.keys()].map((k) => `'${k}'`).join(", ");
@@ -4575,7 +4583,13 @@ function withWriter(fn) {
4575
4583
  return writerScopeStore.run(true, () => withReadOnlyAsync(fn));
4576
4584
  }
4577
4585
  var WriterStickyClock = class {
4578
- lastWriteAt = 0;
4586
+ /**
4587
+ * `null` until the first `mark()` — absence, NOT a value sentinel. The injectable clocks legitimately
4588
+ * return 0 (a monotonic clock right after process start; rust's `SystemClock` does), so encoding
4589
+ * "never marked" as the value 0 would mis-classify a `mark()` at clock t=0 as unmarked and leak the
4590
+ * read-your-writes read to the reader replica. Absence must be distinct from the value 0.
4591
+ */
4592
+ lastWriteAt = null;
4579
4593
  enabled;
4580
4594
  stickyDurationMs;
4581
4595
  /** Injectable clock (tests advance it); defaults to `Date.now`. */
@@ -4591,12 +4605,12 @@ var WriterStickyClock = class {
4591
4605
  }
4592
4606
  /** Is a read currently sticky-to-writer (within `writerStickyDuration` of the last write)? */
4593
4607
  isSticky() {
4594
- if (!this.enabled || this.lastWriteAt === 0) return false;
4608
+ if (!this.enabled || this.lastWriteAt === null) return false;
4595
4609
  return this.now() - this.lastWriteAt < this.stickyDurationMs;
4596
4610
  }
4597
4611
  /** Reset the clock (e.g. between tests / on `closeAllPools`). */
4598
4612
  reset() {
4599
- this.lastWriteAt = 0;
4613
+ this.lastWriteAt = null;
4600
4614
  }
4601
4615
  };
4602
4616
  function resolvePool(intent, routing) {
@@ -4945,15 +4959,20 @@ function connectionForDriver(driver) {
4945
4959
  var BasicContext = class _BasicContext {
4946
4960
  middleware;
4947
4961
  base;
4948
- /** The pinned tx connection (present ⇒ this is a tx-scoped ctx; every statement uses it). */
4962
+ /** The pinned tx connection (present ⇒ this is a tx-scoped ctx; every statement of the tx's own database (an unnamed one, or one naming that database) uses it). */
4949
4963
  pinned;
4950
4964
  constructor(base, middleware, pinned) {
4951
4965
  this.base = base;
4952
4966
  this.middleware = middleware;
4953
4967
  this.pinned = pinned;
4954
4968
  }
4955
- connectionFor(_intent) {
4956
- return this.pinned ?? this.base;
4969
+ connectionFor(intent) {
4970
+ if (this.pinned !== null) {
4971
+ assertTxDbAgrees(intent.db, null);
4972
+ return this.pinned;
4973
+ }
4974
+ assertRoutableNamedDb(intent.db, "a single-connection (non-routed) execution context");
4975
+ return this.base;
4957
4976
  }
4958
4977
  withConnection(conn, tx) {
4959
4978
  return new _BasicContext(this.base, this.middleware, tx ? conn : null);
@@ -4991,7 +5010,10 @@ var PooledAsyncContext = class {
4991
5010
  }
4992
5011
  connectionFor(intent) {
4993
5012
  const pinned = asyncCtxStore.getStore();
4994
- if (pinned !== void 0) return pinned;
5013
+ if (pinned !== void 0) {
5014
+ assertTxDbAgrees(intent.db, pinned.db);
5015
+ return pinned.conn;
5016
+ }
4995
5017
  const pool = resolvePool(intent, this.routing);
4996
5018
  return {
4997
5019
  async execute(sql2, params) {
@@ -5035,8 +5057,8 @@ var PooledAsyncContext = class {
5035
5057
  function isRoutingConfig(x) {
5036
5058
  return typeof x === "object" && x !== null && "registry" in x && "sticky" in x;
5037
5059
  }
5038
- function runWithPinnedAsyncConnection(conn, fn) {
5039
- return asyncCtxStore.run(conn, fn);
5060
+ function runWithPinnedAsyncConnection(conn, db, fn) {
5061
+ return asyncCtxStore.run({ conn, db }, fn);
5040
5062
  }
5041
5063
  function currentPinnedAsyncConnection() {
5042
5064
  return asyncCtxStore.getStore();
@@ -5047,9 +5069,9 @@ function defaultIsConnectionError(err) {
5047
5069
  return message.includes("Connection terminated") || message.includes("Client has encountered a connection error") || message.includes("ECONNRESET") || message.includes("ECONNREFUSED") || message.includes("Connection lost") || message.includes("This socket has been ended by the other party") || code === "ECONNRESET" || code === "ECONNREFUSED" || code === "EPIPE" || code === "PROTOCOL_CONNECTION_LOST";
5048
5070
  }
5049
5071
  async function withTransactionAsync(ctx, fn, opts = {}, dialect = "postgres", isConnectionError2 = defaultIsConnectionError, connection) {
5050
- const outerConn = currentPinnedAsyncConnection();
5051
- if (outerConn !== void 0) {
5052
- return fn(ctx.withConnection(outerConn, true));
5072
+ const outer = currentPinnedAsyncConnection();
5073
+ if (outer !== void 0) {
5074
+ return fn(ctx.withConnection(outer.conn, true));
5053
5075
  }
5054
5076
  const resolved = resolveTxOptions(opts);
5055
5077
  const begins = beginStatements(dialect, resolved.isolation);
@@ -5060,7 +5082,7 @@ async function withTransactionAsync(ctx, fn, opts = {}, dialect = "postgres", is
5060
5082
  const conn = await pool.acquire();
5061
5083
  const txCtx = ctx.withConnection(conn, true);
5062
5084
  let poisoned = false;
5063
- const attemptResult = await runWithPinnedAsyncConnection(conn, async () => {
5085
+ const attemptResult = await runWithPinnedAsyncConnection(conn, connection ?? null, async () => {
5064
5086
  for (const begin of begins) await runAsync(txCtx, begin, []);
5065
5087
  try {
5066
5088
  const r = await runInTransactionScope(() => fn(txCtx));
@@ -5091,21 +5113,6 @@ async function withTransactionAsync(ctx, fn, opts = {}, dialect = "postgres", is
5091
5113
  function transaction(ctx, fn, options = {}, dialect = "postgres", isConnectionError2 = defaultIsConnectionError, connection) {
5092
5114
  return withTransactionAsync(ctx, () => fn(), options, dialect, isConnectionError2, connection);
5093
5115
  }
5094
- function withTransactionSync(outer, fn, dialect = "sqlite", isolation) {
5095
- const ctx = outer.withConnection(outer.connectionFor({ write: true }), true);
5096
- for (const begin of beginStatements(dialect, isolation)) run(ctx, begin, []);
5097
- try {
5098
- const r = runInTransactionScope(() => fn(ctx));
5099
- run(ctx, r.commit ? "COMMIT" : "ROLLBACK", []);
5100
- return r.value;
5101
- } catch (e) {
5102
- try {
5103
- run(ctx, "ROLLBACK", []);
5104
- } catch {
5105
- }
5106
- throw e;
5107
- }
5108
- }
5109
5116
 
5110
5117
  // src/scp/limit-config.ts
5111
5118
  var globalLimitConfig = {};
@@ -5185,25 +5192,20 @@ function attachToParent(parent, pkCols, byKey, single) {
5185
5192
  }
5186
5193
 
5187
5194
  // src/scp/leaves.ts
5188
- var WHERE_TAIL_RE = /\s+(GROUP BY|ORDER BY|LIMIT|OFFSET|FOR UPDATE|RETURNING)\b/i;
5189
- function spliceWhere(baseSql, whereSql) {
5190
- if (whereSql === "") return baseSql;
5191
- const tail = WHERE_TAIL_RE.exec(baseSql);
5192
- return tail === null ? baseSql + whereSql : baseSql.slice(0, tail.index) + whereSql + baseSql.slice(tail.index);
5193
- }
5194
5195
  function assembleDynamicWhere(p) {
5195
- const frags = p.whereDynamic.frags.filter((f) => !f.skipped);
5196
- let whereSql = "";
5197
- const whereParams = [];
5198
- frags.forEach((f, i) => {
5199
- whereSql += (i === 0 ? " WHERE " : " AND ") + f.sql;
5200
- whereParams.push(...f.params);
5201
- });
5202
- return { sql: spliceWhere(p.sql, whereSql), params: [...whereParams, ...p.params] };
5196
+ const plan = dynamicWherePlan(p.whereDynamic);
5197
+ const frags = plan.frags.filter((f) => !f.skipped);
5198
+ const clause = frags.length === 0 ? "" : ` ${plan.lead} ${frags.map((f) => f.sql).join(" AND ")}`;
5199
+ return {
5200
+ sql: p.sql + clause + plan.tail,
5201
+ params: [...p.params, ...frags.flatMap((f) => f.params), ...plan.tailParams]
5202
+ };
5203
5203
  }
5204
5204
  function effectiveStatement(p) {
5205
- if (p.whereDynamic == null) return p;
5206
- return { ...assembleDynamicWhere({ sql: p.sql, params: p.params, whereDynamic: p.whereDynamic }), write: p.write };
5205
+ const write = p.write !== null;
5206
+ const db = p.db ?? null;
5207
+ if (p.whereDynamic == null) return { sql: p.sql, params: p.params, write, db };
5208
+ return { ...assembleDynamicWhere({ sql: p.sql, params: p.params, whereDynamic: p.whereDynamic }), write, db };
5207
5209
  }
5208
5210
  function toBcInt(v) {
5209
5211
  return typeof v === "bigint" ? v : BigInt(v);
@@ -5225,7 +5227,7 @@ function prepareSql(p, dialect) {
5225
5227
  }
5226
5228
  sql2 = renderPlaceholders(sql2, dialect);
5227
5229
  const bound = encodeParams(p.params, dialect);
5228
- const intent = { write: p.write === true };
5230
+ const intent = { write: p.write, ...p.db != null ? { db: p.db } : {} };
5229
5231
  return { sql: sql2, bound, intent };
5230
5232
  }
5231
5233
  function writeSummary(info) {
@@ -5233,14 +5235,14 @@ function writeSummary(info) {
5233
5235
  }
5234
5236
  function executeSQL(p, ctx) {
5235
5237
  const prepared = prepareSql(effectiveStatement(p), ctx.dialect);
5236
- if (p.write === true && p.returning !== true) return writeSummary(run(ctx.exec, prepared.sql, prepared.bound, prepared.intent));
5238
+ if (p.write !== null && !p.write.returning) return writeSummary(run(ctx.exec, prepared.sql, prepared.bound, prepared.intent));
5237
5239
  const rows = executeSafe(ctx.exec, prepared.sql, prepared.bound, prepared.intent);
5238
5240
  assertRelationHardLimit(rows, p.guard);
5239
5241
  return rows;
5240
5242
  }
5241
5243
  async function executeSQLAsync(p, ctx) {
5242
5244
  const prepared = prepareSql(effectiveStatement(p), ctx.dialect);
5243
- if (p.write === true && p.returning !== true) return writeSummary(await runAsync(ctx.execAsync, prepared.sql, prepared.bound, prepared.intent));
5245
+ if (p.write !== null && !p.write.returning) return writeSummary(await runAsync(ctx.execAsync, prepared.sql, prepared.bound, prepared.intent));
5244
5246
  const rows = await executeAsync(ctx.execAsync, prepared.sql, prepared.bound, prepared.intent);
5245
5247
  assertRelationHardLimit(rows, p.guard);
5246
5248
  return rows;
@@ -5251,40 +5253,106 @@ function pluck(p) {
5251
5253
  }
5252
5254
  function group(p) {
5253
5255
  const byKey = groupByKey(p.children, p.fk);
5254
- return p.parents.map((par) => ({ ...par, [p.into]: attachToParent(par, p.pk, byKey, p.single === true) }));
5256
+ return p.parents.map((par) => ({ ...par, [p.into]: attachToParent(par, p.pk, byKey, p.single) }));
5257
+ }
5258
+ var PORT_TYPES = {
5259
+ bool: (v) => typeof v === "boolean",
5260
+ int: (v) => typeof v === "bigint",
5261
+ string: (v) => typeof v === "string",
5262
+ list: (v) => Array.isArray(v),
5263
+ "string[]": (v) => Array.isArray(v) && v.every((e) => typeof e === "string"),
5264
+ record: (v) => typeof v === "object" && v !== null && !Array.isArray(v)
5265
+ };
5266
+ function portTyped(value, what, declared) {
5267
+ const kind = declared.endsWith("|null") ? declared.slice(0, -"|null".length) : declared;
5268
+ if (kind !== declared && value === null) return null;
5269
+ if (!PORT_TYPES[kind](value)) {
5270
+ const got = JSON.stringify(value, (_k, v) => typeof v === "bigint" ? `${v}n` : v);
5271
+ throw new Error(`scp leaf: ${what} must be ${declared}, got ${got}`);
5272
+ }
5273
+ return value;
5255
5274
  }
5256
- function relationGuardPort(port) {
5257
- if (port === void 0 || port === null) return null;
5258
- const g = port;
5259
- const limit = typeof g.limit === "bigint" ? Number(g.limit) : g.limit;
5260
- if (typeof limit !== "number" || !Number.isInteger(limit) || typeof g.relation !== "string") {
5275
+ function requiredField(record, name, at, declared) {
5276
+ if (!(name in record)) {
5261
5277
  throw new Error(
5262
- `scp leaf executeSQL: the 'guard' port must be a {limit:int, model?:string, relation:string} relation cap, got ${JSON.stringify(port)}`
5278
+ `scp leaf: ${at} is missing its '${name}' field \u2014 a generated module spells every field of every struct it wires, so an ABSENT key is an ABI break (a null VALUE is how an absent write mode / plan / cap is spelled)`
5263
5279
  );
5264
5280
  }
5265
- return { limit, ...typeof g.model === "string" ? { model: g.model } : {}, relation: g.relation };
5281
+ return portTyped(record[name], `${at}'s '${name}'`, declared);
5282
+ }
5283
+ var PLAN_AT = `the 'whereDynamic' plan`;
5284
+ function dynamicWherePlan(port) {
5285
+ const plan = port;
5286
+ const at = `a 'whereDynamic' fragment`;
5287
+ const frags = requiredField(plan, "frags", PLAN_AT, "list").map((frag) => {
5288
+ const f = portTyped(frag, at, "record");
5289
+ return {
5290
+ skipped: requiredField(f, "skipped", at, "bool"),
5291
+ sql: requiredField(f, "sql", at, "string"),
5292
+ params: requiredField(f, "params", at, "list")
5293
+ };
5294
+ });
5295
+ return {
5296
+ frags,
5297
+ lead: requiredField(plan, "lead", PLAN_AT, "string"),
5298
+ tail: requiredField(plan, "tail", PLAN_AT, "string"),
5299
+ tailParams: requiredField(plan, "tailParams", PLAN_AT, "list")
5300
+ };
5301
+ }
5302
+ function relationGuardPort(port) {
5303
+ if (port === null) return null;
5304
+ const g = port;
5305
+ const at = `the 'guard' cap`;
5306
+ const raw = requiredField(g, "limit", at, "int");
5307
+ const model = requiredField(g, "model", at, "string|null");
5308
+ return {
5309
+ limit: Number(raw),
5310
+ ...model === null ? {} : { model },
5311
+ relation: requiredField(g, "relation", at, "string")
5312
+ };
5313
+ }
5314
+ var OPTS_AT = `the 'opts' control record`;
5315
+ function optsField(opts, name) {
5316
+ return requiredField(opts, name, OPTS_AT, "record|null");
5317
+ }
5318
+ function writeModePort(port) {
5319
+ if (port === null) return null;
5320
+ return { returning: requiredField(port, "returning", `the 'write' mode`, "bool") };
5266
5321
  }
5267
5322
  function executeSqlPorts(ports) {
5323
+ const at = "the executeSQL payload";
5324
+ const sql2 = requiredField(ports, "sql", at, "string");
5325
+ const params = requiredField(ports, "params", at, "list");
5326
+ const opts = "opts" in ports ? requiredField(ports, "opts", at, "record|null") : null;
5327
+ if (opts === null) return { sql: sql2, params, write: null };
5268
5328
  return {
5269
- sql: ports.sql,
5270
- params: ports.params,
5271
- write: ports.write === true,
5272
- returning: ports.returning === true,
5273
- bigint: ports.bigint === true,
5274
- whereDynamic: ports.whereDynamic ?? null,
5275
- guard: relationGuardPort(ports.guard)
5329
+ sql: sql2,
5330
+ params,
5331
+ // The NAMED database — the one control field that is a bare `string|null` rather than a struct, so
5332
+ // it is read against its own declared type instead of through `optsField`'s `record|null`.
5333
+ db: requiredField(opts, "db", OPTS_AT, "string|null"),
5334
+ write: writeModePort(optsField(opts, "write")),
5335
+ whereDynamic: optsField(opts, "whereDynamic"),
5336
+ guard: relationGuardPort(optsField(opts, "guard"))
5276
5337
  };
5277
5338
  }
5339
+ var PLUCK_AT = "the pluck payload";
5340
+ var GROUP_AT = "the group payload";
5278
5341
  var shapingHandlers = {
5279
- pluck: (ports) => ({ ok: pluck({ rows: ports.rows, col: ports.col }) }),
5342
+ pluck: (ports) => ({
5343
+ ok: pluck({
5344
+ rows: requiredField(ports, "rows", PLUCK_AT, "list"),
5345
+ col: requiredField(ports, "col", PLUCK_AT, "string[]")
5346
+ })
5347
+ }),
5280
5348
  group: (ports) => ({
5281
5349
  ok: group({
5282
- parents: ports.parents,
5283
- children: ports.children,
5284
- pk: ports.pk,
5285
- fk: ports.fk,
5286
- into: ports.into,
5287
- single: ports.single === true
5350
+ parents: requiredField(ports, "parents", GROUP_AT, "list"),
5351
+ children: requiredField(ports, "children", GROUP_AT, "list"),
5352
+ pk: requiredField(ports, "pk", GROUP_AT, "string[]"),
5353
+ fk: requiredField(ports, "fk", GROUP_AT, "string[]"),
5354
+ into: requiredField(ports, "into", GROUP_AT, "string"),
5355
+ single: requiredField(ports, "single", GROUP_AT, "bool")
5288
5356
  })
5289
5357
  })
5290
5358
  };
@@ -5313,111 +5381,6 @@ function isConnectionError(error) {
5313
5381
  return message.includes("Connection terminated unexpectedly") || message.includes("Connection terminated") || message.includes("Client has encountered a connection error") || message.includes("read ECONNRESET") || message.includes("connect ECONNREFUSED") || message.includes("Connection lost") || message.includes("This socket has been ended by the other party") || code === "ECONNRESET" || code === "ECONNREFUSED" || code === "EPIPE" || code === "EAI_AGAIN" || code === "PROTOCOL_CONNECTION_LOST";
5314
5382
  }
5315
5383
 
5316
- // src/scp/writes.ts
5317
- var ENTITY_ROOT = "__entity";
5318
- var PATH_RE = /^\$\.(input|entity)\.([A-Za-z_][A-Za-z0-9_]*)$/;
5319
- var REF_PATH_RE = /^\$\.ref\.([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)$/;
5320
- function parseEffectPath(value) {
5321
- const m = PATH_RE.exec(value);
5322
- if (m !== null) return { root: m[1], field: m[2] };
5323
- const r = REF_PATH_RE.exec(value);
5324
- if (r !== null) return { root: "ref", writeName: r[1], field: r[2] };
5325
- throw new Error(
5326
- `write-time relations: '${value}' is not a valid path-rooted value; use '$.input.<field>' (the Command input), '$.entity.<field>' (the sole written row), or '$.ref.<writeName>.<field>' (a named upstream write's RETURNING row).`
5327
- );
5328
- }
5329
- function assertPaths(map) {
5330
- for (const v of Object.values(map)) parseEffectPath(v);
5331
- }
5332
- var recorder = {
5333
- lifecycle(effects = {}) {
5334
- for (const r of effects.requires ?? []) assertPaths(r.keys);
5335
- if (effects.idempotency !== void 0) parseEffectPath(effects.idempotency.token);
5336
- for (const u of effects.unique ?? []) {
5337
- u.scope.forEach((p) => parseEffectPath(p));
5338
- u.fields.forEach((p) => parseEffectPath(p));
5339
- }
5340
- for (const d of effects.derive ?? []) assertPaths(d.keys);
5341
- for (const e of effects.edges ?? []) {
5342
- assertPaths(e.columns);
5343
- if (e.where !== void 0) assertPaths(e.where);
5344
- }
5345
- for (const em of effects.emits ?? []) assertPaths(em.payload);
5346
- return { effects: freezeEffects(effects) };
5347
- },
5348
- exists(table, keys) {
5349
- return { kind: "requires", table, keys };
5350
- },
5351
- unique(spec) {
5352
- return { kind: "unique", name: spec.name, guardTable: spec.guardTable, scope: spec.scope, fields: spec.fields };
5353
- },
5354
- increment(table, keys, attribute, amount) {
5355
- if (!Number.isFinite(amount)) {
5356
- throw new Error(`entityWrites: increment amount for '${table}.${attribute}' must be a finite number (got ${String(amount)})`);
5357
- }
5358
- return { kind: "derive", table, keys, attribute, amount };
5359
- },
5360
- edge(spec) {
5361
- if (spec.relation === "fk" && spec.where === void 0) {
5362
- throw new Error(`entityWrites: a 'fk' edge on '${spec.table}' requires a 'where' key map (the matched row's identity)`);
5363
- }
5364
- return {
5365
- kind: "edge",
5366
- relation: spec.relation,
5367
- action: spec.action,
5368
- table: spec.table,
5369
- columns: spec.columns,
5370
- ...spec.where !== void 0 ? { where: spec.where } : {}
5371
- };
5372
- },
5373
- event(name, outboxTable, payload) {
5374
- return { kind: "emit", name, outboxTable, payload };
5375
- },
5376
- idempotentBy(table, column2, token) {
5377
- return { kind: "idempotency", table, column: column2, token };
5378
- }
5379
- };
5380
- function freezeEffects(effects) {
5381
- const out = {};
5382
- if (effects.requires !== void 0) out.requires = effects.requires;
5383
- if (effects.idempotency !== void 0) out.idempotency = effects.idempotency;
5384
- if (effects.unique !== void 0) out.unique = effects.unique;
5385
- if (effects.derive !== void 0) out.derive = effects.derive;
5386
- if (effects.edges !== void 0) out.edges = effects.edges;
5387
- if (effects.emits !== void 0) out.emits = effects.emits;
5388
- return out;
5389
- }
5390
- function entityWrites(builder) {
5391
- const shape = builder(recorder);
5392
- if (shape.create === void 0 && shape.update === void 0 && shape.remove === void 0) {
5393
- throw new Error(
5394
- "entityWrites(...) must declare at least one lifecycle (`create` / `update` / `remove`)."
5395
- );
5396
- }
5397
- return {
5398
- ...shape.create !== void 0 ? { create: shape.create } : {},
5399
- ...shape.update !== void 0 ? { update: shape.update } : {},
5400
- ...shape.remove !== void 0 ? { remove: shape.remove } : {}
5401
- };
5402
- }
5403
- function edgeWrites(builder) {
5404
- const def = entityWrites(builder);
5405
- for (const phase of ["create", "update", "remove"]) {
5406
- const c = def[phase];
5407
- if (c === void 0) continue;
5408
- const e = c.effects;
5409
- if (e.requires !== void 0 || e.unique !== void 0 || e.derive !== void 0 || e.emits !== void 0 || e.idempotency !== void 0) {
5410
- throw new Error(
5411
- `edgeWrites: the '${phase}' lifecycle may declare ONLY 'edges' effects (an edge entity carries only edge writes; use entityWrites for a full save contract).`
5412
- );
5413
- }
5414
- }
5415
- return def;
5416
- }
5417
- function lifecycleFor(def, phase) {
5418
- return def[phase];
5419
- }
5420
-
5421
5384
  // src/scp/makesql/tx.ts
5422
5385
  function literalize(value) {
5423
5386
  if (value === null || value === void 0) return null;
@@ -5430,100 +5393,9 @@ function literalize(value) {
5430
5393
  }
5431
5394
  return value;
5432
5395
  }
5433
- function pathToRef(value) {
5434
- const p = parseEffectPath(value);
5435
- if (p.root === "input") return { ref: [p.field] };
5436
- if (p.root === "entity") return { ref: [ENTITY_ROOT, p.field] };
5437
- return { ref: [p.writeName, p.field] };
5438
- }
5439
- function guardInsert(dialect, table, columns, placeholders2) {
5440
- const cols = `(${columns.join(", ")})`;
5441
- if (dialect === "mysql") return `INSERT IGNORE INTO ${table} ${cols} VALUES (${placeholders2})`;
5442
- return `INSERT INTO ${table} ${cols} VALUES (${placeholders2}) ON CONFLICT DO NOTHING`;
5443
- }
5444
- function makeIdGen() {
5445
- let n = 0;
5446
- return (role) => `tx_${role}_${n++}`;
5447
- }
5448
- function compileRequires(e, nextId) {
5449
- const cols = Object.keys(e.keys);
5450
- if (cols.length === 0) throw new Error(`write-plan: requires on '${e.table}' declares no keys`);
5451
- const whereSql = v1EqualityWhereText(cols);
5452
- const op = {
5453
- sql: `SELECT 1 FROM ${e.table} WHERE ${whereSql}`,
5454
- params: cols.map((c) => pathToRef(e.keys[c]))
5455
- };
5456
- return { id: nextId("requires"), role: "gate:requires", op, gate: "existsElseRollback", label: `requires ${e.table}` };
5457
- }
5458
- function compileIdempotency(e, nextId, dialect) {
5459
- const op = {
5460
- sql: guardInsert(dialect, e.table, [e.column], "?"),
5461
- params: [pathToRef(e.token)]
5462
- };
5463
- return { id: nextId("idem"), role: "gate:idempotency", op, gate: "insertedElseNoop", label: `idempotency ${e.table}` };
5464
- }
5465
- function compileUnique(e, nextId, dialect) {
5466
- const scopeCols = e.scope.map((_, i) => `s${i}`);
5467
- const fieldCols = e.fields.map((_, i) => `f${i}`);
5468
- const cols = ["name", ...scopeCols, ...fieldCols];
5469
- const placeholders2 = cols.map(() => "?").join(", ");
5470
- const params = [
5471
- e.name,
5472
- // literal discriminator (a closed-set string literal)
5473
- ...e.scope.map(pathToRef),
5474
- ...e.fields.map(pathToRef)
5475
- ];
5476
- const op = { sql: guardInsert(dialect, e.guardTable, cols, placeholders2), params };
5477
- return { id: nextId("unique"), role: "gate:unique", op, gate: "insertedElseRollback", label: `unique ${e.name}` };
5478
- }
5479
- function compileDerive(e, nextId) {
5480
- const keyCols = Object.keys(e.keys);
5481
- if (keyCols.length === 0) throw new Error(`write-plan: derive on '${e.table}' declares no keys`);
5482
- const whereSql = v1EqualityWhereText(keyCols);
5483
- const op = {
5484
- sql: `UPDATE ${e.table} SET ${e.attribute} = ${e.attribute} + ? WHERE ${whereSql}`,
5485
- // SET amount is the first param (before the WHERE keys), matching the v1 param order.
5486
- params: [e.amount, ...keyCols.map((c) => pathToRef(e.keys[c]))]
5487
- };
5488
- return { id: nextId("derive"), role: "derive", op, label: `derive ${e.table}.${e.attribute}` };
5489
- }
5490
- function compileEdge(e, nextId) {
5491
- if (e.relation === "m2m") {
5492
- const cols = Object.keys(e.columns);
5493
- if (e.action === "set") {
5494
- const placeholders2 = cols.map(() => "?").join(", ");
5495
- const op3 = {
5496
- sql: `INSERT INTO ${e.table} (${cols.join(", ")}) VALUES (${placeholders2})`,
5497
- params: cols.map((c) => pathToRef(e.columns[c]))
5498
- };
5499
- return { id: nextId("edge"), role: "edge", op: op3, label: `edge m2m link ${e.table}` };
5500
- }
5501
- const whereSql2 = v1EqualityWhereText(cols);
5502
- const op2 = {
5503
- sql: `DELETE FROM ${e.table} WHERE ${whereSql2}`,
5504
- params: cols.map((c) => pathToRef(e.columns[c]))
5505
- };
5506
- return { id: nextId("edge"), role: "edge", op: op2, label: `edge m2m unlink ${e.table}` };
5507
- }
5508
- const setCols = Object.keys(e.columns);
5509
- const whereCols = Object.keys(e.where);
5510
- const setClauses = setCols.map((c) => e.action === "set" ? `${c} = ?` : `${c} = NULL`);
5511
- const setParams = e.action === "set" ? setCols.map((c) => pathToRef(e.columns[c])) : [];
5512
- const whereSql = v1EqualityWhereText(whereCols);
5513
- const op = {
5514
- sql: `UPDATE ${e.table} SET ${setClauses.join(", ")} WHERE ${whereSql}`,
5515
- params: [...setParams, ...whereCols.map((c) => pathToRef(e.where[c]))]
5516
- };
5517
- return { id: nextId("edge"), role: "edge", op, label: `edge fk ${e.action} ${e.table}` };
5518
- }
5519
- function compileEmit(e, nextId) {
5520
- const payloadObj = {};
5521
- for (const [k, v] of Object.entries(e.payload)) payloadObj[k] = pathToRef(v);
5522
- const op = {
5523
- sql: `INSERT INTO ${e.outboxTable} (type, payload) VALUES (?, ?)`,
5524
- params: [e.name, { obj: payloadObj }]
5525
- };
5526
- return { id: nextId("emit"), role: "emit", op, label: `emit ${e.name}` };
5396
+ var ENTITY_ROOT = "__entity";
5397
+ function isBatchPlan(plan) {
5398
+ return plan.entityFrom === null && plan.statements.every((s) => s.binds === void 0 && s.role === "body");
5527
5399
  }
5528
5400
  function stringPort(ports, name) {
5529
5401
  const v = ports[name];
@@ -5580,11 +5452,6 @@ function v1ConditionText(conditions) {
5580
5452
  const probe = [];
5581
5453
  return new DBConditions(conditions).compile(probe);
5582
5454
  }
5583
- function v1EqualityWhereText(columns) {
5584
- const conditions = {};
5585
- for (const c of columns) conditions[c] = PROBE;
5586
- return v1ConditionText(conditions);
5587
- }
5588
5455
  function lowerWhereMember(node, at, dialect) {
5589
5456
  const op = opKey(node);
5590
5457
  if (op === void 0) throw new Error(`compileWriteNode: ${at}: a where member must be a single-operator Expression node`);
@@ -5722,7 +5589,7 @@ function compileWriteNodeSql(node, dialect, resolveColumnType) {
5722
5589
  const sorted = [...cols].sort();
5723
5590
  if (stringPort(ports, "batch") === "true") {
5724
5591
  if (dialect === "postgres") {
5725
- if (resolveColumnType === void 0) throw new Error(`compileWriteNode: batch insert on postgres needs the column-type resolver (schema SoT) to derive the UNNEST element casts \u2014 pass it through compileCreateManyBundle/compileWriteBundle (the decorator-adapter write path).`);
5592
+ if (resolveColumnType === void 0) throw new Error(`compileWriteNode: batch insert on postgres needs the column-type resolver (schema SoT) to derive the UNNEST element casts \u2014 pass it through compileCreateManyBundle (the decorator-adapter batch write path).`);
5726
5593
  return pgBatchInsert(table, sorted, values, ports, resolveColumnType, dialect);
5727
5594
  }
5728
5595
  const shapeOpts = { tableName: table, columns: sorted, records: [], ...onConflictJsonOpts(ports, sorted), ...stringPort(ports, "returning") !== void 0 ? { returning: stringPort(ports, "returning") } : {} };
@@ -5750,7 +5617,7 @@ function compileWriteNodeSql(node, dialect, resolveColumnType) {
5750
5617
  if (keyCols.length === 0) throw new Error(`compileWriteNode: batch Update requires at least one 'key.<field>' port`);
5751
5618
  const updateCols = [...setCols].sort();
5752
5619
  if (dialect === "postgres") {
5753
- if (resolveColumnType === void 0) throw new Error(`compileWriteNode: batch update on postgres needs the column-type resolver (schema SoT) to derive the UNNEST element casts \u2014 pass it through compileCreateManyBundle/compileWriteBundle (the decorator-adapter write path).`);
5620
+ if (resolveColumnType === void 0) throw new Error(`compileWriteNode: batch update on postgres needs the column-type resolver (schema SoT) to derive the UNNEST element casts \u2014 pass it through compileCreateManyBundle (the decorator-adapter batch write path).`);
5754
5621
  return pgBatchUpdate(table, keyCols, updateCols, key, set, ports, resolveColumnType, dialect);
5755
5622
  }
5756
5623
  const shapeOpts = { tableName: table, keyColumns: keyCols, updateColumns: updateCols, records: [], ...stringPort(ports, "returning") !== void 0 ? { returning: stringPort(ports, "returning") } : {} };
@@ -5782,133 +5649,9 @@ function compileWriteNodeSql(node, dialect, resolveColumnType) {
5782
5649
  throw new Error(`compileWriteNode: catalog component '${component}' has no write compile (SQL writes: Insert/Update/Delete)`);
5783
5650
  }
5784
5651
  }
5785
- function compileWriteGroup(base, lifecycle, nextId, dialect, seqRef) {
5786
- const e = lifecycle.effects;
5787
- const nodes = [];
5788
- const mk = (stmt, isGate) => {
5789
- const consumes = refHeadsOf(stmt).filter((h) => h !== ENTITY_ROOT && !isInputHead(h, stmt));
5790
- return { stmt, seq: seqRef.n++, consumes, produces: stmt.binds ?? null, isGate };
5791
- };
5792
- for (const r of e.requires ?? []) nodes.push(mk(compileRequires(r, nextId), true));
5793
- if (e.idempotency !== void 0) nodes.push(mk(compileIdempotency(e.idempotency, nextId, dialect), true));
5794
- for (const u of e.unique ?? []) nodes.push(mk(compileUnique(u, nextId, dialect), true));
5795
- const bodyId = nextId("body");
5796
- const bodyStmt = {
5797
- id: bodyId,
5798
- role: "body",
5799
- op: base.op,
5800
- label: base.label,
5801
- ...base.name !== void 0 ? { binds: base.name } : {}
5802
- };
5803
- nodes.push(mk(bodyStmt, false));
5804
- for (const d of e.derive ?? []) nodes.push(mk(compileDerive(d, nextId), false));
5805
- for (const ed of e.edges ?? []) nodes.push(mk(compileEdge(ed, nextId), false));
5806
- for (const em of e.emits ?? []) nodes.push(mk(compileEmit(em, nextId), false));
5807
- return { nodes, bodyId };
5808
- }
5809
- function topoOrder(nodes) {
5810
- const byName = /* @__PURE__ */ new Map();
5811
- for (const n of nodes) {
5812
- if (n.produces !== null) {
5813
- if (byName.has(n.produces)) {
5814
- throw new Error(`write-plan: two writes both bind the name '${n.produces}' \u2014 write names must be unique in a composite Command.`);
5815
- }
5816
- byName.set(n.produces, n);
5817
- }
5818
- }
5819
- const succ = /* @__PURE__ */ new Map();
5820
- const indeg = /* @__PURE__ */ new Map();
5821
- const nodeById = /* @__PURE__ */ new Map();
5822
- for (const n of nodes) {
5823
- nodeById.set(n.stmt.id, n);
5824
- succ.set(n.stmt.id, /* @__PURE__ */ new Set());
5825
- indeg.set(n.stmt.id, 0);
5826
- }
5827
- const addEdge = (fromId, toId) => {
5828
- if (fromId === toId) return;
5829
- const s = succ.get(fromId);
5830
- if (!s.has(toId)) {
5831
- s.add(toId);
5832
- indeg.set(toId, indeg.get(toId) + 1);
5833
- }
5834
- };
5835
- for (const n of nodes) {
5836
- for (const dep of n.consumes) {
5837
- const producer = byName.get(dep);
5838
- if (producer === void 0) {
5839
- throw new Error(
5840
- `write-plan: statement '${n.stmt.label}' references '$.ref.${dep}.*' but no write in this Command binds the name '${dep}' \u2014 a dangling write reference (fail-closed; no silent skip).`
5841
- );
5842
- }
5843
- addEdge(producer.stmt.id, n.stmt.id);
5844
- }
5845
- }
5846
- const gates = nodes.filter((n) => n.isGate);
5847
- const nonGates = nodes.filter((n) => !n.isGate);
5848
- for (const g of gates) for (const b of nonGates) addEdge(g.stmt.id, b.stmt.id);
5849
- const ordered = [];
5850
- const ready = nodes.filter((n) => indeg.get(n.stmt.id) === 0).sort((a, b) => a.seq - b.seq);
5851
- while (ready.length > 0) {
5852
- const n = ready.shift();
5853
- ordered.push(n.stmt);
5854
- for (const depId of succ.get(n.stmt.id)) {
5855
- indeg.set(depId, indeg.get(depId) - 1);
5856
- if (indeg.get(depId) === 0) {
5857
- const dn = nodeById.get(depId);
5858
- let lo = 0;
5859
- while (lo < ready.length && ready[lo].seq < dn.seq) lo++;
5860
- ready.splice(lo, 0, dn);
5861
- }
5862
- }
5863
- }
5864
- if (ordered.length !== nodes.length) {
5865
- const stuck = nodes.filter((n) => !ordered.includes(n.stmt)).map((n) => n.stmt.label);
5866
- throw new Error(
5867
- `write-plan: the write-time transaction DAG has a CYCLE (unresolvable ordering among [${stuck.join(", ")}]). This is either a mutual '$.ref' data dependency or a gate that depends on a body row (a gate cannot depend on a not-yet-written row). ESCALATE: the Command's write dependencies are contradictory \u2014 no order satisfies both the data dependencies and gate-first. (Fail-closed; the derivation never guesses an order.)`
5868
- );
5869
- }
5870
- return ordered;
5871
- }
5872
- function deriveTransactionPlan(phase, bases, lifecycle, dialect = "sqlite") {
5873
- if (bases.length === 0) {
5874
- throw new Error("write-plan: a Command must declare at least one base write (Insert/Update/Delete).");
5875
- }
5876
- const nextId = makeIdGen();
5877
- const seqRef = { n: 0 };
5878
- const composite = bases.length > 1;
5879
- const allNodes = [];
5880
- const bodyIds = [];
5881
- bases.forEach((base, i) => {
5882
- const lc = base.effects !== void 0 ? { effects: base.effects } : i === 0 ? lifecycle : { effects: {} };
5883
- const { nodes, bodyId } = compileWriteGroup(base, lc, nextId, dialect, seqRef);
5884
- allNodes.push(...nodes);
5885
- bodyIds.push(bodyId);
5886
- });
5887
- const statements = topoOrder(allNodes);
5888
- const soleBody = bases[0];
5889
- const soleBodyId = bodyIds[0];
5890
- const usesEntity = referencesHead(statements, ENTITY_ROOT);
5891
- const soleBodyReturns = /\breturning\b/i.test(soleBody.op.sql);
5892
- if (usesEntity && composite) {
5893
- throw new Error(
5894
- `write-plan: a composite (multi-write) Command uses '$.entity.*' \u2014 ambiguous which write it denotes. Address each write's row by name via '$.ref.<writeName>.<field>' (fail-closed).`
5895
- );
5896
- }
5897
- if (usesEntity && !soleBodyReturns) {
5898
- throw new Error(
5899
- `write-plan: a derive/edges/emits stage references '$.entity.*' but the body write ('${soleBody.label}') has no RETURNING clause \u2014 the written row cannot be exposed to later stages. Add a RETURNING to the base write (fail-closed; no absent-entity default).`
5900
- );
5901
- }
5902
- const entityFrom = usesEntity || !composite && soleBodyReturns ? soleBodyId : null;
5903
- for (const base of bases) {
5904
- if (base.name === void 0) continue;
5905
- if (referencesHead(statements, base.name) && !/\breturning\b/i.test(base.op.sql)) {
5906
- throw new Error(
5907
- `write-plan: write '${base.name}' is referenced as '$.ref.${base.name}.*' by a later statement but its op has no RETURNING clause \u2014 its row cannot be bound for downstream writes. Add a RETURNING to write '${base.name}' (fail-closed; no absent-row default).`
5908
- );
5909
- }
5910
- }
5911
- return { phase, entityFrom, statements, onIdempotentHit: "rollback" };
5652
+ function makeIdGen() {
5653
+ let n = 0;
5654
+ return (role) => `tx_${role}_${n++}`;
5912
5655
  }
5913
5656
  function deriveBatchPlan(phase, ops) {
5914
5657
  if (ops.length === 0) {
@@ -5921,56 +5664,7 @@ function deriveBatchPlan(phase, ops) {
5921
5664
  op: { sql: op.sql, params: op.params.map((p) => literalize(p)) },
5922
5665
  label: op.label ?? `batch[${i}]`
5923
5666
  }));
5924
- return { phase, entityFrom: null, statements, onIdempotentHit: "rollback" };
5925
- }
5926
- function refHeadsOf(stmt) {
5927
- const heads = /* @__PURE__ */ new Set();
5928
- collectRefHeads(stmt.op.params, heads);
5929
- return [...heads];
5930
- }
5931
- function isInputHead(head, stmt) {
5932
- return refPathLengthFor(stmt.op.params, head) === 1;
5933
- }
5934
- function refPathLengthFor(node, head) {
5935
- let maxLen = 0;
5936
- const walk = (n) => {
5937
- if (n === null || typeof n !== "object") return;
5938
- if (Array.isArray(n)) {
5939
- for (const el of n) walk(el);
5940
- return;
5941
- }
5942
- const keys = Object.keys(n);
5943
- if (keys.length === 1 && keys[0] === "ref") {
5944
- const path = n.ref;
5945
- if (Array.isArray(path) && path[0] === head) maxLen = Math.max(maxLen, path.length);
5946
- return;
5947
- }
5948
- for (const k of keys) walk(n[k]);
5949
- };
5950
- walk(node);
5951
- return maxLen;
5952
- }
5953
- function referencesHead(statements, head) {
5954
- const heads = /* @__PURE__ */ new Set();
5955
- for (const s of statements) collectRefHeads(s.op.params, heads);
5956
- return heads.has(head);
5957
- }
5958
- function collectRefHeads(node, heads) {
5959
- if (node === null || typeof node !== "object") return;
5960
- if (Array.isArray(node)) {
5961
- for (const el of node) collectRefHeads(el, heads);
5962
- return;
5963
- }
5964
- const keys = Object.keys(node);
5965
- if (keys.length === 1 && keys[0] === "ref") {
5966
- const path = node.ref;
5967
- if (Array.isArray(path) && typeof path[0] === "string") heads.add(path[0]);
5968
- return;
5969
- }
5970
- for (const k of keys) collectRefHeads(node[k], heads);
5971
- }
5972
- function asContext(dbOrCtx) {
5973
- return "connectionFor" in dbOrCtx ? dbOrCtx : contextForDriver(dbOrCtx);
5667
+ return { phase, entityFrom: null, statements };
5974
5668
  }
5975
5669
  function toDriverParam(v, dialect) {
5976
5670
  if (typeof v === "bigint") {
@@ -5989,78 +5683,13 @@ function renderStatement(op, scope, dialect) {
5989
5683
  const { sql: sql2, params } = evalAssemble(op, scope, dialect);
5990
5684
  return { sql: renderPlaceholders(sql2, dialect), params };
5991
5685
  }
5992
- function execStatement(ctx, op, scope, dialect) {
5993
- const { sql: sql2, params } = evalAssemble(op, scope, dialect);
5994
- const hasReturn = /\bselect\b/i.test(sql2.slice(0, 8)) || /\breturning\b/i.test(sql2);
5995
- const out = executeSQL({ sql: sql2, params, write: true, returning: hasReturn, bigint: false }, { exec: ctx, dialect });
5996
- return hasReturn ? { rows: out, changes: out.length } : { rows: [], changes: Number(out[0]?.changes ?? 0) };
5997
- }
5998
- function gateShortCircuit(gate, result) {
5999
- switch (gate) {
6000
- case "existsElseRollback":
6001
- return result.rows.length === 0 ? "requires_absent" : null;
6002
- case "insertedElseRollback":
6003
- return result.changes === 0 ? "unique_collision" : null;
6004
- case "insertedElseNoop":
6005
- return result.changes === 0 ? "idempotent_duplicate" : null;
6006
- default:
6007
- throw new Error(`scp write: unknown gate rule '${String(gate)}'`);
6008
- }
6009
- }
6010
- function executeTransaction(db, plan, input, dialect = "sqlite") {
6011
- const outer = asContext(db);
6012
- const executed = [];
6013
- const scope = { ...input };
6014
- let entity = null;
6015
- const isBatch = plan.entityFrom === null && plan.statements.every((s) => s.gate === void 0 && s.binds === void 0 && s.role === "body");
6016
- const returnedRows = [];
6017
- try {
6018
- return withTransactionSync(
6019
- outer,
6020
- (ctx) => {
6021
- for (const stmt of plan.statements) {
6022
- const result = execStatement(ctx, stmt.op, scope, dialect);
6023
- executed.push(stmt.id);
6024
- if (stmt.gate !== void 0) {
6025
- const reason = gateShortCircuit(stmt.gate, result);
6026
- if (reason !== null) {
6027
- return { commit: false, value: { committed: false, shortCircuit: { statementId: stmt.id, reason }, entity: null, executed } };
6028
- }
6029
- }
6030
- if (stmt.id === plan.entityFrom) {
6031
- entity = result.rows.length > 0 ? result.rows[0] : null;
6032
- if (entity !== null) scope[ENTITY_ROOT] = entity;
6033
- }
6034
- if (stmt.binds !== void 0 && result.rows.length > 0) {
6035
- scope[stmt.binds] = result.rows[0];
6036
- }
6037
- if (isBatch && stmt.role === "body" && result.rows.length > 0) returnedRows.push(result.rows);
6038
- }
6039
- return { commit: true, value: { committed: true, entity, executed, ...returnedRows.length > 0 ? { returnedRows } : {} } };
6040
- },
6041
- dialect
6042
- );
6043
- } catch (e) {
6044
- throw mapSqliteError(e);
6045
- }
6046
- }
6047
- function countingDriver(db) {
6048
- const prepared = [];
6049
- const wrapped = {
6050
- prepare(sql2) {
6051
- prepared.push(sql2);
6052
- return db.prepare(sql2);
6053
- }
6054
- };
6055
- return { db: wrapped, prepared };
6056
- }
6057
5686
  function renderTxStatement(op, scope, dialect = "sqlite") {
6058
5687
  return renderStatement(op, scope, dialect);
6059
5688
  }
6060
5689
  async function execStatementAsync(ctx, op, scope, dialect) {
6061
5690
  const { sql: sql2, params } = evalAssemble(op, scope, dialect);
6062
5691
  const hasReturn = /\bselect\b/i.test(sql2.slice(0, 8)) || /\breturning\b/i.test(sql2);
6063
- const out = await executeSQLAsync({ sql: sql2, params, write: true, returning: hasReturn, bigint: false }, { execAsync: ctx, dialect });
5692
+ const out = await executeSQLAsync({ sql: sql2, params, write: { returning: hasReturn } }, { execAsync: ctx, dialect });
6064
5693
  return hasReturn ? { rows: out, changes: out.length } : { rows: [], changes: Number(out[0]?.changes ?? 0) };
6065
5694
  }
6066
5695
  function executeTransactionAsync(ctx, plan, input, dialect = "sqlite", options = {}) {
@@ -6073,7 +5702,7 @@ function executeTransactionAsync(ctx, plan, input, dialect = "sqlite", options =
6073
5702
  return runTransactionPlanAsync(ctx, plan, input, dialect, options);
6074
5703
  }
6075
5704
  function runTransactionPlanAsync(ctx, plan, input, dialect, options) {
6076
- const isBatch = plan.entityFrom === null && plan.statements.every((s) => s.gate === void 0 && s.binds === void 0 && s.role === "body");
5705
+ const batch = isBatchPlan(plan);
6077
5706
  return withTransactionAsync(ctx, async (txCtx) => {
6078
5707
  const executed = [];
6079
5708
  const scope = { ...input };
@@ -6082,12 +5711,6 @@ function runTransactionPlanAsync(ctx, plan, input, dialect, options) {
6082
5711
  for (const stmt of plan.statements) {
6083
5712
  const result = await execStatementAsync(txCtx, stmt.op, scope, dialect);
6084
5713
  executed.push(stmt.id);
6085
- if (stmt.gate !== void 0) {
6086
- const reason = gateShortCircuit(stmt.gate, result);
6087
- if (reason !== null) {
6088
- throw new GateShortCircuit(stmt.id, reason, executed);
6089
- }
6090
- }
6091
5714
  if (stmt.id === plan.entityFrom) {
6092
5715
  entity = result.rows.length > 0 ? result.rows[0] : null;
6093
5716
  if (entity !== null) scope[ENTITY_ROOT] = entity;
@@ -6095,28 +5718,13 @@ function runTransactionPlanAsync(ctx, plan, input, dialect, options) {
6095
5718
  if (stmt.binds !== void 0 && result.rows.length > 0) {
6096
5719
  scope[stmt.binds] = result.rows[0];
6097
5720
  }
6098
- if (isBatch && stmt.role === "body" && result.rows.length > 0) returnedRows.push(result.rows);
5721
+ if (batch && stmt.role === "body" && result.rows.length > 0) returnedRows.push(result.rows);
6099
5722
  }
6100
5723
  return { committed: true, entity, executed, ...returnedRows.length > 0 ? { returnedRows } : {} };
6101
5724
  }, options, dialect === "sqlite" ? "postgres" : dialect, isConnectionError).catch((e) => {
6102
- if (e instanceof GateShortCircuit) {
6103
- return { committed: false, shortCircuit: { statementId: e.statementId, reason: e.reason }, entity: null, executed: e.executed };
6104
- }
6105
5725
  throw mapSqliteError(e);
6106
5726
  });
6107
5727
  }
6108
- var GateShortCircuit = class extends Error {
6109
- constructor(statementId, reason, executed) {
6110
- super(`scp write: gate short-circuit at '${statementId}' (${reason})`);
6111
- this.statementId = statementId;
6112
- this.reason = reason;
6113
- this.executed = executed;
6114
- this.name = "GateShortCircuit";
6115
- }
6116
- statementId;
6117
- reason;
6118
- executed;
6119
- };
6120
5728
 
6121
5729
  // src/scp/makesql/pool-executor.ts
6122
5730
  var PG_OID = { INT2: 21, INT4: 23, INT8: 20, DATE: 1082, TIMESTAMP: 1114, TIMESTAMPTZ: 1184, TIME: 1083, TIMETZ: 1266 };
@@ -6310,8 +5918,8 @@ function declarationStub(leafName, args) {
6310
5918
  );
6311
5919
  }
6312
5920
  var Db = class {
6313
- static executeSQL(sql2, params, write, returning, bigint, whereDynamic, guard) {
6314
- return declarationStub("executeSQL", [sql2, params, write, returning, bigint, whereDynamic, guard]);
5921
+ static executeSQL(sql2, params, opts) {
5922
+ return declarationStub("executeSQL", [sql2, params, opts]);
6315
5923
  }
6316
5924
  static pluck(rows, col) {
6317
5925
  return declarationStub("pluck", [rows, col]);
@@ -6514,11 +6122,7 @@ function targetKeyCols(op) {
6514
6122
  }
6515
6123
  function relationGuard(op) {
6516
6124
  if (op.hardLimit === void 0) return null;
6517
- return {
6518
- limit: op.hardLimit,
6519
- ...op.targetTable !== void 0 ? { model: op.targetTable } : {},
6520
- relation: op.name
6521
- };
6125
+ return { limit: op.hardLimit, model: op.targetTable, relation: op.name };
6522
6126
  }
6523
6127
  function bindKeys(op, tuples) {
6524
6128
  if (op.parentKeys !== void 0) return [encodeJsonParam(op.dialect, tuples.map((t) => [...t]))];
@@ -6536,7 +6140,10 @@ function runRelationOp(op, parents, db) {
6536
6140
  const tCols = targetKeyCols(op);
6537
6141
  const childCols = op.materializers;
6538
6142
  const boundParams = bindKeys(op, keys);
6539
- const rawRows = executeSafe(ctx, sql2, boundParams);
6143
+ const rawRows = executeSafe(ctx, sql2, boundParams, {
6144
+ write: false,
6145
+ ...op.connection !== void 0 ? { db: op.connection } : {}
6146
+ });
6540
6147
  assertRelationHardLimit(rawRows, relationGuard(op));
6541
6148
  const rows = materializeChildRows(rawRows, childCols);
6542
6149
  return { sql: sql2, keys, batch: groupByKey(rows, tCols) };
@@ -6565,7 +6172,6 @@ function distributeToParent(op, parent, batch) {
6565
6172
  function toPortableScalar(scalar) {
6566
6173
  return scalar;
6567
6174
  }
6568
- var SHORT_CIRCUIT_TYPE = { obj: { statementId: "string", reason: "string" } };
6569
6175
  function targetAndReturning(sql2) {
6570
6176
  const s = sql2.trim();
6571
6177
  const ins = /^INSERT\s+(?:IGNORE\s+|OR\s+IGNORE\s+)?INTO\s+([A-Za-z_][A-Za-z0-9_]*)/i.exec(s);
@@ -6600,9 +6206,6 @@ function rowObjType(table, returning, resolve) {
6600
6206
  function bodyStatements(plan) {
6601
6207
  return plan.statements.filter((s) => s.role === "body");
6602
6208
  }
6603
- function isBatchPlan(plan) {
6604
- return plan.entityFrom === null && plan.statements.length > 0 && plan.statements.every((s) => s.gate === void 0 && s.binds === void 0 && s.role === "body");
6605
- }
6606
6209
  function writeTargetReturning(plan) {
6607
6210
  const bodies = bodyStatements(plan);
6608
6211
  if (bodies.length === 0) {
@@ -6639,7 +6242,6 @@ function deriveWriteOutputType(plan, resolve) {
6639
6242
  const fields = {
6640
6243
  committed: "bool",
6641
6244
  executed: { arr: "string" },
6642
- shortCircuit: { opt: SHORT_CIRCUIT_TYPE },
6643
6245
  entity: { opt: row }
6644
6246
  };
6645
6247
  if (isBatchPlan(plan) && hasReturning) fields.returnedRows = { opt: { arr: { arr: row } } };
@@ -6656,27 +6258,6 @@ function annotateWriteBundleOutType(bundle, resolve) {
6656
6258
  }
6657
6259
 
6658
6260
  // src/scp/write-bundle.ts
6659
- function writeLabelOf(sql2) {
6660
- const m = /^\s*(INSERT|UPDATE|DELETE)/i.exec(sql2);
6661
- return m === null ? "Write" : m[1][0].toUpperCase() + m[1].slice(1).toLowerCase();
6662
- }
6663
- function compileWriteBundle(name, base, writes, phase, dialectName = "sqlite", resolveColumnType) {
6664
- const lifecycle = lifecycleFor(writes, phase);
6665
- if (lifecycle === void 0) {
6666
- throw new Error(`scp write: the '${phase}' lifecycle is not declared in the entityWrites save contract`);
6667
- }
6668
- const plan = deriveTransactionPlan(phase, [{ op: base, label: writeLabelOf(base.sql) }], lifecycle, dialectName);
6669
- const bundle = { dialect: dialectName, name, statement: base, transaction: plan };
6670
- return resolveColumnType === void 0 ? bundle : annotateWriteBundleOutType(bundle, resolveColumnType);
6671
- }
6672
- function compileCompositeWriteBundle(entries, phase, dialectName = "sqlite") {
6673
- if (entries.length < 2) {
6674
- throw new Error("scp write: a composite write bundle needs at least 2 named write members (use compileWriteBundle for a single write).");
6675
- }
6676
- const bases = entries.map((e) => ({ op: e.base, label: `${writeLabelOf(e.base.sql)} ${e.name}`, name: e.name, effects: e.lifecycle.effects }));
6677
- const plan = deriveTransactionPlan(phase, bases, { effects: {} }, dialectName);
6678
- return { dialect: dialectName, name: entries[0].name, statement: entries[0].base, transaction: plan };
6679
- }
6680
6261
  function flattenBatchOp(node, label) {
6681
6262
  const assembled = assembleMakeSQL(node);
6682
6263
  return { sql: assembled.sql, params: assembled.params, label };
@@ -6708,14 +6289,11 @@ function compileDeleteManyBundle(name, options, dialectName = "sqlite") {
6708
6289
  const components = compileDeleteMany({ dialect: dialectName, ...options });
6709
6290
  const ops = components.map((c, i) => flattenBatchOp(c, `deleteMany group ${i}`));
6710
6291
  if (ops.length === 0) {
6711
- return { dialect: dialectName, name, statement: { sql: "", params: [] }, transaction: { phase: "remove", entityFrom: null, statements: [], onIdempotentHit: "rollback" } };
6292
+ return { dialect: dialectName, name, statement: { sql: "", params: [] }, transaction: { phase: "remove", entityFrom: null, statements: [] } };
6712
6293
  }
6713
6294
  const plan = deriveBatchPlan("remove", ops);
6714
6295
  return { dialect: dialectName, name, statement: { sql: ops[0].sql, params: ops[0].params }, transaction: plan };
6715
6296
  }
6716
- function executeTransactionBundle(bundle, input, options) {
6717
- return executeTransaction(options.db, bundle.transaction, input, bundle.dialect);
6718
- }
6719
6297
 
6720
6298
  // src/decorators.ts
6721
6299
  var import_reflect_metadata = __toESM(require_Reflect());
@@ -7236,6 +6814,9 @@ function deriveModelColumns(modelClass, options = {}) {
7236
6814
  function tableNameOf(modelClass) {
7237
6815
  return modelClass.TABLE_NAME ?? modelClass.name.toLowerCase();
7238
6816
  }
6817
+ function connectionOf(modelClass) {
6818
+ return modelClass.CONNECTION;
6819
+ }
7239
6820
  function compileCreateBundle(modelClass, name, options, dialect = "sqlite", columnsOptions) {
7240
6821
  const resolveColumnType = modelColumnResolver(modelClass, columnsOptions);
7241
6822
  return compileCreateManyBundle(name, options, dialect, resolveColumnType);
@@ -7276,12 +6857,14 @@ function relationDeclOf(rel, resolveTargetModel, dialect = "sqlite") {
7276
6857
  const targetTable = tableNameOf(targetModel);
7277
6858
  const select = targetProjection(targetModel);
7278
6859
  const order = rel.options?.order ? orderToString(rel.options.order()) : void 0;
6860
+ const connection = connectionOf(targetModel);
7279
6861
  const base = {
7280
6862
  name: rel.propertyKey,
7281
6863
  kind: rel.type,
7282
6864
  targetTable,
7283
6865
  select,
7284
6866
  dialect,
6867
+ ...connection !== void 0 ? { connection } : {},
7285
6868
  ...order !== void 0 ? { order } : {},
7286
6869
  ...rel.options?.limit !== void 0 ? { limit: rel.options.limit } : {},
7287
6870
  ...rel.options?.hardLimit !== void 0 ? { hardLimit: rel.options.hardLimit } : {}
@@ -7373,6 +6956,20 @@ var EmitContext = class {
7373
6956
  bindTypeOf(model, column2) {
7374
6957
  return tsScalar(sqlTypeToBcScalar(this.sqlTypeOf(model, column2)));
7375
6958
  }
6959
+ /**
6960
+ * The NAMED connection every statement of an endpoint declared over `model` runs on — read from the
6961
+ * model, the ONE authority ({@link connectionOf}), and handed to {@link execOptions} as its `db`
6962
+ * field. `{}` ⇒ the DEFAULT connection, which leaves a bounded read's option record unspelled
6963
+ * entirely (native-clean).
6964
+ *
6965
+ * A RELATION's child fetch does NOT come through here: its connection is the TARGET model's, which
6966
+ * `relationDeclOf` already resolved onto the compiled op (`RelationOp.connection`) — the emitter
6967
+ * consumes that rather than re-deriving it from a second model handle.
6968
+ */
6969
+ dbOf(model) {
6970
+ const db = connectionOf(model);
6971
+ return db !== void 0 ? { db } : {};
6972
+ }
7376
6973
  // ── methods ────────────────────────────────────────────────────────────────────────────────
7377
6974
  method(name, endpoint) {
7378
6975
  assertIdentifier(name, `endpoint name '${name}'`);
@@ -7436,12 +7033,7 @@ var EmitContext = class {
7436
7033
  const readResolve = endpoint.view !== void 0 ? (t, c) => resolve(t === alias ? baseTable : t, c) : resolve;
7437
7034
  const cap = this.bakedFindCap(endpoint);
7438
7035
  const where = endpoint.where ?? [];
7439
- const dynamic = where.some(isOptional);
7440
- if (dynamic && endpoint.view !== void 0) {
7441
- throw new Error(
7442
- `emit: endpoint '${name}': a QUERY view-model binds its own CTE params BEFORE the WHERE, and a dynamic (SKIP) WHERE is assembled at execution time \u2014 the two cannot share one param order. Declare the view's own predicate inside the view query, or drop the optional predicate.`
7443
- );
7444
- }
7036
+ const optional = where.filter(isOptional);
7445
7037
  const params = [];
7446
7038
  for (const p of where) this.declareWhereParams(model, p, params);
7447
7039
  const limit = this.page(endpoint.limit ?? (cap !== null ? cap + 1 : void 0), params);
@@ -7450,15 +7042,21 @@ var EmitContext = class {
7450
7042
  dialect: this.spec.dialect,
7451
7043
  tableName: table,
7452
7044
  select: projection.join(", "),
7453
- ...dynamic ? {} : { conditions: this.conditions(model, where) },
7045
+ // The BOUNDED predicates — this read's static WHERE, whatever else it declares. (An all-optional
7046
+ // read compiles an EMPTY condition set, which is the same no-WHERE statement it always was.)
7047
+ conditions: this.conditions(model, where.filter((p) => !isOptional(p))),
7454
7048
  ...endpoint.view !== void 0 ? { cte: cteOf(endpoint.view) } : {},
7455
7049
  ...endpoint.order !== void 0 ? { order: endpoint.order } : {},
7456
7050
  ...limit !== void 0 ? { limit } : {},
7457
7051
  ...offset !== void 0 ? { offset } : {},
7458
7052
  ...endpoint.lock === "update" ? { forUpdate: true } : endpoint.lock === "share" ? { forShare: true } : {}
7459
7053
  });
7460
- const bigint = endpoint.bigint === true;
7461
- const call = `${quote(compiled.sql)}, [${compiled.params.map(renderSlot).join(", ")}], false, false, ${bigint}${dynamic ? `, ${this.dynamicWherePlan(model, where)}` : ""}`;
7054
+ const plan = optional.length > 0 ? this.dynamicWherePlan(model, optional, compiled) : void 0;
7055
+ const [sql2, slots] = plan === void 0 ? [compiled.sql, compiled.params] : [compiled.head, compiled.headParams];
7056
+ const call = `${quote(sql2)}, [${slots.map(renderSlot).join(", ")}]${execOptions({
7057
+ ...this.dbOf(model),
7058
+ ...plan !== void 0 ? { whereDynamic: plan } : {}
7059
+ })}`;
7462
7060
  const rowObj = objOf(deriveReadRow(table, projection, readResolve, `endpoint '${name}'`).outType, name);
7463
7061
  const selections = normalizeSelections(endpoint.with ?? []);
7464
7062
  const fresh = freshNames(params.map((p) => p.name));
@@ -7475,28 +7073,35 @@ var EmitContext = class {
7475
7073
  const lines = [];
7476
7074
  const parentVar = fresh("rows");
7477
7075
  lines.push(`const ${parentVar}: WireValue[] = Db.executeSQL(${call});`);
7478
- const graph = this.relations(model, parentVar, rowObj, selections, lines, fresh, bigint, `endpoint '${name}'`);
7076
+ const graph = this.relations(model, parentVar, rowObj, selections, lines, fresh, `endpoint '${name}'`);
7479
7077
  const rowType = this.declareInterface(`${cap1(name)}Row`, graph.obj);
7480
7078
  lines[lines.length - 1] = `const ${graph.terminalVar}: ${rowType}[] = ${graph.terminalCall} as ${rowType}[];`;
7481
7079
  lines.push(`return ${graph.terminalVar};`);
7482
7080
  return { name, params, returnType: `${rowType}[]`, lines };
7483
7081
  }
7484
7082
  /**
7485
- * The DYNAMIC WHERE plan expression: ONE fragment per declared predicate, every one the SAME struct
7486
- * `{skipped, sql, params}` — `sql`/`params` from the SAME `compileWhere` that produces a bounded
7487
- * WHERE, and `skipped` the per-call SKIP decision carried as DATA (a bounded predicate is a literal
7488
- * `false`; an optional one is `param === null`). That is the whole fragment vocabulary (CLAUDE.md §2:
7489
- * SQL + params + a SKIP flag). The array is HOMOGENEOUS — never a `cond`-to-null element — so the
7490
- * go/rust native-codegen emitters resolve it (a variant/nullable array element is the one shape they
7491
- * reject); the `executeSQL` leaf drops the `skipped` fragments at run.
7083
+ * The DYNAMIC WHERE plan expression: ONE fragment per OPTIONAL predicate — never a bounded one, which
7084
+ * is already in the static statement (CLAUDE.md §2) — every fragment the SAME struct
7085
+ * `{skipped, sql, params}`, with `sql`/`params` from the SAME `compileWhere` that produces a bounded
7086
+ * WHERE and `skipped` the per-call SKIP decision carried as DATA (`param === null`). That is the whole
7087
+ * fragment vocabulary (SQL + params + a SKIP flag). The array is HOMOGENEOUS — never a `cond`-to-null
7088
+ * element — so the go/rust native-codegen emitters resolve it (a variant/nullable array element is the
7089
+ * one shape they reject); the `executeSQL` leaf drops the `skipped` fragments at run and continues the
7090
+ * static WHERE with the survivors.
7091
+ *
7092
+ * The plan also carries what FINISHES the statement, taken straight off the bundle this same read
7093
+ * compiled ({@link import('../makesql/compile-select').SelectBundle}): `lead` — the connector the first
7094
+ * survivor joins with, which is `AND` exactly when the builder emitted a static WHERE — and `tail` /
7095
+ * `tailParams`, the text and bound values that follow the WHERE region. The leaf then concatenates
7096
+ * `head` + clause + `tail`; nothing on the run side has to find the boundary again, which is what a
7097
+ * lexical scan got wrong on a nested statement (#198) and on a quoted `?` (#202).
7492
7098
  */
7493
- dynamicWherePlan(model, where) {
7494
- const frags = where.map((p) => {
7099
+ dynamicWherePlan(model, optional, compiled) {
7100
+ const frags = optional.map((p) => {
7495
7101
  const w = compileWhere(this.conditions(model, [p]), this.spec.dialect);
7496
- const skipped = isParameterised(p) && p.optional === true ? `${p.param} === null` : "false";
7497
- return `{ skipped: ${skipped}, sql: ${quote(w.sql)}, params: [${w.params.map(renderSlot).join(", ")}] }`;
7102
+ return `{ skipped: ${p.param} === null, sql: ${quote(w.sql)}, params: [${w.params.map(renderSlot).join(", ")}] }`;
7498
7103
  });
7499
- return `{ frags: [${frags.join(", ")}] }`;
7104
+ return `{ frags: [${frags.join(", ")}], lead: ${quote(compiled.lead)}, tail: ${quote(compiled.tail)}, tailParams: [${compiled.tailParams.map(renderSlot).join(", ")}] }`;
7500
7105
  }
7501
7106
  /**
7502
7107
  * Emit ONE relation LEVEL: for each selected relation, pluck the parent key set, run the ONE batched
@@ -7504,7 +7109,7 @@ var EmitContext = class {
7504
7109
  * selection recurses FIRST, so a grandchild level is grouped into its own parent before that parent
7505
7110
  * is grouped into ours (one query per depth — never one per parent).
7506
7111
  */
7507
- relations(model, parentVar, parentObj, selections, lines, fresh, bigint, at) {
7112
+ relations(model, parentVar, parentObj, selections, lines, fresh, at) {
7508
7113
  let acc = parentVar;
7509
7114
  const obj = { ...parentObj };
7510
7115
  let terminalCall = "";
@@ -7516,16 +7121,20 @@ var EmitContext = class {
7516
7121
  lines.push(`const ${keysVar}: WireValue[] = Db.pluck(${acc}, ${jsonArray(parentKeyCols(op))});`);
7517
7122
  const childVar = fresh(sel.name);
7518
7123
  const guard = relationGuard(op);
7519
- const guardExpr = guard !== null ? `{ limit: ${guard.limit} as Int, ${guard.model !== void 0 ? `model: ${quote(guard.model)}, ` : ""}relation: ${quote(guard.relation)} }` : null;
7520
- const guardPorts = guardExpr !== null ? `, { frags: [] }, ${guardExpr}` : "";
7521
- lines.push(`const ${childVar}: WireValue[] = Db.executeSQL(${quote(op.sql)}, [${keysVar}], false, false, ${bigint}${guardPorts});`);
7124
+ const guardExpr = guard !== null ? `{ limit: ${guard.limit} as Int, model: ${quote(guard.model)}, relation: ${quote(guard.relation)} }` : void 0;
7125
+ lines.push(
7126
+ `const ${childVar}: WireValue[] = Db.executeSQL(${quote(op.sql)}, [${keysVar}]${execOptions({
7127
+ ...op.connection !== void 0 ? { db: op.connection } : {},
7128
+ ...guardExpr !== void 0 ? { guard: guardExpr } : {}
7129
+ })});`
7130
+ );
7522
7131
  let childRows = childVar;
7523
7132
  let childObj = objOf(
7524
7133
  deriveReadRow(decl.targetTable, decl.select, this.resolver(targetModel), `${at} relation '${sel.name}'`).outType,
7525
7134
  `${at} relation '${sel.name}'`
7526
7135
  );
7527
7136
  if (sel.with.length > 0) {
7528
- const nested = this.relations(targetModel, childVar, childObj, normalizeSelections(sel.with), lines, fresh, bigint, `${at}.${sel.name}`);
7137
+ const nested = this.relations(targetModel, childVar, childObj, normalizeSelections(sel.with), lines, fresh, `${at}.${sel.name}`);
7529
7138
  childObj = nested.obj;
7530
7139
  childRows = nested.terminalVar;
7531
7140
  }
@@ -7582,7 +7191,7 @@ var EmitContext = class {
7582
7191
  params,
7583
7192
  returnType: `${rowType}[]`,
7584
7193
  lines: [
7585
- `const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${op.params.map(renderSlot).join(", ")}], true, ${returning !== void 0}, false) as ${rowType}[];`,
7194
+ `const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${op.params.map(renderSlot).join(", ")}]${execOptions({ ...this.dbOf(model), write: { returning: returning !== void 0 } })}) as ${rowType}[];`,
7586
7195
  `return ${out};`
7587
7196
  ]
7588
7197
  };
@@ -7659,7 +7268,7 @@ var EmitContext = class {
7659
7268
  params,
7660
7269
  returnType: `${rowType}[]`,
7661
7270
  lines: [
7662
- `const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${slots.join(", ")}], true, ${endpoint.returning !== void 0}, false) as ${rowType}[];`,
7271
+ `const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${slots.join(", ")}]${execOptions({ ...this.dbOf(model), write: { returning: endpoint.returning !== void 0 } })}) as ${rowType}[];`,
7663
7272
  `return ${out};`
7664
7273
  ]
7665
7274
  };
@@ -7687,7 +7296,7 @@ var EmitContext = class {
7687
7296
  params,
7688
7297
  returnType: `${rowType}[]`,
7689
7298
  lines: [
7690
- `const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${op.params.map(renderSlot).join(", ")}], true, ${endpoint.returning !== void 0}, false) as ${rowType}[];`,
7299
+ `const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${op.params.map(renderSlot).join(", ")}]${execOptions({ ...this.dbOf(model), write: { returning: endpoint.returning !== void 0 } })}) as ${rowType}[];`,
7691
7300
  `return ${out};`
7692
7301
  ]
7693
7302
  };
@@ -7905,6 +7514,11 @@ function isParameterised(p) {
7905
7514
  function isOptional(p) {
7906
7515
  return isParameterised(p) && p.optional === true;
7907
7516
  }
7517
+ function execOptions(o) {
7518
+ if (o.db === void 0 && o.write === void 0 && o.whereDynamic === void 0 && o.guard === void 0) return "";
7519
+ const write = o.write === void 0 ? "null" : `{ returning: ${o.write.returning} }`;
7520
+ return `, { db: ${o.db === void 0 ? "null" : quote(o.db)}, write: ${write}, whereDynamic: ${o.whereDynamic ?? "null"}, guard: ${o.guard ?? "null"} }`;
7521
+ }
7908
7522
  function renderSlot(slot) {
7909
7523
  if (slot instanceof ParamRef) return slot.expr;
7910
7524
  const ref = slot?.__batchArray?.ref;
@@ -8079,20 +7693,13 @@ var RelationContext = class {
8079
7693
  ops;
8080
7694
  db;
8081
7695
  hydrate;
8082
- /** CROSS-DB (V0 R1): name → driver registry; a tagged relation routes to `connections[tag]`. */
8083
- connections;
8084
7696
  /** Per-relation resolved batch (name → grouping), memoized: one batch query per relation. */
8085
7697
  resolved = /* @__PURE__ */ new Map();
8086
- constructor(siblings, ops, db, hydrate, connections) {
7698
+ constructor(siblings, ops, db, hydrate) {
8087
7699
  this.siblings = siblings;
8088
7700
  this.ops = ops;
8089
7701
  this.db = db;
8090
7702
  this.hydrate = hydrate;
8091
- this.connections = connections;
8092
- }
8093
- /** The driver a relation runs against: its tagged cross-DB connection, else the primary `db`. */
8094
- driverFor(op) {
8095
- return op.connection === void 0 ? this.db : driverForOp(op, this.connections);
8096
7703
  }
8097
7704
  /**
8098
7705
  * Resolve `relationName` for `parent` (lazy access). The relation is batch-loaded across
@@ -8106,19 +7713,12 @@ var RelationContext = class {
8106
7713
  }
8107
7714
  let batch = this.resolved.get(relationName);
8108
7715
  if (batch === void 0) {
8109
- batch = runRelationOp(op, this.siblings, this.driverFor(op)).batch;
7716
+ batch = runRelationOp(op, this.siblings, this.db).batch;
8110
7717
  this.resolved.set(relationName, batch);
8111
7718
  }
8112
7719
  return applyHydrate(distributeToParent(op, parent, batch), this.hydrate);
8113
7720
  }
8114
7721
  };
8115
- function driverForOp(op, connections) {
8116
- const d = connections?.[op.connection];
8117
- if (d === void 0) {
8118
- throw new Error(`cross-DB relation '${op.name}': no driver registered for connection '${op.connection}' (pass it in ReadOptions.connections)`);
8119
- }
8120
- return d;
8121
- }
8122
7722
  function readRelationContext(o) {
8123
7723
  const v = o[RELATION_CONTEXT];
8124
7724
  return v instanceof RelationContext ? v : void 0;
@@ -8156,7 +7756,7 @@ function buildResultSet(rawRows, ops, db, options = {}) {
8156
7756
  for (const k of Object.keys(raw)) o[k] = raw[k];
8157
7757
  return o;
8158
7758
  });
8159
- const ctx = new RelationContext(rows, ops, db, hydrate, options.connections);
7759
+ const ctx = new RelationContext(rows, ops, db, hydrate);
8160
7760
  for (const o of rows) {
8161
7761
  Object.defineProperty(o, RELATION_CONTEXT, {
8162
7762
  value: ctx,
@@ -8168,8 +7768,7 @@ function buildResultSet(rawRows, ops, db, options = {}) {
8168
7768
  for (const name of Object.keys(selected)) {
8169
7769
  const op = ops[name];
8170
7770
  if (op === void 0) throw new Error(`declarative select: relation '${name}' is not declared on this model`);
8171
- const relDb = op.connection === void 0 ? db : driverForOp(op, options.connections);
8172
- const { batch } = runRelationOp(op, rows, relDb);
7771
+ const { batch } = runRelationOp(op, rows, db);
8173
7772
  for (const o of rows) {
8174
7773
  o[name] = applyHydrate(distributeToParent(op, o, batch), hydrate);
8175
7774
  }
@@ -8186,7 +7785,6 @@ function buildResultSet(rawRows, ops, db, options = {}) {
8186
7785
  DEFAULT_CONNECTION,
8187
7786
  DEFAULT_UNCAST_SQL_TYPE,
8188
7787
  Db,
8189
- ENTITY_ROOT,
8190
7788
  LEAF_TRANSPORT_SYMBOLS,
8191
7789
  LITEDBMODEL_MAKESQL_CATALOG,
8192
7790
  LimitExceededError,
@@ -8223,7 +7821,6 @@ function buildResultSet(rawRows, ops, db, options = {}) {
8223
7821
  compileCompositeKeyLimited,
8224
7822
  compileCompositeKeyStaticUnlimited,
8225
7823
  compileCompositeKeyUnlimited,
8226
- compileCompositeWriteBundle,
8227
7824
  compileCreateBundle,
8228
7825
  compileCreateManyBundle,
8229
7826
  compileDeleteBundle,
@@ -8242,7 +7839,6 @@ function buildResultSet(rawRows, ops, db, options = {}) {
8242
7839
  compileUpdateManyBundle,
8243
7840
  compileUpdateSingle,
8244
7841
  compileWhere,
8245
- compileWriteBundle,
8246
7842
  compileWriteNode,
8247
7843
  composeMakeSQL,
8248
7844
  configurePgDeboxTypeParsers,
@@ -8250,7 +7846,6 @@ function buildResultSet(rawRows, ops, db, options = {}) {
8250
7846
  connectionForDriver,
8251
7847
  contextForConnection,
8252
7848
  contextForDriver,
8253
- countingDriver,
8254
7849
  createMiddleware,
8255
7850
  currentPinnedAsyncConnection,
8256
7851
  currentRegistry,
@@ -8258,20 +7853,15 @@ function buildResultSet(rawRows, ops, db, options = {}) {
8258
7853
  dbCastIn,
8259
7854
  deriveModelColumns,
8260
7855
  deriveRelationDecls,
8261
- deriveTransactionPlan,
8262
7856
  dialectFor,
8263
7857
  distributeToParent,
8264
- edgeWrites,
8265
7858
  emitBehaviorModule,
8266
- entityWrites,
8267
7859
  execute,
8268
7860
  executeAsync,
8269
7861
  executeSQL,
8270
7862
  executeSQLAsync,
8271
7863
  executeSafe,
8272
- executeTransaction,
8273
7864
  executeTransactionAsync,
8274
- executeTransactionBundle,
8275
7865
  failClosedMaterializeResolverFromColumnMap,
8276
7866
  formatterFor,
8277
7867
  getLimitConfig,
@@ -8286,7 +7876,6 @@ function buildResultSet(rawRows, ops, db, options = {}) {
8286
7876
  leaf,
8287
7877
  leafHandlers,
8288
7878
  leafHandlersAsync,
8289
- lifecycleFor,
8290
7879
  makeSqlCatalogEntry,
8291
7880
  makeSqlHandler,
8292
7881
  makeSqlHandlerSync,
@@ -8301,7 +7890,6 @@ function buildResultSet(rawRows, ops, db, options = {}) {
8301
7890
  mysqlPoolFactory,
8302
7891
  noCastFormatter,
8303
7892
  opt,
8304
- parseEffectPath,
8305
7893
  parseSchemaColumnTypes,
8306
7894
  pgCastFormatter,
8307
7895
  pgConnectionPool,
@@ -8342,7 +7930,6 @@ function buildResultSet(rawRows, ops, db, options = {}) {
8342
7930
  setLimitConfig,
8343
7931
  singlePoolPair,
8344
7932
  sleep,
8345
- spliceWhere,
8346
7933
  sqlTypeToBcScalar,
8347
7934
  sqlTypeToMaterializeClass,
8348
7935
  stripMysqlPkHint,