litedbmodel 2.2.1 → 2.2.3
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.
- package/README.md +2 -1
- package/dist/DBModel.d.ts.map +1 -1
- package/dist/DBModel.js +3 -0
- package/dist/DBModel.js.map +1 -1
- package/dist/Middleware.d.ts +19 -2
- package/dist/Middleware.d.ts.map +1 -1
- package/dist/Middleware.js +1 -0
- package/dist/Middleware.js.map +1 -1
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +8 -0
- package/dist/decorators.js.map +1 -1
- package/dist/drivers/MysqlHelper.d.ts +1 -1
- package/dist/drivers/MysqlHelper.d.ts.map +1 -1
- package/dist/drivers/MysqlHelper.js.map +1 -1
- package/dist/drivers/PostgresHelper.d.ts +1 -1
- package/dist/drivers/PostgresHelper.d.ts.map +1 -1
- package/dist/drivers/PostgresHelper.js.map +1 -1
- package/dist/drivers/SqliteHelper.d.ts +1 -1
- package/dist/drivers/SqliteHelper.d.ts.map +1 -1
- package/dist/drivers/SqliteHelper.js.map +1 -1
- package/dist/index.cjs +99 -33
- package/dist/scp/connection-routing.d.ts +38 -1
- package/dist/scp/connection-routing.d.ts.map +1 -1
- package/dist/scp/connection-routing.js +64 -5
- package/dist/scp/connection-routing.js.map +1 -1
- package/dist/scp/dbmodel-runtime.d.ts +2 -1
- package/dist/scp/dbmodel-runtime.d.ts.map +1 -1
- package/dist/scp/dbmodel-runtime.js +2 -1
- package/dist/scp/dbmodel-runtime.js.map +1 -1
- package/dist/scp/decorator-adapter.d.ts +22 -0
- package/dist/scp/decorator-adapter.d.ts.map +1 -1
- package/dist/scp/decorator-adapter.js +26 -0
- package/dist/scp/decorator-adapter.js.map +1 -1
- package/dist/scp/emit/emitter.d.ts +21 -8
- package/dist/scp/emit/emitter.d.ts.map +1 -1
- package/dist/scp/emit/emitter.js +121 -39
- package/dist/scp/emit/emitter.js.map +1 -1
- package/dist/scp/emit/endpoint.d.ts +3 -8
- package/dist/scp/emit/endpoint.d.ts.map +1 -1
- package/dist/scp/exec-context.d.ts +39 -17
- package/dist/scp/exec-context.d.ts.map +1 -1
- package/dist/scp/exec-context.js +69 -35
- package/dist/scp/exec-context.js.map +1 -1
- package/dist/scp/grouping.d.ts.map +1 -1
- package/dist/scp/grouping.js +0 -0
- package/dist/scp/grouping.js.map +1 -1
- package/dist/scp/index.cjs +251 -624
- package/dist/scp/index.d.ts +8 -12
- package/dist/scp/index.d.ts.map +1 -1
- package/dist/scp/index.js +10 -28
- package/dist/scp/index.js.map +1 -1
- package/dist/scp/index.mjs +251 -612
- package/dist/scp/leaf-transport.d.ts +155 -12
- package/dist/scp/leaf-transport.d.ts.map +1 -1
- package/dist/scp/leaf-transport.js +31 -12
- package/dist/scp/leaf-transport.js.map +1 -1
- package/dist/scp/leaves.d.ts +59 -33
- package/dist/scp/leaves.d.ts.map +1 -1
- package/dist/scp/leaves.js +225 -65
- package/dist/scp/leaves.js.map +1 -1
- package/dist/scp/makesql/compile-select.d.ts +28 -1
- package/dist/scp/makesql/compile-select.d.ts.map +1 -1
- package/dist/scp/makesql/compile-select.js +19 -17
- package/dist/scp/makesql/compile-select.js.map +1 -1
- package/dist/scp/makesql/index.d.ts +2 -2
- package/dist/scp/makesql/index.d.ts.map +1 -1
- package/dist/scp/makesql/index.js +4 -6
- package/dist/scp/makesql/index.js.map +1 -1
- package/dist/scp/makesql/tx.d.ts +27 -69
- package/dist/scp/makesql/tx.d.ts.map +1 -1
- package/dist/scp/makesql/tx.js +26 -448
- package/dist/scp/makesql/tx.js.map +1 -1
- package/dist/scp/middleware.d.ts +4 -4
- package/dist/scp/middleware.d.ts.map +1 -1
- package/dist/scp/middleware.js +2 -2
- package/dist/scp/middleware.js.map +1 -1
- package/dist/scp/relation.d.ts +31 -12
- package/dist/scp/relation.d.ts.map +1 -1
- package/dist/scp/relation.js +16 -6
- package/dist/scp/relation.js.map +1 -1
- package/dist/scp/typed-object.d.ts +23 -19
- package/dist/scp/typed-object.d.ts.map +1 -1
- package/dist/scp/typed-object.js +24 -30
- package/dist/scp/typed-object.js.map +1 -1
- package/dist/scp/write-bundle.d.ts +21 -70
- package/dist/scp/write-bundle.d.ts.map +1 -1
- package/dist/scp/write-bundle.js +15 -79
- package/dist/scp/write-bundle.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/docs/architecture.md +452 -0
- package/docs/design/phase-a-execution-context.md +2 -0
- package/package.json +15 -4
- package/dist/scp/writes.d.ts +0 -251
- package/dist/scp/writes.d.ts.map +0 -1
- package/dist/scp/writes.js +0 -176
- package/dist/scp/writes.js.map +0 -1
- package/docs/proposal/contracts-architecture-feasibility.md +0 -328
- package/docs/proposal/contracts-architecture.md +0 -579
- package/docs/proposal/litedbmodel-v2-scp-architecture.md +0 -433
- package/docs/proposal/sql-dynamic-expansion-spec.md +0 -175
- package/docs/proposal/v0-coverage-matrix.md +0 -157
- package/docs/proposal/v2-completeness-plan.md +0 -90
- package/docs/proposal/v2-sql-parity-checklist.md +0 -162
package/dist/scp/index.mjs
CHANGED
|
@@ -1768,30 +1768,32 @@ function lockTail(opts) {
|
|
|
1768
1768
|
return "";
|
|
1769
1769
|
}
|
|
1770
1770
|
function compileSelect(desc) {
|
|
1771
|
-
const
|
|
1771
|
+
const headParams = [];
|
|
1772
1772
|
const selectCols = desc.select || (desc.selectColumn ?? "*");
|
|
1773
1773
|
const formatter = formatterFor(desc.dialect);
|
|
1774
|
-
if (desc.cte?.params && desc.cte.params.length > 0)
|
|
1775
|
-
if (desc.joinParams && desc.joinParams.length > 0)
|
|
1776
|
-
const whereClause2 = conditionsFor(desc.conditions ?? {}, desc.dialect).compile(
|
|
1777
|
-
let
|
|
1778
|
-
if (desc.cte)
|
|
1779
|
-
|
|
1780
|
-
if (desc.join)
|
|
1781
|
-
if (whereClause2)
|
|
1782
|
-
|
|
1774
|
+
if (desc.cte?.params && desc.cte.params.length > 0) headParams.push(...desc.cte.params);
|
|
1775
|
+
if (desc.joinParams && desc.joinParams.length > 0) headParams.push(...desc.joinParams);
|
|
1776
|
+
const whereClause2 = conditionsFor(desc.conditions ?? {}, desc.dialect).compile(headParams, formatter);
|
|
1777
|
+
let head = "";
|
|
1778
|
+
if (desc.cte) head = `WITH ${desc.cte.name} AS (${desc.cte.sql}) `;
|
|
1779
|
+
head += `SELECT ${selectCols} FROM ${desc.tableName}`;
|
|
1780
|
+
if (desc.join) head += ` ${desc.join}`;
|
|
1781
|
+
if (whereClause2) head += ` WHERE ${whereClause2}`;
|
|
1782
|
+
let tail = "";
|
|
1783
|
+
if (desc.group) tail += ` GROUP BY ${desc.group}`;
|
|
1783
1784
|
const orderClause = typeof desc.order === "string" ? desc.order : orderToString(desc.order);
|
|
1784
|
-
if (orderClause)
|
|
1785
|
+
if (orderClause) tail += ` ORDER BY ${orderClause}`;
|
|
1786
|
+
const tailParams = [];
|
|
1785
1787
|
const count = (c) => {
|
|
1786
1788
|
if (!isBoundCount(c)) return String(c);
|
|
1787
|
-
|
|
1789
|
+
tailParams.push(c.bind);
|
|
1788
1790
|
return "?";
|
|
1789
1791
|
};
|
|
1790
|
-
if (desc.limit !== void 0)
|
|
1791
|
-
if (desc.offset !== void 0)
|
|
1792
|
-
|
|
1793
|
-
if (desc.append)
|
|
1794
|
-
return { sql:
|
|
1792
|
+
if (desc.limit !== void 0) tail += ` LIMIT ${count(desc.limit)}`;
|
|
1793
|
+
if (desc.offset !== void 0) tail += ` OFFSET ${count(desc.offset)}`;
|
|
1794
|
+
tail += lockTail(desc);
|
|
1795
|
+
if (desc.append) tail += ` ${desc.append}`;
|
|
1796
|
+
return { sql: head + tail, params: [...headParams, ...tailParams], head, headParams, tail, tailParams, lead: whereClause2 ? "AND" : "WHERE" };
|
|
1795
1797
|
}
|
|
1796
1798
|
|
|
1797
1799
|
// src/scp/makesql/compile-relation.ts
|
|
@@ -3980,6 +3982,24 @@ function readerWriterPair(reader, writer) {
|
|
|
3980
3982
|
return { reader, writer };
|
|
3981
3983
|
}
|
|
3982
3984
|
var DEFAULT_CONNECTION = "default";
|
|
3985
|
+
function assertRoutableNamedDb(db, contextDescription) {
|
|
3986
|
+
if (effectiveConnection(db) === DEFAULT_CONNECTION) return;
|
|
3987
|
+
throw new Error(
|
|
3988
|
+
`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.`
|
|
3989
|
+
);
|
|
3990
|
+
}
|
|
3991
|
+
function effectiveConnection(db) {
|
|
3992
|
+
return db === void 0 || db === null ? DEFAULT_CONNECTION : db;
|
|
3993
|
+
}
|
|
3994
|
+
function assertTxDbAgrees(db, txDb) {
|
|
3995
|
+
if (db === void 0 || db === null) return;
|
|
3996
|
+
const want = effectiveConnection(db);
|
|
3997
|
+
const open = effectiveConnection(txDb);
|
|
3998
|
+
if (want === open) return;
|
|
3999
|
+
throw new Error(
|
|
4000
|
+
`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.`
|
|
4001
|
+
);
|
|
4002
|
+
}
|
|
3983
4003
|
var ConnectionRegistry = class _ConnectionRegistry {
|
|
3984
4004
|
/** name → reader/writer pools. */
|
|
3985
4005
|
connections;
|
|
@@ -4000,7 +4020,7 @@ var ConnectionRegistry = class _ConnectionRegistry {
|
|
|
4000
4020
|
}
|
|
4001
4021
|
/** The reader/writer pair for `name` (or {@link DEFAULT_CONNECTION} when `undefined`). Loud on a missing name. */
|
|
4002
4022
|
pairFor(name) {
|
|
4003
|
-
const key = name
|
|
4023
|
+
const key = effectiveConnection(name);
|
|
4004
4024
|
const pair = this.connections.get(key);
|
|
4005
4025
|
if (pair === void 0) {
|
|
4006
4026
|
const known = [...this.connections.keys()].map((k) => `'${k}'`).join(", ");
|
|
@@ -4048,7 +4068,13 @@ function withWriter(fn) {
|
|
|
4048
4068
|
return writerScopeStore.run(true, () => withReadOnlyAsync(fn));
|
|
4049
4069
|
}
|
|
4050
4070
|
var WriterStickyClock = class {
|
|
4051
|
-
|
|
4071
|
+
/**
|
|
4072
|
+
* `null` until the first `mark()` — absence, NOT a value sentinel. The injectable clocks legitimately
|
|
4073
|
+
* return 0 (a monotonic clock right after process start; rust's `SystemClock` does), so encoding
|
|
4074
|
+
* "never marked" as the value 0 would mis-classify a `mark()` at clock t=0 as unmarked and leak the
|
|
4075
|
+
* read-your-writes read to the reader replica. Absence must be distinct from the value 0.
|
|
4076
|
+
*/
|
|
4077
|
+
lastWriteAt = null;
|
|
4052
4078
|
enabled;
|
|
4053
4079
|
stickyDurationMs;
|
|
4054
4080
|
/** Injectable clock (tests advance it); defaults to `Date.now`. */
|
|
@@ -4064,12 +4090,12 @@ var WriterStickyClock = class {
|
|
|
4064
4090
|
}
|
|
4065
4091
|
/** Is a read currently sticky-to-writer (within `writerStickyDuration` of the last write)? */
|
|
4066
4092
|
isSticky() {
|
|
4067
|
-
if (!this.enabled || this.lastWriteAt ===
|
|
4093
|
+
if (!this.enabled || this.lastWriteAt === null) return false;
|
|
4068
4094
|
return this.now() - this.lastWriteAt < this.stickyDurationMs;
|
|
4069
4095
|
}
|
|
4070
4096
|
/** Reset the clock (e.g. between tests / on `closeAllPools`). */
|
|
4071
4097
|
reset() {
|
|
4072
|
-
this.lastWriteAt =
|
|
4098
|
+
this.lastWriteAt = null;
|
|
4073
4099
|
}
|
|
4074
4100
|
};
|
|
4075
4101
|
function resolvePool(intent, routing) {
|
|
@@ -4418,15 +4444,20 @@ function connectionForDriver(driver) {
|
|
|
4418
4444
|
var BasicContext = class _BasicContext {
|
|
4419
4445
|
middleware;
|
|
4420
4446
|
base;
|
|
4421
|
-
/** The pinned tx connection (present ⇒ this is a tx-scoped ctx; every statement uses it). */
|
|
4447
|
+
/** 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). */
|
|
4422
4448
|
pinned;
|
|
4423
4449
|
constructor(base, middleware, pinned) {
|
|
4424
4450
|
this.base = base;
|
|
4425
4451
|
this.middleware = middleware;
|
|
4426
4452
|
this.pinned = pinned;
|
|
4427
4453
|
}
|
|
4428
|
-
connectionFor(
|
|
4429
|
-
|
|
4454
|
+
connectionFor(intent) {
|
|
4455
|
+
if (this.pinned !== null) {
|
|
4456
|
+
assertTxDbAgrees(intent.db, null);
|
|
4457
|
+
return this.pinned;
|
|
4458
|
+
}
|
|
4459
|
+
assertRoutableNamedDb(intent.db, "a single-connection (non-routed) execution context");
|
|
4460
|
+
return this.base;
|
|
4430
4461
|
}
|
|
4431
4462
|
withConnection(conn, tx) {
|
|
4432
4463
|
return new _BasicContext(this.base, this.middleware, tx ? conn : null);
|
|
@@ -4464,7 +4495,10 @@ var PooledAsyncContext = class {
|
|
|
4464
4495
|
}
|
|
4465
4496
|
connectionFor(intent) {
|
|
4466
4497
|
const pinned = asyncCtxStore.getStore();
|
|
4467
|
-
if (pinned !== void 0)
|
|
4498
|
+
if (pinned !== void 0) {
|
|
4499
|
+
assertTxDbAgrees(intent.db, pinned.db);
|
|
4500
|
+
return pinned.conn;
|
|
4501
|
+
}
|
|
4468
4502
|
const pool = resolvePool(intent, this.routing);
|
|
4469
4503
|
return {
|
|
4470
4504
|
async execute(sql2, params) {
|
|
@@ -4508,8 +4542,8 @@ var PooledAsyncContext = class {
|
|
|
4508
4542
|
function isRoutingConfig(x) {
|
|
4509
4543
|
return typeof x === "object" && x !== null && "registry" in x && "sticky" in x;
|
|
4510
4544
|
}
|
|
4511
|
-
function runWithPinnedAsyncConnection(conn, fn) {
|
|
4512
|
-
return asyncCtxStore.run(conn, fn);
|
|
4545
|
+
function runWithPinnedAsyncConnection(conn, db, fn) {
|
|
4546
|
+
return asyncCtxStore.run({ conn, db }, fn);
|
|
4513
4547
|
}
|
|
4514
4548
|
function currentPinnedAsyncConnection() {
|
|
4515
4549
|
return asyncCtxStore.getStore();
|
|
@@ -4520,9 +4554,9 @@ function defaultIsConnectionError(err) {
|
|
|
4520
4554
|
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";
|
|
4521
4555
|
}
|
|
4522
4556
|
async function withTransactionAsync(ctx, fn, opts = {}, dialect = "postgres", isConnectionError2 = defaultIsConnectionError, connection) {
|
|
4523
|
-
const
|
|
4524
|
-
if (
|
|
4525
|
-
return fn(ctx.withConnection(
|
|
4557
|
+
const outer = currentPinnedAsyncConnection();
|
|
4558
|
+
if (outer !== void 0) {
|
|
4559
|
+
return fn(ctx.withConnection(outer.conn, true));
|
|
4526
4560
|
}
|
|
4527
4561
|
const resolved = resolveTxOptions(opts);
|
|
4528
4562
|
const begins = beginStatements(dialect, resolved.isolation);
|
|
@@ -4533,7 +4567,7 @@ async function withTransactionAsync(ctx, fn, opts = {}, dialect = "postgres", is
|
|
|
4533
4567
|
const conn = await pool.acquire();
|
|
4534
4568
|
const txCtx = ctx.withConnection(conn, true);
|
|
4535
4569
|
let poisoned = false;
|
|
4536
|
-
const attemptResult = await runWithPinnedAsyncConnection(conn, async () => {
|
|
4570
|
+
const attemptResult = await runWithPinnedAsyncConnection(conn, connection ?? null, async () => {
|
|
4537
4571
|
for (const begin of begins) await runAsync(txCtx, begin, []);
|
|
4538
4572
|
try {
|
|
4539
4573
|
const r = await runInTransactionScope(() => fn(txCtx));
|
|
@@ -4564,21 +4598,6 @@ async function withTransactionAsync(ctx, fn, opts = {}, dialect = "postgres", is
|
|
|
4564
4598
|
function transaction(ctx, fn, options = {}, dialect = "postgres", isConnectionError2 = defaultIsConnectionError, connection) {
|
|
4565
4599
|
return withTransactionAsync(ctx, () => fn(), options, dialect, isConnectionError2, connection);
|
|
4566
4600
|
}
|
|
4567
|
-
function withTransactionSync(outer, fn, dialect = "sqlite", isolation) {
|
|
4568
|
-
const ctx = outer.withConnection(outer.connectionFor({ write: true }), true);
|
|
4569
|
-
for (const begin of beginStatements(dialect, isolation)) run(ctx, begin, []);
|
|
4570
|
-
try {
|
|
4571
|
-
const r = runInTransactionScope(() => fn(ctx));
|
|
4572
|
-
run(ctx, r.commit ? "COMMIT" : "ROLLBACK", []);
|
|
4573
|
-
return r.value;
|
|
4574
|
-
} catch (e) {
|
|
4575
|
-
try {
|
|
4576
|
-
run(ctx, "ROLLBACK", []);
|
|
4577
|
-
} catch {
|
|
4578
|
-
}
|
|
4579
|
-
throw e;
|
|
4580
|
-
}
|
|
4581
|
-
}
|
|
4582
4601
|
|
|
4583
4602
|
// src/scp/limit-config.ts
|
|
4584
4603
|
var globalLimitConfig = {};
|
|
@@ -4658,25 +4677,20 @@ function attachToParent(parent, pkCols, byKey, single) {
|
|
|
4658
4677
|
}
|
|
4659
4678
|
|
|
4660
4679
|
// src/scp/leaves.ts
|
|
4661
|
-
var WHERE_TAIL_RE = /\s+(GROUP BY|ORDER BY|LIMIT|OFFSET|FOR UPDATE|RETURNING)\b/i;
|
|
4662
|
-
function spliceWhere(baseSql, whereSql) {
|
|
4663
|
-
if (whereSql === "") return baseSql;
|
|
4664
|
-
const tail = WHERE_TAIL_RE.exec(baseSql);
|
|
4665
|
-
return tail === null ? baseSql + whereSql : baseSql.slice(0, tail.index) + whereSql + baseSql.slice(tail.index);
|
|
4666
|
-
}
|
|
4667
4680
|
function assembleDynamicWhere(p) {
|
|
4668
|
-
const
|
|
4669
|
-
|
|
4670
|
-
const
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
}
|
|
4675
|
-
return { sql: spliceWhere(p.sql, whereSql), params: [...whereParams, ...p.params] };
|
|
4681
|
+
const plan = dynamicWherePlan(p.whereDynamic);
|
|
4682
|
+
const frags = plan.frags.filter((f) => !f.skipped);
|
|
4683
|
+
const clause = frags.length === 0 ? "" : ` ${plan.lead} ${frags.map((f) => f.sql).join(" AND ")}`;
|
|
4684
|
+
return {
|
|
4685
|
+
sql: p.sql + clause + plan.tail,
|
|
4686
|
+
params: [...p.params, ...frags.flatMap((f) => f.params), ...plan.tailParams]
|
|
4687
|
+
};
|
|
4676
4688
|
}
|
|
4677
4689
|
function effectiveStatement(p) {
|
|
4678
|
-
|
|
4679
|
-
|
|
4690
|
+
const write = p.write !== null;
|
|
4691
|
+
const db = p.db ?? null;
|
|
4692
|
+
if (p.whereDynamic == null) return { sql: p.sql, params: p.params, write, db };
|
|
4693
|
+
return { ...assembleDynamicWhere({ sql: p.sql, params: p.params, whereDynamic: p.whereDynamic }), write, db };
|
|
4680
4694
|
}
|
|
4681
4695
|
function toBcInt(v) {
|
|
4682
4696
|
return typeof v === "bigint" ? v : BigInt(v);
|
|
@@ -4698,7 +4712,7 @@ function prepareSql(p, dialect) {
|
|
|
4698
4712
|
}
|
|
4699
4713
|
sql2 = renderPlaceholders(sql2, dialect);
|
|
4700
4714
|
const bound = encodeParams(p.params, dialect);
|
|
4701
|
-
const intent = { write: p.write
|
|
4715
|
+
const intent = { write: p.write, ...p.db != null ? { db: p.db } : {} };
|
|
4702
4716
|
return { sql: sql2, bound, intent };
|
|
4703
4717
|
}
|
|
4704
4718
|
function writeSummary(info) {
|
|
@@ -4706,14 +4720,14 @@ function writeSummary(info) {
|
|
|
4706
4720
|
}
|
|
4707
4721
|
function executeSQL(p, ctx) {
|
|
4708
4722
|
const prepared = prepareSql(effectiveStatement(p), ctx.dialect);
|
|
4709
|
-
if (p.write
|
|
4723
|
+
if (p.write !== null && !p.write.returning) return writeSummary(run(ctx.exec, prepared.sql, prepared.bound, prepared.intent));
|
|
4710
4724
|
const rows = executeSafe(ctx.exec, prepared.sql, prepared.bound, prepared.intent);
|
|
4711
4725
|
assertRelationHardLimit(rows, p.guard);
|
|
4712
4726
|
return rows;
|
|
4713
4727
|
}
|
|
4714
4728
|
async function executeSQLAsync(p, ctx) {
|
|
4715
4729
|
const prepared = prepareSql(effectiveStatement(p), ctx.dialect);
|
|
4716
|
-
if (p.write
|
|
4730
|
+
if (p.write !== null && !p.write.returning) return writeSummary(await runAsync(ctx.execAsync, prepared.sql, prepared.bound, prepared.intent));
|
|
4717
4731
|
const rows = await executeAsync(ctx.execAsync, prepared.sql, prepared.bound, prepared.intent);
|
|
4718
4732
|
assertRelationHardLimit(rows, p.guard);
|
|
4719
4733
|
return rows;
|
|
@@ -4724,40 +4738,106 @@ function pluck(p) {
|
|
|
4724
4738
|
}
|
|
4725
4739
|
function group(p) {
|
|
4726
4740
|
const byKey = groupByKey(p.children, p.fk);
|
|
4727
|
-
return p.parents.map((par) => ({ ...par, [p.into]: attachToParent(par, p.pk, byKey, p.single
|
|
4741
|
+
return p.parents.map((par) => ({ ...par, [p.into]: attachToParent(par, p.pk, byKey, p.single) }));
|
|
4742
|
+
}
|
|
4743
|
+
var PORT_TYPES = {
|
|
4744
|
+
bool: (v) => typeof v === "boolean",
|
|
4745
|
+
int: (v) => typeof v === "bigint",
|
|
4746
|
+
string: (v) => typeof v === "string",
|
|
4747
|
+
list: (v) => Array.isArray(v),
|
|
4748
|
+
"string[]": (v) => Array.isArray(v) && v.every((e) => typeof e === "string"),
|
|
4749
|
+
record: (v) => typeof v === "object" && v !== null && !Array.isArray(v)
|
|
4750
|
+
};
|
|
4751
|
+
function portTyped(value, what, declared) {
|
|
4752
|
+
const kind = declared.endsWith("|null") ? declared.slice(0, -"|null".length) : declared;
|
|
4753
|
+
if (kind !== declared && value === null) return null;
|
|
4754
|
+
if (!PORT_TYPES[kind](value)) {
|
|
4755
|
+
const got = JSON.stringify(value, (_k, v) => typeof v === "bigint" ? `${v}n` : v);
|
|
4756
|
+
throw new Error(`scp leaf: ${what} must be ${declared}, got ${got}`);
|
|
4757
|
+
}
|
|
4758
|
+
return value;
|
|
4728
4759
|
}
|
|
4729
|
-
function
|
|
4730
|
-
if (
|
|
4731
|
-
const g = port;
|
|
4732
|
-
const limit = typeof g.limit === "bigint" ? Number(g.limit) : g.limit;
|
|
4733
|
-
if (typeof limit !== "number" || !Number.isInteger(limit) || typeof g.relation !== "string") {
|
|
4760
|
+
function requiredField(record, name, at, declared) {
|
|
4761
|
+
if (!(name in record)) {
|
|
4734
4762
|
throw new Error(
|
|
4735
|
-
`scp leaf
|
|
4763
|
+
`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)`
|
|
4736
4764
|
);
|
|
4737
4765
|
}
|
|
4738
|
-
return
|
|
4766
|
+
return portTyped(record[name], `${at}'s '${name}'`, declared);
|
|
4767
|
+
}
|
|
4768
|
+
var PLAN_AT = `the 'whereDynamic' plan`;
|
|
4769
|
+
function dynamicWherePlan(port) {
|
|
4770
|
+
const plan = port;
|
|
4771
|
+
const at = `a 'whereDynamic' fragment`;
|
|
4772
|
+
const frags = requiredField(plan, "frags", PLAN_AT, "list").map((frag) => {
|
|
4773
|
+
const f = portTyped(frag, at, "record");
|
|
4774
|
+
return {
|
|
4775
|
+
skipped: requiredField(f, "skipped", at, "bool"),
|
|
4776
|
+
sql: requiredField(f, "sql", at, "string"),
|
|
4777
|
+
params: requiredField(f, "params", at, "list")
|
|
4778
|
+
};
|
|
4779
|
+
});
|
|
4780
|
+
return {
|
|
4781
|
+
frags,
|
|
4782
|
+
lead: requiredField(plan, "lead", PLAN_AT, "string"),
|
|
4783
|
+
tail: requiredField(plan, "tail", PLAN_AT, "string"),
|
|
4784
|
+
tailParams: requiredField(plan, "tailParams", PLAN_AT, "list")
|
|
4785
|
+
};
|
|
4786
|
+
}
|
|
4787
|
+
function relationGuardPort(port) {
|
|
4788
|
+
if (port === null) return null;
|
|
4789
|
+
const g = port;
|
|
4790
|
+
const at = `the 'guard' cap`;
|
|
4791
|
+
const raw = requiredField(g, "limit", at, "int");
|
|
4792
|
+
const model = requiredField(g, "model", at, "string|null");
|
|
4793
|
+
return {
|
|
4794
|
+
limit: Number(raw),
|
|
4795
|
+
...model === null ? {} : { model },
|
|
4796
|
+
relation: requiredField(g, "relation", at, "string")
|
|
4797
|
+
};
|
|
4798
|
+
}
|
|
4799
|
+
var OPTS_AT = `the 'opts' control record`;
|
|
4800
|
+
function optsField(opts, name) {
|
|
4801
|
+
return requiredField(opts, name, OPTS_AT, "record|null");
|
|
4802
|
+
}
|
|
4803
|
+
function writeModePort(port) {
|
|
4804
|
+
if (port === null) return null;
|
|
4805
|
+
return { returning: requiredField(port, "returning", `the 'write' mode`, "bool") };
|
|
4739
4806
|
}
|
|
4740
4807
|
function executeSqlPorts(ports) {
|
|
4808
|
+
const at = "the executeSQL payload";
|
|
4809
|
+
const sql2 = requiredField(ports, "sql", at, "string");
|
|
4810
|
+
const params = requiredField(ports, "params", at, "list");
|
|
4811
|
+
const opts = "opts" in ports ? requiredField(ports, "opts", at, "record|null") : null;
|
|
4812
|
+
if (opts === null) return { sql: sql2, params, write: null };
|
|
4741
4813
|
return {
|
|
4742
|
-
sql:
|
|
4743
|
-
params
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4814
|
+
sql: sql2,
|
|
4815
|
+
params,
|
|
4816
|
+
// The NAMED database — the one control field that is a bare `string|null` rather than a struct, so
|
|
4817
|
+
// it is read against its own declared type instead of through `optsField`'s `record|null`.
|
|
4818
|
+
db: requiredField(opts, "db", OPTS_AT, "string|null"),
|
|
4819
|
+
write: writeModePort(optsField(opts, "write")),
|
|
4820
|
+
whereDynamic: optsField(opts, "whereDynamic"),
|
|
4821
|
+
guard: relationGuardPort(optsField(opts, "guard"))
|
|
4749
4822
|
};
|
|
4750
4823
|
}
|
|
4824
|
+
var PLUCK_AT = "the pluck payload";
|
|
4825
|
+
var GROUP_AT = "the group payload";
|
|
4751
4826
|
var shapingHandlers = {
|
|
4752
|
-
pluck: (ports) => ({
|
|
4827
|
+
pluck: (ports) => ({
|
|
4828
|
+
ok: pluck({
|
|
4829
|
+
rows: requiredField(ports, "rows", PLUCK_AT, "list"),
|
|
4830
|
+
col: requiredField(ports, "col", PLUCK_AT, "string[]")
|
|
4831
|
+
})
|
|
4832
|
+
}),
|
|
4753
4833
|
group: (ports) => ({
|
|
4754
4834
|
ok: group({
|
|
4755
|
-
parents: ports
|
|
4756
|
-
children: ports
|
|
4757
|
-
pk: ports
|
|
4758
|
-
fk: ports
|
|
4759
|
-
into: ports
|
|
4760
|
-
single: ports
|
|
4835
|
+
parents: requiredField(ports, "parents", GROUP_AT, "list"),
|
|
4836
|
+
children: requiredField(ports, "children", GROUP_AT, "list"),
|
|
4837
|
+
pk: requiredField(ports, "pk", GROUP_AT, "string[]"),
|
|
4838
|
+
fk: requiredField(ports, "fk", GROUP_AT, "string[]"),
|
|
4839
|
+
into: requiredField(ports, "into", GROUP_AT, "string"),
|
|
4840
|
+
single: requiredField(ports, "single", GROUP_AT, "bool")
|
|
4761
4841
|
})
|
|
4762
4842
|
})
|
|
4763
4843
|
};
|
|
@@ -4786,111 +4866,6 @@ function isConnectionError(error) {
|
|
|
4786
4866
|
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";
|
|
4787
4867
|
}
|
|
4788
4868
|
|
|
4789
|
-
// src/scp/writes.ts
|
|
4790
|
-
var ENTITY_ROOT = "__entity";
|
|
4791
|
-
var PATH_RE = /^\$\.(input|entity)\.([A-Za-z_][A-Za-z0-9_]*)$/;
|
|
4792
|
-
var REF_PATH_RE = /^\$\.ref\.([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)$/;
|
|
4793
|
-
function parseEffectPath(value) {
|
|
4794
|
-
const m = PATH_RE.exec(value);
|
|
4795
|
-
if (m !== null) return { root: m[1], field: m[2] };
|
|
4796
|
-
const r = REF_PATH_RE.exec(value);
|
|
4797
|
-
if (r !== null) return { root: "ref", writeName: r[1], field: r[2] };
|
|
4798
|
-
throw new Error(
|
|
4799
|
-
`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).`
|
|
4800
|
-
);
|
|
4801
|
-
}
|
|
4802
|
-
function assertPaths(map) {
|
|
4803
|
-
for (const v of Object.values(map)) parseEffectPath(v);
|
|
4804
|
-
}
|
|
4805
|
-
var recorder = {
|
|
4806
|
-
lifecycle(effects = {}) {
|
|
4807
|
-
for (const r of effects.requires ?? []) assertPaths(r.keys);
|
|
4808
|
-
if (effects.idempotency !== void 0) parseEffectPath(effects.idempotency.token);
|
|
4809
|
-
for (const u of effects.unique ?? []) {
|
|
4810
|
-
u.scope.forEach((p) => parseEffectPath(p));
|
|
4811
|
-
u.fields.forEach((p) => parseEffectPath(p));
|
|
4812
|
-
}
|
|
4813
|
-
for (const d of effects.derive ?? []) assertPaths(d.keys);
|
|
4814
|
-
for (const e of effects.edges ?? []) {
|
|
4815
|
-
assertPaths(e.columns);
|
|
4816
|
-
if (e.where !== void 0) assertPaths(e.where);
|
|
4817
|
-
}
|
|
4818
|
-
for (const em of effects.emits ?? []) assertPaths(em.payload);
|
|
4819
|
-
return { effects: freezeEffects(effects) };
|
|
4820
|
-
},
|
|
4821
|
-
exists(table, keys) {
|
|
4822
|
-
return { kind: "requires", table, keys };
|
|
4823
|
-
},
|
|
4824
|
-
unique(spec) {
|
|
4825
|
-
return { kind: "unique", name: spec.name, guardTable: spec.guardTable, scope: spec.scope, fields: spec.fields };
|
|
4826
|
-
},
|
|
4827
|
-
increment(table, keys, attribute, amount) {
|
|
4828
|
-
if (!Number.isFinite(amount)) {
|
|
4829
|
-
throw new Error(`entityWrites: increment amount for '${table}.${attribute}' must be a finite number (got ${String(amount)})`);
|
|
4830
|
-
}
|
|
4831
|
-
return { kind: "derive", table, keys, attribute, amount };
|
|
4832
|
-
},
|
|
4833
|
-
edge(spec) {
|
|
4834
|
-
if (spec.relation === "fk" && spec.where === void 0) {
|
|
4835
|
-
throw new Error(`entityWrites: a 'fk' edge on '${spec.table}' requires a 'where' key map (the matched row's identity)`);
|
|
4836
|
-
}
|
|
4837
|
-
return {
|
|
4838
|
-
kind: "edge",
|
|
4839
|
-
relation: spec.relation,
|
|
4840
|
-
action: spec.action,
|
|
4841
|
-
table: spec.table,
|
|
4842
|
-
columns: spec.columns,
|
|
4843
|
-
...spec.where !== void 0 ? { where: spec.where } : {}
|
|
4844
|
-
};
|
|
4845
|
-
},
|
|
4846
|
-
event(name, outboxTable, payload) {
|
|
4847
|
-
return { kind: "emit", name, outboxTable, payload };
|
|
4848
|
-
},
|
|
4849
|
-
idempotentBy(table, column2, token) {
|
|
4850
|
-
return { kind: "idempotency", table, column: column2, token };
|
|
4851
|
-
}
|
|
4852
|
-
};
|
|
4853
|
-
function freezeEffects(effects) {
|
|
4854
|
-
const out = {};
|
|
4855
|
-
if (effects.requires !== void 0) out.requires = effects.requires;
|
|
4856
|
-
if (effects.idempotency !== void 0) out.idempotency = effects.idempotency;
|
|
4857
|
-
if (effects.unique !== void 0) out.unique = effects.unique;
|
|
4858
|
-
if (effects.derive !== void 0) out.derive = effects.derive;
|
|
4859
|
-
if (effects.edges !== void 0) out.edges = effects.edges;
|
|
4860
|
-
if (effects.emits !== void 0) out.emits = effects.emits;
|
|
4861
|
-
return out;
|
|
4862
|
-
}
|
|
4863
|
-
function entityWrites(builder) {
|
|
4864
|
-
const shape = builder(recorder);
|
|
4865
|
-
if (shape.create === void 0 && shape.update === void 0 && shape.remove === void 0) {
|
|
4866
|
-
throw new Error(
|
|
4867
|
-
"entityWrites(...) must declare at least one lifecycle (`create` / `update` / `remove`)."
|
|
4868
|
-
);
|
|
4869
|
-
}
|
|
4870
|
-
return {
|
|
4871
|
-
...shape.create !== void 0 ? { create: shape.create } : {},
|
|
4872
|
-
...shape.update !== void 0 ? { update: shape.update } : {},
|
|
4873
|
-
...shape.remove !== void 0 ? { remove: shape.remove } : {}
|
|
4874
|
-
};
|
|
4875
|
-
}
|
|
4876
|
-
function edgeWrites(builder) {
|
|
4877
|
-
const def = entityWrites(builder);
|
|
4878
|
-
for (const phase of ["create", "update", "remove"]) {
|
|
4879
|
-
const c = def[phase];
|
|
4880
|
-
if (c === void 0) continue;
|
|
4881
|
-
const e = c.effects;
|
|
4882
|
-
if (e.requires !== void 0 || e.unique !== void 0 || e.derive !== void 0 || e.emits !== void 0 || e.idempotency !== void 0) {
|
|
4883
|
-
throw new Error(
|
|
4884
|
-
`edgeWrites: the '${phase}' lifecycle may declare ONLY 'edges' effects (an edge entity carries only edge writes; use entityWrites for a full save contract).`
|
|
4885
|
-
);
|
|
4886
|
-
}
|
|
4887
|
-
}
|
|
4888
|
-
return def;
|
|
4889
|
-
}
|
|
4890
|
-
function lifecycleFor(def, phase) {
|
|
4891
|
-
return def[phase];
|
|
4892
|
-
}
|
|
4893
|
-
|
|
4894
4869
|
// src/scp/makesql/tx.ts
|
|
4895
4870
|
function literalize(value) {
|
|
4896
4871
|
if (value === null || value === void 0) return null;
|
|
@@ -4903,101 +4878,7 @@ function literalize(value) {
|
|
|
4903
4878
|
}
|
|
4904
4879
|
return value;
|
|
4905
4880
|
}
|
|
4906
|
-
|
|
4907
|
-
const p = parseEffectPath(value);
|
|
4908
|
-
if (p.root === "input") return { ref: [p.field] };
|
|
4909
|
-
if (p.root === "entity") return { ref: [ENTITY_ROOT, p.field] };
|
|
4910
|
-
return { ref: [p.writeName, p.field] };
|
|
4911
|
-
}
|
|
4912
|
-
function guardInsert(dialect, table, columns, placeholders2) {
|
|
4913
|
-
const cols = `(${columns.join(", ")})`;
|
|
4914
|
-
if (dialect === "mysql") return `INSERT IGNORE INTO ${table} ${cols} VALUES (${placeholders2})`;
|
|
4915
|
-
return `INSERT INTO ${table} ${cols} VALUES (${placeholders2}) ON CONFLICT DO NOTHING`;
|
|
4916
|
-
}
|
|
4917
|
-
function makeIdGen() {
|
|
4918
|
-
let n = 0;
|
|
4919
|
-
return (role) => `tx_${role}_${n++}`;
|
|
4920
|
-
}
|
|
4921
|
-
function compileRequires(e, nextId) {
|
|
4922
|
-
const cols = Object.keys(e.keys);
|
|
4923
|
-
if (cols.length === 0) throw new Error(`write-plan: requires on '${e.table}' declares no keys`);
|
|
4924
|
-
const whereSql = v1EqualityWhereText(cols);
|
|
4925
|
-
const op = {
|
|
4926
|
-
sql: `SELECT 1 FROM ${e.table} WHERE ${whereSql}`,
|
|
4927
|
-
params: cols.map((c) => pathToRef(e.keys[c]))
|
|
4928
|
-
};
|
|
4929
|
-
return { id: nextId("requires"), role: "gate:requires", op, gate: "existsElseRollback", label: `requires ${e.table}` };
|
|
4930
|
-
}
|
|
4931
|
-
function compileIdempotency(e, nextId, dialect) {
|
|
4932
|
-
const op = {
|
|
4933
|
-
sql: guardInsert(dialect, e.table, [e.column], "?"),
|
|
4934
|
-
params: [pathToRef(e.token)]
|
|
4935
|
-
};
|
|
4936
|
-
return { id: nextId("idem"), role: "gate:idempotency", op, gate: "insertedElseNoop", label: `idempotency ${e.table}` };
|
|
4937
|
-
}
|
|
4938
|
-
function compileUnique(e, nextId, dialect) {
|
|
4939
|
-
const scopeCols = e.scope.map((_, i) => `s${i}`);
|
|
4940
|
-
const fieldCols = e.fields.map((_, i) => `f${i}`);
|
|
4941
|
-
const cols = ["name", ...scopeCols, ...fieldCols];
|
|
4942
|
-
const placeholders2 = cols.map(() => "?").join(", ");
|
|
4943
|
-
const params = [
|
|
4944
|
-
e.name,
|
|
4945
|
-
// literal discriminator (a closed-set string literal)
|
|
4946
|
-
...e.scope.map(pathToRef),
|
|
4947
|
-
...e.fields.map(pathToRef)
|
|
4948
|
-
];
|
|
4949
|
-
const op = { sql: guardInsert(dialect, e.guardTable, cols, placeholders2), params };
|
|
4950
|
-
return { id: nextId("unique"), role: "gate:unique", op, gate: "insertedElseRollback", label: `unique ${e.name}` };
|
|
4951
|
-
}
|
|
4952
|
-
function compileDerive(e, nextId) {
|
|
4953
|
-
const keyCols = Object.keys(e.keys);
|
|
4954
|
-
if (keyCols.length === 0) throw new Error(`write-plan: derive on '${e.table}' declares no keys`);
|
|
4955
|
-
const whereSql = v1EqualityWhereText(keyCols);
|
|
4956
|
-
const op = {
|
|
4957
|
-
sql: `UPDATE ${e.table} SET ${e.attribute} = ${e.attribute} + ? WHERE ${whereSql}`,
|
|
4958
|
-
// SET amount is the first param (before the WHERE keys), matching the v1 param order.
|
|
4959
|
-
params: [e.amount, ...keyCols.map((c) => pathToRef(e.keys[c]))]
|
|
4960
|
-
};
|
|
4961
|
-
return { id: nextId("derive"), role: "derive", op, label: `derive ${e.table}.${e.attribute}` };
|
|
4962
|
-
}
|
|
4963
|
-
function compileEdge(e, nextId) {
|
|
4964
|
-
if (e.relation === "m2m") {
|
|
4965
|
-
const cols = Object.keys(e.columns);
|
|
4966
|
-
if (e.action === "set") {
|
|
4967
|
-
const placeholders2 = cols.map(() => "?").join(", ");
|
|
4968
|
-
const op3 = {
|
|
4969
|
-
sql: `INSERT INTO ${e.table} (${cols.join(", ")}) VALUES (${placeholders2})`,
|
|
4970
|
-
params: cols.map((c) => pathToRef(e.columns[c]))
|
|
4971
|
-
};
|
|
4972
|
-
return { id: nextId("edge"), role: "edge", op: op3, label: `edge m2m link ${e.table}` };
|
|
4973
|
-
}
|
|
4974
|
-
const whereSql2 = v1EqualityWhereText(cols);
|
|
4975
|
-
const op2 = {
|
|
4976
|
-
sql: `DELETE FROM ${e.table} WHERE ${whereSql2}`,
|
|
4977
|
-
params: cols.map((c) => pathToRef(e.columns[c]))
|
|
4978
|
-
};
|
|
4979
|
-
return { id: nextId("edge"), role: "edge", op: op2, label: `edge m2m unlink ${e.table}` };
|
|
4980
|
-
}
|
|
4981
|
-
const setCols = Object.keys(e.columns);
|
|
4982
|
-
const whereCols = Object.keys(e.where);
|
|
4983
|
-
const setClauses = setCols.map((c) => e.action === "set" ? `${c} = ?` : `${c} = NULL`);
|
|
4984
|
-
const setParams = e.action === "set" ? setCols.map((c) => pathToRef(e.columns[c])) : [];
|
|
4985
|
-
const whereSql = v1EqualityWhereText(whereCols);
|
|
4986
|
-
const op = {
|
|
4987
|
-
sql: `UPDATE ${e.table} SET ${setClauses.join(", ")} WHERE ${whereSql}`,
|
|
4988
|
-
params: [...setParams, ...whereCols.map((c) => pathToRef(e.where[c]))]
|
|
4989
|
-
};
|
|
4990
|
-
return { id: nextId("edge"), role: "edge", op, label: `edge fk ${e.action} ${e.table}` };
|
|
4991
|
-
}
|
|
4992
|
-
function compileEmit(e, nextId) {
|
|
4993
|
-
const payloadObj = {};
|
|
4994
|
-
for (const [k, v] of Object.entries(e.payload)) payloadObj[k] = pathToRef(v);
|
|
4995
|
-
const op = {
|
|
4996
|
-
sql: `INSERT INTO ${e.outboxTable} (type, payload) VALUES (?, ?)`,
|
|
4997
|
-
params: [e.name, { obj: payloadObj }]
|
|
4998
|
-
};
|
|
4999
|
-
return { id: nextId("emit"), role: "emit", op, label: `emit ${e.name}` };
|
|
5000
|
-
}
|
|
4881
|
+
var ENTITY_ROOT = "__entity";
|
|
5001
4882
|
function stringPort(ports, name) {
|
|
5002
4883
|
const v = ports[name];
|
|
5003
4884
|
if (v === void 0) return void 0;
|
|
@@ -5053,11 +4934,6 @@ function v1ConditionText(conditions) {
|
|
|
5053
4934
|
const probe = [];
|
|
5054
4935
|
return new DBConditions(conditions).compile(probe);
|
|
5055
4936
|
}
|
|
5056
|
-
function v1EqualityWhereText(columns) {
|
|
5057
|
-
const conditions = {};
|
|
5058
|
-
for (const c of columns) conditions[c] = PROBE;
|
|
5059
|
-
return v1ConditionText(conditions);
|
|
5060
|
-
}
|
|
5061
4937
|
function lowerWhereMember(node, at, dialect) {
|
|
5062
4938
|
const op = opKey(node);
|
|
5063
4939
|
if (op === void 0) throw new Error(`compileWriteNode: ${at}: a where member must be a single-operator Expression node`);
|
|
@@ -5195,7 +5071,7 @@ function compileWriteNodeSql(node, dialect, resolveColumnType) {
|
|
|
5195
5071
|
const sorted = [...cols].sort();
|
|
5196
5072
|
if (stringPort(ports, "batch") === "true") {
|
|
5197
5073
|
if (dialect === "postgres") {
|
|
5198
|
-
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
|
|
5074
|
+
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).`);
|
|
5199
5075
|
return pgBatchInsert(table, sorted, values, ports, resolveColumnType, dialect);
|
|
5200
5076
|
}
|
|
5201
5077
|
const shapeOpts = { tableName: table, columns: sorted, records: [], ...onConflictJsonOpts(ports, sorted), ...stringPort(ports, "returning") !== void 0 ? { returning: stringPort(ports, "returning") } : {} };
|
|
@@ -5223,7 +5099,7 @@ function compileWriteNodeSql(node, dialect, resolveColumnType) {
|
|
|
5223
5099
|
if (keyCols.length === 0) throw new Error(`compileWriteNode: batch Update requires at least one 'key.<field>' port`);
|
|
5224
5100
|
const updateCols = [...setCols].sort();
|
|
5225
5101
|
if (dialect === "postgres") {
|
|
5226
|
-
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
|
|
5102
|
+
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).`);
|
|
5227
5103
|
return pgBatchUpdate(table, keyCols, updateCols, key, set, ports, resolveColumnType, dialect);
|
|
5228
5104
|
}
|
|
5229
5105
|
const shapeOpts = { tableName: table, keyColumns: keyCols, updateColumns: updateCols, records: [], ...stringPort(ports, "returning") !== void 0 ? { returning: stringPort(ports, "returning") } : {} };
|
|
@@ -5255,133 +5131,9 @@ function compileWriteNodeSql(node, dialect, resolveColumnType) {
|
|
|
5255
5131
|
throw new Error(`compileWriteNode: catalog component '${component}' has no write compile (SQL writes: Insert/Update/Delete)`);
|
|
5256
5132
|
}
|
|
5257
5133
|
}
|
|
5258
|
-
function
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
const mk = (stmt, isGate) => {
|
|
5262
|
-
const consumes = refHeadsOf(stmt).filter((h) => h !== ENTITY_ROOT && !isInputHead(h, stmt));
|
|
5263
|
-
return { stmt, seq: seqRef.n++, consumes, produces: stmt.binds ?? null, isGate };
|
|
5264
|
-
};
|
|
5265
|
-
for (const r of e.requires ?? []) nodes.push(mk(compileRequires(r, nextId), true));
|
|
5266
|
-
if (e.idempotency !== void 0) nodes.push(mk(compileIdempotency(e.idempotency, nextId, dialect), true));
|
|
5267
|
-
for (const u of e.unique ?? []) nodes.push(mk(compileUnique(u, nextId, dialect), true));
|
|
5268
|
-
const bodyId = nextId("body");
|
|
5269
|
-
const bodyStmt = {
|
|
5270
|
-
id: bodyId,
|
|
5271
|
-
role: "body",
|
|
5272
|
-
op: base.op,
|
|
5273
|
-
label: base.label,
|
|
5274
|
-
...base.name !== void 0 ? { binds: base.name } : {}
|
|
5275
|
-
};
|
|
5276
|
-
nodes.push(mk(bodyStmt, false));
|
|
5277
|
-
for (const d of e.derive ?? []) nodes.push(mk(compileDerive(d, nextId), false));
|
|
5278
|
-
for (const ed of e.edges ?? []) nodes.push(mk(compileEdge(ed, nextId), false));
|
|
5279
|
-
for (const em of e.emits ?? []) nodes.push(mk(compileEmit(em, nextId), false));
|
|
5280
|
-
return { nodes, bodyId };
|
|
5281
|
-
}
|
|
5282
|
-
function topoOrder(nodes) {
|
|
5283
|
-
const byName = /* @__PURE__ */ new Map();
|
|
5284
|
-
for (const n of nodes) {
|
|
5285
|
-
if (n.produces !== null) {
|
|
5286
|
-
if (byName.has(n.produces)) {
|
|
5287
|
-
throw new Error(`write-plan: two writes both bind the name '${n.produces}' \u2014 write names must be unique in a composite Command.`);
|
|
5288
|
-
}
|
|
5289
|
-
byName.set(n.produces, n);
|
|
5290
|
-
}
|
|
5291
|
-
}
|
|
5292
|
-
const succ = /* @__PURE__ */ new Map();
|
|
5293
|
-
const indeg = /* @__PURE__ */ new Map();
|
|
5294
|
-
const nodeById = /* @__PURE__ */ new Map();
|
|
5295
|
-
for (const n of nodes) {
|
|
5296
|
-
nodeById.set(n.stmt.id, n);
|
|
5297
|
-
succ.set(n.stmt.id, /* @__PURE__ */ new Set());
|
|
5298
|
-
indeg.set(n.stmt.id, 0);
|
|
5299
|
-
}
|
|
5300
|
-
const addEdge = (fromId, toId) => {
|
|
5301
|
-
if (fromId === toId) return;
|
|
5302
|
-
const s = succ.get(fromId);
|
|
5303
|
-
if (!s.has(toId)) {
|
|
5304
|
-
s.add(toId);
|
|
5305
|
-
indeg.set(toId, indeg.get(toId) + 1);
|
|
5306
|
-
}
|
|
5307
|
-
};
|
|
5308
|
-
for (const n of nodes) {
|
|
5309
|
-
for (const dep of n.consumes) {
|
|
5310
|
-
const producer = byName.get(dep);
|
|
5311
|
-
if (producer === void 0) {
|
|
5312
|
-
throw new Error(
|
|
5313
|
-
`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).`
|
|
5314
|
-
);
|
|
5315
|
-
}
|
|
5316
|
-
addEdge(producer.stmt.id, n.stmt.id);
|
|
5317
|
-
}
|
|
5318
|
-
}
|
|
5319
|
-
const gates = nodes.filter((n) => n.isGate);
|
|
5320
|
-
const nonGates = nodes.filter((n) => !n.isGate);
|
|
5321
|
-
for (const g of gates) for (const b of nonGates) addEdge(g.stmt.id, b.stmt.id);
|
|
5322
|
-
const ordered = [];
|
|
5323
|
-
const ready = nodes.filter((n) => indeg.get(n.stmt.id) === 0).sort((a, b) => a.seq - b.seq);
|
|
5324
|
-
while (ready.length > 0) {
|
|
5325
|
-
const n = ready.shift();
|
|
5326
|
-
ordered.push(n.stmt);
|
|
5327
|
-
for (const depId of succ.get(n.stmt.id)) {
|
|
5328
|
-
indeg.set(depId, indeg.get(depId) - 1);
|
|
5329
|
-
if (indeg.get(depId) === 0) {
|
|
5330
|
-
const dn = nodeById.get(depId);
|
|
5331
|
-
let lo = 0;
|
|
5332
|
-
while (lo < ready.length && ready[lo].seq < dn.seq) lo++;
|
|
5333
|
-
ready.splice(lo, 0, dn);
|
|
5334
|
-
}
|
|
5335
|
-
}
|
|
5336
|
-
}
|
|
5337
|
-
if (ordered.length !== nodes.length) {
|
|
5338
|
-
const stuck = nodes.filter((n) => !ordered.includes(n.stmt)).map((n) => n.stmt.label);
|
|
5339
|
-
throw new Error(
|
|
5340
|
-
`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.)`
|
|
5341
|
-
);
|
|
5342
|
-
}
|
|
5343
|
-
return ordered;
|
|
5344
|
-
}
|
|
5345
|
-
function deriveTransactionPlan(phase, bases, lifecycle, dialect = "sqlite") {
|
|
5346
|
-
if (bases.length === 0) {
|
|
5347
|
-
throw new Error("write-plan: a Command must declare at least one base write (Insert/Update/Delete).");
|
|
5348
|
-
}
|
|
5349
|
-
const nextId = makeIdGen();
|
|
5350
|
-
const seqRef = { n: 0 };
|
|
5351
|
-
const composite = bases.length > 1;
|
|
5352
|
-
const allNodes = [];
|
|
5353
|
-
const bodyIds = [];
|
|
5354
|
-
bases.forEach((base, i) => {
|
|
5355
|
-
const lc = base.effects !== void 0 ? { effects: base.effects } : i === 0 ? lifecycle : { effects: {} };
|
|
5356
|
-
const { nodes, bodyId } = compileWriteGroup(base, lc, nextId, dialect, seqRef);
|
|
5357
|
-
allNodes.push(...nodes);
|
|
5358
|
-
bodyIds.push(bodyId);
|
|
5359
|
-
});
|
|
5360
|
-
const statements = topoOrder(allNodes);
|
|
5361
|
-
const soleBody = bases[0];
|
|
5362
|
-
const soleBodyId = bodyIds[0];
|
|
5363
|
-
const usesEntity = referencesHead(statements, ENTITY_ROOT);
|
|
5364
|
-
const soleBodyReturns = /\breturning\b/i.test(soleBody.op.sql);
|
|
5365
|
-
if (usesEntity && composite) {
|
|
5366
|
-
throw new Error(
|
|
5367
|
-
`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).`
|
|
5368
|
-
);
|
|
5369
|
-
}
|
|
5370
|
-
if (usesEntity && !soleBodyReturns) {
|
|
5371
|
-
throw new Error(
|
|
5372
|
-
`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).`
|
|
5373
|
-
);
|
|
5374
|
-
}
|
|
5375
|
-
const entityFrom = usesEntity || !composite && soleBodyReturns ? soleBodyId : null;
|
|
5376
|
-
for (const base of bases) {
|
|
5377
|
-
if (base.name === void 0) continue;
|
|
5378
|
-
if (referencesHead(statements, base.name) && !/\breturning\b/i.test(base.op.sql)) {
|
|
5379
|
-
throw new Error(
|
|
5380
|
-
`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).`
|
|
5381
|
-
);
|
|
5382
|
-
}
|
|
5383
|
-
}
|
|
5384
|
-
return { phase, entityFrom, statements, onIdempotentHit: "rollback" };
|
|
5134
|
+
function makeIdGen() {
|
|
5135
|
+
let n = 0;
|
|
5136
|
+
return (role) => `tx_${role}_${n++}`;
|
|
5385
5137
|
}
|
|
5386
5138
|
function deriveBatchPlan(phase, ops) {
|
|
5387
5139
|
if (ops.length === 0) {
|
|
@@ -5396,55 +5148,6 @@ function deriveBatchPlan(phase, ops) {
|
|
|
5396
5148
|
}));
|
|
5397
5149
|
return { phase, entityFrom: null, statements, onIdempotentHit: "rollback" };
|
|
5398
5150
|
}
|
|
5399
|
-
function refHeadsOf(stmt) {
|
|
5400
|
-
const heads = /* @__PURE__ */ new Set();
|
|
5401
|
-
collectRefHeads(stmt.op.params, heads);
|
|
5402
|
-
return [...heads];
|
|
5403
|
-
}
|
|
5404
|
-
function isInputHead(head, stmt) {
|
|
5405
|
-
return refPathLengthFor(stmt.op.params, head) === 1;
|
|
5406
|
-
}
|
|
5407
|
-
function refPathLengthFor(node, head) {
|
|
5408
|
-
let maxLen = 0;
|
|
5409
|
-
const walk = (n) => {
|
|
5410
|
-
if (n === null || typeof n !== "object") return;
|
|
5411
|
-
if (Array.isArray(n)) {
|
|
5412
|
-
for (const el of n) walk(el);
|
|
5413
|
-
return;
|
|
5414
|
-
}
|
|
5415
|
-
const keys = Object.keys(n);
|
|
5416
|
-
if (keys.length === 1 && keys[0] === "ref") {
|
|
5417
|
-
const path = n.ref;
|
|
5418
|
-
if (Array.isArray(path) && path[0] === head) maxLen = Math.max(maxLen, path.length);
|
|
5419
|
-
return;
|
|
5420
|
-
}
|
|
5421
|
-
for (const k of keys) walk(n[k]);
|
|
5422
|
-
};
|
|
5423
|
-
walk(node);
|
|
5424
|
-
return maxLen;
|
|
5425
|
-
}
|
|
5426
|
-
function referencesHead(statements, head) {
|
|
5427
|
-
const heads = /* @__PURE__ */ new Set();
|
|
5428
|
-
for (const s of statements) collectRefHeads(s.op.params, heads);
|
|
5429
|
-
return heads.has(head);
|
|
5430
|
-
}
|
|
5431
|
-
function collectRefHeads(node, heads) {
|
|
5432
|
-
if (node === null || typeof node !== "object") return;
|
|
5433
|
-
if (Array.isArray(node)) {
|
|
5434
|
-
for (const el of node) collectRefHeads(el, heads);
|
|
5435
|
-
return;
|
|
5436
|
-
}
|
|
5437
|
-
const keys = Object.keys(node);
|
|
5438
|
-
if (keys.length === 1 && keys[0] === "ref") {
|
|
5439
|
-
const path = node.ref;
|
|
5440
|
-
if (Array.isArray(path) && typeof path[0] === "string") heads.add(path[0]);
|
|
5441
|
-
return;
|
|
5442
|
-
}
|
|
5443
|
-
for (const k of keys) collectRefHeads(node[k], heads);
|
|
5444
|
-
}
|
|
5445
|
-
function asContext(dbOrCtx) {
|
|
5446
|
-
return "connectionFor" in dbOrCtx ? dbOrCtx : contextForDriver(dbOrCtx);
|
|
5447
|
-
}
|
|
5448
5151
|
function toDriverParam(v, dialect) {
|
|
5449
5152
|
if (typeof v === "bigint") {
|
|
5450
5153
|
if (v >= BigInt(Number.MIN_SAFE_INTEGER) && v <= BigInt(Number.MAX_SAFE_INTEGER)) return Number(v);
|
|
@@ -5462,12 +5165,6 @@ function renderStatement(op, scope, dialect) {
|
|
|
5462
5165
|
const { sql: sql2, params } = evalAssemble(op, scope, dialect);
|
|
5463
5166
|
return { sql: renderPlaceholders(sql2, dialect), params };
|
|
5464
5167
|
}
|
|
5465
|
-
function execStatement(ctx, op, scope, dialect) {
|
|
5466
|
-
const { sql: sql2, params } = evalAssemble(op, scope, dialect);
|
|
5467
|
-
const hasReturn = /\bselect\b/i.test(sql2.slice(0, 8)) || /\breturning\b/i.test(sql2);
|
|
5468
|
-
const out = executeSQL({ sql: sql2, params, write: true, returning: hasReturn, bigint: false }, { exec: ctx, dialect });
|
|
5469
|
-
return hasReturn ? { rows: out, changes: out.length } : { rows: [], changes: Number(out[0]?.changes ?? 0) };
|
|
5470
|
-
}
|
|
5471
5168
|
function gateShortCircuit(gate, result) {
|
|
5472
5169
|
switch (gate) {
|
|
5473
5170
|
case "existsElseRollback":
|
|
@@ -5480,60 +5177,13 @@ function gateShortCircuit(gate, result) {
|
|
|
5480
5177
|
throw new Error(`scp write: unknown gate rule '${String(gate)}'`);
|
|
5481
5178
|
}
|
|
5482
5179
|
}
|
|
5483
|
-
function executeTransaction(db, plan, input, dialect = "sqlite") {
|
|
5484
|
-
const outer = asContext(db);
|
|
5485
|
-
const executed = [];
|
|
5486
|
-
const scope = { ...input };
|
|
5487
|
-
let entity = null;
|
|
5488
|
-
const isBatch = plan.entityFrom === null && plan.statements.every((s) => s.gate === void 0 && s.binds === void 0 && s.role === "body");
|
|
5489
|
-
const returnedRows = [];
|
|
5490
|
-
try {
|
|
5491
|
-
return withTransactionSync(
|
|
5492
|
-
outer,
|
|
5493
|
-
(ctx) => {
|
|
5494
|
-
for (const stmt of plan.statements) {
|
|
5495
|
-
const result = execStatement(ctx, stmt.op, scope, dialect);
|
|
5496
|
-
executed.push(stmt.id);
|
|
5497
|
-
if (stmt.gate !== void 0) {
|
|
5498
|
-
const reason = gateShortCircuit(stmt.gate, result);
|
|
5499
|
-
if (reason !== null) {
|
|
5500
|
-
return { commit: false, value: { committed: false, shortCircuit: { statementId: stmt.id, reason }, entity: null, executed } };
|
|
5501
|
-
}
|
|
5502
|
-
}
|
|
5503
|
-
if (stmt.id === plan.entityFrom) {
|
|
5504
|
-
entity = result.rows.length > 0 ? result.rows[0] : null;
|
|
5505
|
-
if (entity !== null) scope[ENTITY_ROOT] = entity;
|
|
5506
|
-
}
|
|
5507
|
-
if (stmt.binds !== void 0 && result.rows.length > 0) {
|
|
5508
|
-
scope[stmt.binds] = result.rows[0];
|
|
5509
|
-
}
|
|
5510
|
-
if (isBatch && stmt.role === "body" && result.rows.length > 0) returnedRows.push(result.rows);
|
|
5511
|
-
}
|
|
5512
|
-
return { commit: true, value: { committed: true, entity, executed, ...returnedRows.length > 0 ? { returnedRows } : {} } };
|
|
5513
|
-
},
|
|
5514
|
-
dialect
|
|
5515
|
-
);
|
|
5516
|
-
} catch (e) {
|
|
5517
|
-
throw mapSqliteError(e);
|
|
5518
|
-
}
|
|
5519
|
-
}
|
|
5520
|
-
function countingDriver(db) {
|
|
5521
|
-
const prepared = [];
|
|
5522
|
-
const wrapped = {
|
|
5523
|
-
prepare(sql2) {
|
|
5524
|
-
prepared.push(sql2);
|
|
5525
|
-
return db.prepare(sql2);
|
|
5526
|
-
}
|
|
5527
|
-
};
|
|
5528
|
-
return { db: wrapped, prepared };
|
|
5529
|
-
}
|
|
5530
5180
|
function renderTxStatement(op, scope, dialect = "sqlite") {
|
|
5531
5181
|
return renderStatement(op, scope, dialect);
|
|
5532
5182
|
}
|
|
5533
5183
|
async function execStatementAsync(ctx, op, scope, dialect) {
|
|
5534
5184
|
const { sql: sql2, params } = evalAssemble(op, scope, dialect);
|
|
5535
5185
|
const hasReturn = /\bselect\b/i.test(sql2.slice(0, 8)) || /\breturning\b/i.test(sql2);
|
|
5536
|
-
const out = await executeSQLAsync({ sql: sql2, params, write:
|
|
5186
|
+
const out = await executeSQLAsync({ sql: sql2, params, write: { returning: hasReturn } }, { execAsync: ctx, dialect });
|
|
5537
5187
|
return hasReturn ? { rows: out, changes: out.length } : { rows: [], changes: Number(out[0]?.changes ?? 0) };
|
|
5538
5188
|
}
|
|
5539
5189
|
function executeTransactionAsync(ctx, plan, input, dialect = "sqlite", options = {}) {
|
|
@@ -5743,8 +5393,8 @@ function declarationStub(leafName, args) {
|
|
|
5743
5393
|
);
|
|
5744
5394
|
}
|
|
5745
5395
|
var Db = class {
|
|
5746
|
-
static executeSQL(sql2, params,
|
|
5747
|
-
return declarationStub("executeSQL", [sql2, params,
|
|
5396
|
+
static executeSQL(sql2, params, opts) {
|
|
5397
|
+
return declarationStub("executeSQL", [sql2, params, opts]);
|
|
5748
5398
|
}
|
|
5749
5399
|
static pluck(rows, col) {
|
|
5750
5400
|
return declarationStub("pluck", [rows, col]);
|
|
@@ -5947,11 +5597,7 @@ function targetKeyCols(op) {
|
|
|
5947
5597
|
}
|
|
5948
5598
|
function relationGuard(op) {
|
|
5949
5599
|
if (op.hardLimit === void 0) return null;
|
|
5950
|
-
return {
|
|
5951
|
-
limit: op.hardLimit,
|
|
5952
|
-
...op.targetTable !== void 0 ? { model: op.targetTable } : {},
|
|
5953
|
-
relation: op.name
|
|
5954
|
-
};
|
|
5600
|
+
return { limit: op.hardLimit, model: op.targetTable, relation: op.name };
|
|
5955
5601
|
}
|
|
5956
5602
|
function bindKeys(op, tuples) {
|
|
5957
5603
|
if (op.parentKeys !== void 0) return [encodeJsonParam(op.dialect, tuples.map((t) => [...t]))];
|
|
@@ -5969,7 +5615,10 @@ function runRelationOp(op, parents, db) {
|
|
|
5969
5615
|
const tCols = targetKeyCols(op);
|
|
5970
5616
|
const childCols = op.materializers;
|
|
5971
5617
|
const boundParams = bindKeys(op, keys);
|
|
5972
|
-
const rawRows = executeSafe(ctx, sql2, boundParams
|
|
5618
|
+
const rawRows = executeSafe(ctx, sql2, boundParams, {
|
|
5619
|
+
write: false,
|
|
5620
|
+
...op.connection !== void 0 ? { db: op.connection } : {}
|
|
5621
|
+
});
|
|
5973
5622
|
assertRelationHardLimit(rawRows, relationGuard(op));
|
|
5974
5623
|
const rows = materializeChildRows(rawRows, childCols);
|
|
5975
5624
|
return { sql: sql2, keys, batch: groupByKey(rows, tCols) };
|
|
@@ -6089,27 +5738,6 @@ function annotateWriteBundleOutType(bundle, resolve) {
|
|
|
6089
5738
|
}
|
|
6090
5739
|
|
|
6091
5740
|
// src/scp/write-bundle.ts
|
|
6092
|
-
function writeLabelOf(sql2) {
|
|
6093
|
-
const m = /^\s*(INSERT|UPDATE|DELETE)/i.exec(sql2);
|
|
6094
|
-
return m === null ? "Write" : m[1][0].toUpperCase() + m[1].slice(1).toLowerCase();
|
|
6095
|
-
}
|
|
6096
|
-
function compileWriteBundle(name, base, writes, phase, dialectName = "sqlite", resolveColumnType) {
|
|
6097
|
-
const lifecycle = lifecycleFor(writes, phase);
|
|
6098
|
-
if (lifecycle === void 0) {
|
|
6099
|
-
throw new Error(`scp write: the '${phase}' lifecycle is not declared in the entityWrites save contract`);
|
|
6100
|
-
}
|
|
6101
|
-
const plan = deriveTransactionPlan(phase, [{ op: base, label: writeLabelOf(base.sql) }], lifecycle, dialectName);
|
|
6102
|
-
const bundle = { dialect: dialectName, name, statement: base, transaction: plan };
|
|
6103
|
-
return resolveColumnType === void 0 ? bundle : annotateWriteBundleOutType(bundle, resolveColumnType);
|
|
6104
|
-
}
|
|
6105
|
-
function compileCompositeWriteBundle(entries, phase, dialectName = "sqlite") {
|
|
6106
|
-
if (entries.length < 2) {
|
|
6107
|
-
throw new Error("scp write: a composite write bundle needs at least 2 named write members (use compileWriteBundle for a single write).");
|
|
6108
|
-
}
|
|
6109
|
-
const bases = entries.map((e) => ({ op: e.base, label: `${writeLabelOf(e.base.sql)} ${e.name}`, name: e.name, effects: e.lifecycle.effects }));
|
|
6110
|
-
const plan = deriveTransactionPlan(phase, bases, { effects: {} }, dialectName);
|
|
6111
|
-
return { dialect: dialectName, name: entries[0].name, statement: entries[0].base, transaction: plan };
|
|
6112
|
-
}
|
|
6113
5741
|
function flattenBatchOp(node, label) {
|
|
6114
5742
|
const assembled = assembleMakeSQL(node);
|
|
6115
5743
|
return { sql: assembled.sql, params: assembled.params, label };
|
|
@@ -6146,9 +5774,6 @@ function compileDeleteManyBundle(name, options, dialectName = "sqlite") {
|
|
|
6146
5774
|
const plan = deriveBatchPlan("remove", ops);
|
|
6147
5775
|
return { dialect: dialectName, name, statement: { sql: ops[0].sql, params: ops[0].params }, transaction: plan };
|
|
6148
5776
|
}
|
|
6149
|
-
function executeTransactionBundle(bundle, input, options) {
|
|
6150
|
-
return executeTransaction(options.db, bundle.transaction, input, bundle.dialect);
|
|
6151
|
-
}
|
|
6152
5777
|
|
|
6153
5778
|
// src/decorators.ts
|
|
6154
5779
|
var import_reflect_metadata = __toESM(require_Reflect());
|
|
@@ -6669,6 +6294,9 @@ function deriveModelColumns(modelClass, options = {}) {
|
|
|
6669
6294
|
function tableNameOf(modelClass) {
|
|
6670
6295
|
return modelClass.TABLE_NAME ?? modelClass.name.toLowerCase();
|
|
6671
6296
|
}
|
|
6297
|
+
function connectionOf(modelClass) {
|
|
6298
|
+
return modelClass.CONNECTION;
|
|
6299
|
+
}
|
|
6672
6300
|
function compileCreateBundle(modelClass, name, options, dialect = "sqlite", columnsOptions) {
|
|
6673
6301
|
const resolveColumnType = modelColumnResolver(modelClass, columnsOptions);
|
|
6674
6302
|
return compileCreateManyBundle(name, options, dialect, resolveColumnType);
|
|
@@ -6709,12 +6337,14 @@ function relationDeclOf(rel, resolveTargetModel, dialect = "sqlite") {
|
|
|
6709
6337
|
const targetTable = tableNameOf(targetModel);
|
|
6710
6338
|
const select = targetProjection(targetModel);
|
|
6711
6339
|
const order = rel.options?.order ? orderToString(rel.options.order()) : void 0;
|
|
6340
|
+
const connection = connectionOf(targetModel);
|
|
6712
6341
|
const base = {
|
|
6713
6342
|
name: rel.propertyKey,
|
|
6714
6343
|
kind: rel.type,
|
|
6715
6344
|
targetTable,
|
|
6716
6345
|
select,
|
|
6717
6346
|
dialect,
|
|
6347
|
+
...connection !== void 0 ? { connection } : {},
|
|
6718
6348
|
...order !== void 0 ? { order } : {},
|
|
6719
6349
|
...rel.options?.limit !== void 0 ? { limit: rel.options.limit } : {},
|
|
6720
6350
|
...rel.options?.hardLimit !== void 0 ? { hardLimit: rel.options.hardLimit } : {}
|
|
@@ -6806,6 +6436,20 @@ var EmitContext = class {
|
|
|
6806
6436
|
bindTypeOf(model, column2) {
|
|
6807
6437
|
return tsScalar(sqlTypeToBcScalar(this.sqlTypeOf(model, column2)));
|
|
6808
6438
|
}
|
|
6439
|
+
/**
|
|
6440
|
+
* The NAMED connection every statement of an endpoint declared over `model` runs on — read from the
|
|
6441
|
+
* model, the ONE authority ({@link connectionOf}), and handed to {@link execOptions} as its `db`
|
|
6442
|
+
* field. `{}` ⇒ the DEFAULT connection, which leaves a bounded read's option record unspelled
|
|
6443
|
+
* entirely (native-clean).
|
|
6444
|
+
*
|
|
6445
|
+
* A RELATION's child fetch does NOT come through here: its connection is the TARGET model's, which
|
|
6446
|
+
* `relationDeclOf` already resolved onto the compiled op (`RelationOp.connection`) — the emitter
|
|
6447
|
+
* consumes that rather than re-deriving it from a second model handle.
|
|
6448
|
+
*/
|
|
6449
|
+
dbOf(model) {
|
|
6450
|
+
const db = connectionOf(model);
|
|
6451
|
+
return db !== void 0 ? { db } : {};
|
|
6452
|
+
}
|
|
6809
6453
|
// ── methods ────────────────────────────────────────────────────────────────────────────────
|
|
6810
6454
|
method(name, endpoint) {
|
|
6811
6455
|
assertIdentifier(name, `endpoint name '${name}'`);
|
|
@@ -6869,12 +6513,7 @@ var EmitContext = class {
|
|
|
6869
6513
|
const readResolve = endpoint.view !== void 0 ? (t, c) => resolve(t === alias ? baseTable : t, c) : resolve;
|
|
6870
6514
|
const cap = this.bakedFindCap(endpoint);
|
|
6871
6515
|
const where = endpoint.where ?? [];
|
|
6872
|
-
const
|
|
6873
|
-
if (dynamic && endpoint.view !== void 0) {
|
|
6874
|
-
throw new Error(
|
|
6875
|
-
`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.`
|
|
6876
|
-
);
|
|
6877
|
-
}
|
|
6516
|
+
const optional = where.filter(isOptional);
|
|
6878
6517
|
const params = [];
|
|
6879
6518
|
for (const p of where) this.declareWhereParams(model, p, params);
|
|
6880
6519
|
const limit = this.page(endpoint.limit ?? (cap !== null ? cap + 1 : void 0), params);
|
|
@@ -6883,15 +6522,21 @@ var EmitContext = class {
|
|
|
6883
6522
|
dialect: this.spec.dialect,
|
|
6884
6523
|
tableName: table,
|
|
6885
6524
|
select: projection.join(", "),
|
|
6886
|
-
|
|
6525
|
+
// The BOUNDED predicates — this read's static WHERE, whatever else it declares. (An all-optional
|
|
6526
|
+
// read compiles an EMPTY condition set, which is the same no-WHERE statement it always was.)
|
|
6527
|
+
conditions: this.conditions(model, where.filter((p) => !isOptional(p))),
|
|
6887
6528
|
...endpoint.view !== void 0 ? { cte: cteOf(endpoint.view) } : {},
|
|
6888
6529
|
...endpoint.order !== void 0 ? { order: endpoint.order } : {},
|
|
6889
6530
|
...limit !== void 0 ? { limit } : {},
|
|
6890
6531
|
...offset !== void 0 ? { offset } : {},
|
|
6891
6532
|
...endpoint.lock === "update" ? { forUpdate: true } : endpoint.lock === "share" ? { forShare: true } : {}
|
|
6892
6533
|
});
|
|
6893
|
-
const
|
|
6894
|
-
const
|
|
6534
|
+
const plan = optional.length > 0 ? this.dynamicWherePlan(model, optional, compiled) : void 0;
|
|
6535
|
+
const [sql2, slots] = plan === void 0 ? [compiled.sql, compiled.params] : [compiled.head, compiled.headParams];
|
|
6536
|
+
const call = `${quote(sql2)}, [${slots.map(renderSlot).join(", ")}]${execOptions({
|
|
6537
|
+
...this.dbOf(model),
|
|
6538
|
+
...plan !== void 0 ? { whereDynamic: plan } : {}
|
|
6539
|
+
})}`;
|
|
6895
6540
|
const rowObj = objOf(deriveReadRow(table, projection, readResolve, `endpoint '${name}'`).outType, name);
|
|
6896
6541
|
const selections = normalizeSelections(endpoint.with ?? []);
|
|
6897
6542
|
const fresh = freshNames(params.map((p) => p.name));
|
|
@@ -6908,24 +6553,35 @@ var EmitContext = class {
|
|
|
6908
6553
|
const lines = [];
|
|
6909
6554
|
const parentVar = fresh("rows");
|
|
6910
6555
|
lines.push(`const ${parentVar}: WireValue[] = Db.executeSQL(${call});`);
|
|
6911
|
-
const graph = this.relations(model, parentVar, rowObj, selections, lines, fresh,
|
|
6556
|
+
const graph = this.relations(model, parentVar, rowObj, selections, lines, fresh, `endpoint '${name}'`);
|
|
6912
6557
|
const rowType = this.declareInterface(`${cap1(name)}Row`, graph.obj);
|
|
6913
6558
|
lines[lines.length - 1] = `const ${graph.terminalVar}: ${rowType}[] = ${graph.terminalCall} as ${rowType}[];`;
|
|
6914
6559
|
lines.push(`return ${graph.terminalVar};`);
|
|
6915
6560
|
return { name, params, returnType: `${rowType}[]`, lines };
|
|
6916
6561
|
}
|
|
6917
6562
|
/**
|
|
6918
|
-
* The DYNAMIC WHERE plan expression:
|
|
6919
|
-
*
|
|
6920
|
-
*
|
|
6563
|
+
* The DYNAMIC WHERE plan expression: ONE fragment per OPTIONAL predicate — never a bounded one, which
|
|
6564
|
+
* is already in the static statement (CLAUDE.md §2) — every fragment the SAME struct
|
|
6565
|
+
* `{skipped, sql, params}`, with `sql`/`params` from the SAME `compileWhere` that produces a bounded
|
|
6566
|
+
* WHERE and `skipped` the per-call SKIP decision carried as DATA (`param === null`). That is the whole
|
|
6567
|
+
* fragment vocabulary (SQL + params + a SKIP flag). The array is HOMOGENEOUS — never a `cond`-to-null
|
|
6568
|
+
* element — so the go/rust native-codegen emitters resolve it (a variant/nullable array element is the
|
|
6569
|
+
* one shape they reject); the `executeSQL` leaf drops the `skipped` fragments at run and continues the
|
|
6570
|
+
* static WHERE with the survivors.
|
|
6571
|
+
*
|
|
6572
|
+
* The plan also carries what FINISHES the statement, taken straight off the bundle this same read
|
|
6573
|
+
* compiled ({@link import('../makesql/compile-select').SelectBundle}): `lead` — the connector the first
|
|
6574
|
+
* survivor joins with, which is `AND` exactly when the builder emitted a static WHERE — and `tail` /
|
|
6575
|
+
* `tailParams`, the text and bound values that follow the WHERE region. The leaf then concatenates
|
|
6576
|
+
* `head` + clause + `tail`; nothing on the run side has to find the boundary again, which is what a
|
|
6577
|
+
* lexical scan got wrong on a nested statement (#198) and on a quoted `?` (#202).
|
|
6921
6578
|
*/
|
|
6922
|
-
dynamicWherePlan(model,
|
|
6923
|
-
const frags =
|
|
6579
|
+
dynamicWherePlan(model, optional, compiled) {
|
|
6580
|
+
const frags = optional.map((p) => {
|
|
6924
6581
|
const w = compileWhere(this.conditions(model, [p]), this.spec.dialect);
|
|
6925
|
-
|
|
6926
|
-
return isParameterised(p) && p.optional === true ? `${p.param} !== null ? ${frag} : null` : frag;
|
|
6582
|
+
return `{ skipped: ${p.param} === null, sql: ${quote(w.sql)}, params: [${w.params.map(renderSlot).join(", ")}] }`;
|
|
6927
6583
|
});
|
|
6928
|
-
return `{ frags: [${frags.join(", ")}] }`;
|
|
6584
|
+
return `{ frags: [${frags.join(", ")}], lead: ${quote(compiled.lead)}, tail: ${quote(compiled.tail)}, tailParams: [${compiled.tailParams.map(renderSlot).join(", ")}] }`;
|
|
6929
6585
|
}
|
|
6930
6586
|
/**
|
|
6931
6587
|
* Emit ONE relation LEVEL: for each selected relation, pluck the parent key set, run the ONE batched
|
|
@@ -6933,7 +6589,7 @@ var EmitContext = class {
|
|
|
6933
6589
|
* selection recurses FIRST, so a grandchild level is grouped into its own parent before that parent
|
|
6934
6590
|
* is grouped into ours (one query per depth — never one per parent).
|
|
6935
6591
|
*/
|
|
6936
|
-
relations(model, parentVar, parentObj, selections, lines, fresh,
|
|
6592
|
+
relations(model, parentVar, parentObj, selections, lines, fresh, at) {
|
|
6937
6593
|
let acc = parentVar;
|
|
6938
6594
|
const obj = { ...parentObj };
|
|
6939
6595
|
let terminalCall = "";
|
|
@@ -6945,15 +6601,20 @@ var EmitContext = class {
|
|
|
6945
6601
|
lines.push(`const ${keysVar}: WireValue[] = Db.pluck(${acc}, ${jsonArray(parentKeyCols(op))});`);
|
|
6946
6602
|
const childVar = fresh(sel.name);
|
|
6947
6603
|
const guard = relationGuard(op);
|
|
6948
|
-
const
|
|
6949
|
-
lines.push(
|
|
6604
|
+
const guardExpr = guard !== null ? `{ limit: ${guard.limit} as Int, model: ${quote(guard.model)}, relation: ${quote(guard.relation)} }` : void 0;
|
|
6605
|
+
lines.push(
|
|
6606
|
+
`const ${childVar}: WireValue[] = Db.executeSQL(${quote(op.sql)}, [${keysVar}]${execOptions({
|
|
6607
|
+
...op.connection !== void 0 ? { db: op.connection } : {},
|
|
6608
|
+
...guardExpr !== void 0 ? { guard: guardExpr } : {}
|
|
6609
|
+
})});`
|
|
6610
|
+
);
|
|
6950
6611
|
let childRows = childVar;
|
|
6951
6612
|
let childObj = objOf(
|
|
6952
6613
|
deriveReadRow(decl.targetTable, decl.select, this.resolver(targetModel), `${at} relation '${sel.name}'`).outType,
|
|
6953
6614
|
`${at} relation '${sel.name}'`
|
|
6954
6615
|
);
|
|
6955
6616
|
if (sel.with.length > 0) {
|
|
6956
|
-
const nested = this.relations(targetModel, childVar, childObj, normalizeSelections(sel.with), lines, fresh,
|
|
6617
|
+
const nested = this.relations(targetModel, childVar, childObj, normalizeSelections(sel.with), lines, fresh, `${at}.${sel.name}`);
|
|
6957
6618
|
childObj = nested.obj;
|
|
6958
6619
|
childRows = nested.terminalVar;
|
|
6959
6620
|
}
|
|
@@ -7010,7 +6671,7 @@ var EmitContext = class {
|
|
|
7010
6671
|
params,
|
|
7011
6672
|
returnType: `${rowType}[]`,
|
|
7012
6673
|
lines: [
|
|
7013
|
-
`const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${op.params.map(renderSlot).join(", ")}]
|
|
6674
|
+
`const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${op.params.map(renderSlot).join(", ")}]${execOptions({ ...this.dbOf(model), write: { returning: returning !== void 0 } })}) as ${rowType}[];`,
|
|
7014
6675
|
`return ${out};`
|
|
7015
6676
|
]
|
|
7016
6677
|
};
|
|
@@ -7087,7 +6748,7 @@ var EmitContext = class {
|
|
|
7087
6748
|
params,
|
|
7088
6749
|
returnType: `${rowType}[]`,
|
|
7089
6750
|
lines: [
|
|
7090
|
-
`const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${slots.join(", ")}]
|
|
6751
|
+
`const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${slots.join(", ")}]${execOptions({ ...this.dbOf(model), write: { returning: endpoint.returning !== void 0 } })}) as ${rowType}[];`,
|
|
7091
6752
|
`return ${out};`
|
|
7092
6753
|
]
|
|
7093
6754
|
};
|
|
@@ -7115,7 +6776,7 @@ var EmitContext = class {
|
|
|
7115
6776
|
params,
|
|
7116
6777
|
returnType: `${rowType}[]`,
|
|
7117
6778
|
lines: [
|
|
7118
|
-
`const ${out}: ${rowType}[] = Db.executeSQL(${quote(op.sql)}, [${op.params.map(renderSlot).join(", ")}]
|
|
6779
|
+
`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}[];`,
|
|
7119
6780
|
`return ${out};`
|
|
7120
6781
|
]
|
|
7121
6782
|
};
|
|
@@ -7333,6 +6994,11 @@ function isParameterised(p) {
|
|
|
7333
6994
|
function isOptional(p) {
|
|
7334
6995
|
return isParameterised(p) && p.optional === true;
|
|
7335
6996
|
}
|
|
6997
|
+
function execOptions(o) {
|
|
6998
|
+
if (o.db === void 0 && o.write === void 0 && o.whereDynamic === void 0 && o.guard === void 0) return "";
|
|
6999
|
+
const write = o.write === void 0 ? "null" : `{ returning: ${o.write.returning} }`;
|
|
7000
|
+
return `, { db: ${o.db === void 0 ? "null" : quote(o.db)}, write: ${write}, whereDynamic: ${o.whereDynamic ?? "null"}, guard: ${o.guard ?? "null"} }`;
|
|
7001
|
+
}
|
|
7336
7002
|
function renderSlot(slot) {
|
|
7337
7003
|
if (slot instanceof ParamRef) return slot.expr;
|
|
7338
7004
|
const ref = slot?.__batchArray?.ref;
|
|
@@ -7507,20 +7173,13 @@ var RelationContext = class {
|
|
|
7507
7173
|
ops;
|
|
7508
7174
|
db;
|
|
7509
7175
|
hydrate;
|
|
7510
|
-
/** CROSS-DB (V0 R1): name → driver registry; a tagged relation routes to `connections[tag]`. */
|
|
7511
|
-
connections;
|
|
7512
7176
|
/** Per-relation resolved batch (name → grouping), memoized: one batch query per relation. */
|
|
7513
7177
|
resolved = /* @__PURE__ */ new Map();
|
|
7514
|
-
constructor(siblings, ops, db, hydrate
|
|
7178
|
+
constructor(siblings, ops, db, hydrate) {
|
|
7515
7179
|
this.siblings = siblings;
|
|
7516
7180
|
this.ops = ops;
|
|
7517
7181
|
this.db = db;
|
|
7518
7182
|
this.hydrate = hydrate;
|
|
7519
|
-
this.connections = connections;
|
|
7520
|
-
}
|
|
7521
|
-
/** The driver a relation runs against: its tagged cross-DB connection, else the primary `db`. */
|
|
7522
|
-
driverFor(op) {
|
|
7523
|
-
return op.connection === void 0 ? this.db : driverForOp(op, this.connections);
|
|
7524
7183
|
}
|
|
7525
7184
|
/**
|
|
7526
7185
|
* Resolve `relationName` for `parent` (lazy access). The relation is batch-loaded across
|
|
@@ -7534,19 +7193,12 @@ var RelationContext = class {
|
|
|
7534
7193
|
}
|
|
7535
7194
|
let batch = this.resolved.get(relationName);
|
|
7536
7195
|
if (batch === void 0) {
|
|
7537
|
-
batch = runRelationOp(op, this.siblings, this.
|
|
7196
|
+
batch = runRelationOp(op, this.siblings, this.db).batch;
|
|
7538
7197
|
this.resolved.set(relationName, batch);
|
|
7539
7198
|
}
|
|
7540
7199
|
return applyHydrate(distributeToParent(op, parent, batch), this.hydrate);
|
|
7541
7200
|
}
|
|
7542
7201
|
};
|
|
7543
|
-
function driverForOp(op, connections) {
|
|
7544
|
-
const d = connections?.[op.connection];
|
|
7545
|
-
if (d === void 0) {
|
|
7546
|
-
throw new Error(`cross-DB relation '${op.name}': no driver registered for connection '${op.connection}' (pass it in ReadOptions.connections)`);
|
|
7547
|
-
}
|
|
7548
|
-
return d;
|
|
7549
|
-
}
|
|
7550
7202
|
function readRelationContext(o) {
|
|
7551
7203
|
const v = o[RELATION_CONTEXT];
|
|
7552
7204
|
return v instanceof RelationContext ? v : void 0;
|
|
@@ -7584,7 +7236,7 @@ function buildResultSet(rawRows, ops, db, options = {}) {
|
|
|
7584
7236
|
for (const k of Object.keys(raw)) o[k] = raw[k];
|
|
7585
7237
|
return o;
|
|
7586
7238
|
});
|
|
7587
|
-
const ctx = new RelationContext(rows, ops, db, hydrate
|
|
7239
|
+
const ctx = new RelationContext(rows, ops, db, hydrate);
|
|
7588
7240
|
for (const o of rows) {
|
|
7589
7241
|
Object.defineProperty(o, RELATION_CONTEXT, {
|
|
7590
7242
|
value: ctx,
|
|
@@ -7596,8 +7248,7 @@ function buildResultSet(rawRows, ops, db, options = {}) {
|
|
|
7596
7248
|
for (const name of Object.keys(selected)) {
|
|
7597
7249
|
const op = ops[name];
|
|
7598
7250
|
if (op === void 0) throw new Error(`declarative select: relation '${name}' is not declared on this model`);
|
|
7599
|
-
const
|
|
7600
|
-
const { batch } = runRelationOp(op, rows, relDb);
|
|
7251
|
+
const { batch } = runRelationOp(op, rows, db);
|
|
7601
7252
|
for (const o of rows) {
|
|
7602
7253
|
o[name] = applyHydrate(distributeToParent(op, o, batch), hydrate);
|
|
7603
7254
|
}
|
|
@@ -7616,7 +7267,6 @@ export {
|
|
|
7616
7267
|
DEFAULT_CONNECTION,
|
|
7617
7268
|
DEFAULT_UNCAST_SQL_TYPE,
|
|
7618
7269
|
Db,
|
|
7619
|
-
ENTITY_ROOT,
|
|
7620
7270
|
LEAF_TRANSPORT_SYMBOLS,
|
|
7621
7271
|
LITEDBMODEL_MAKESQL_CATALOG,
|
|
7622
7272
|
LimitExceededError,
|
|
@@ -7653,7 +7303,6 @@ export {
|
|
|
7653
7303
|
compileCompositeKeyLimited,
|
|
7654
7304
|
compileCompositeKeyStaticUnlimited,
|
|
7655
7305
|
compileCompositeKeyUnlimited,
|
|
7656
|
-
compileCompositeWriteBundle,
|
|
7657
7306
|
compileCreateBundle,
|
|
7658
7307
|
compileCreateManyBundle,
|
|
7659
7308
|
compileDeleteBundle,
|
|
@@ -7672,7 +7321,6 @@ export {
|
|
|
7672
7321
|
compileUpdateManyBundle,
|
|
7673
7322
|
compileUpdateSingle,
|
|
7674
7323
|
compileWhere,
|
|
7675
|
-
compileWriteBundle,
|
|
7676
7324
|
compileWriteNode,
|
|
7677
7325
|
composeMakeSQL,
|
|
7678
7326
|
configurePgDeboxTypeParsers,
|
|
@@ -7680,7 +7328,6 @@ export {
|
|
|
7680
7328
|
connectionForDriver,
|
|
7681
7329
|
contextForConnection,
|
|
7682
7330
|
contextForDriver,
|
|
7683
|
-
countingDriver,
|
|
7684
7331
|
createMiddleware,
|
|
7685
7332
|
currentPinnedAsyncConnection,
|
|
7686
7333
|
currentRegistry,
|
|
@@ -7688,20 +7335,15 @@ export {
|
|
|
7688
7335
|
dbCastIn,
|
|
7689
7336
|
deriveModelColumns,
|
|
7690
7337
|
deriveRelationDecls,
|
|
7691
|
-
deriveTransactionPlan,
|
|
7692
7338
|
dialectFor,
|
|
7693
7339
|
distributeToParent,
|
|
7694
|
-
edgeWrites,
|
|
7695
7340
|
emitBehaviorModule,
|
|
7696
|
-
entityWrites,
|
|
7697
7341
|
execute,
|
|
7698
7342
|
executeAsync,
|
|
7699
7343
|
executeSQL,
|
|
7700
7344
|
executeSQLAsync,
|
|
7701
7345
|
executeSafe,
|
|
7702
|
-
executeTransaction,
|
|
7703
7346
|
executeTransactionAsync,
|
|
7704
|
-
executeTransactionBundle,
|
|
7705
7347
|
failClosedMaterializeResolverFromColumnMap,
|
|
7706
7348
|
formatterFor,
|
|
7707
7349
|
getLimitConfig,
|
|
@@ -7716,7 +7358,6 @@ export {
|
|
|
7716
7358
|
leaf2 as leaf,
|
|
7717
7359
|
leafHandlers,
|
|
7718
7360
|
leafHandlersAsync,
|
|
7719
|
-
lifecycleFor,
|
|
7720
7361
|
makeSqlCatalogEntry,
|
|
7721
7362
|
makeSqlHandler,
|
|
7722
7363
|
makeSqlHandlerSync,
|
|
@@ -7731,7 +7372,6 @@ export {
|
|
|
7731
7372
|
mysqlPoolFactory,
|
|
7732
7373
|
noCastFormatter,
|
|
7733
7374
|
opt,
|
|
7734
|
-
parseEffectPath,
|
|
7735
7375
|
parseSchemaColumnTypes,
|
|
7736
7376
|
pgCastFormatter,
|
|
7737
7377
|
pgConnectionPool,
|
|
@@ -7772,7 +7412,6 @@ export {
|
|
|
7772
7412
|
setLimitConfig,
|
|
7773
7413
|
singlePoolPair,
|
|
7774
7414
|
sleep,
|
|
7775
|
-
spliceWhere,
|
|
7776
7415
|
sqlTypeToBcScalar,
|
|
7777
7416
|
sqlTypeToMaterializeClass,
|
|
7778
7417
|
stripMysqlPkHint,
|