kitcn 0.32.1 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -0
- package/dist/aggregate/index.d.ts +2 -2
- package/dist/auth/index.js +1 -1
- package/dist/{capabilities-Ctcw2VRq.d.ts → capabilities-Bxzoofl4.d.ts} +195 -9
- package/dist/orm/aggregate-index/index.d.ts +1 -1
- package/dist/orm/aggregate-index/index.js +226 -96
- package/dist/orm/index.d.ts +2 -2
- package/dist/orm/index.js +440 -113
- package/dist/orm/migrations/index.d.ts +2 -2
- package/dist/{query-context-D4z1CnDH.js → query-context-DQLv58LH.js} +34 -1
- package/dist/react/index.js +4 -3
- package/dist/{schema-Bh7AmJwY.js → schema-BF4P0ZjS.js} +322 -1
- package/dist/{where-clause-compiler-BHGHbfb9.d.ts → where-clause-compiler-DrV6lQg0.d.ts} +126 -126
- package/package.json +1 -1
- package/skills/kitcn/references/features/aggregates.md +13 -0
- package/skills/kitcn/references/features/orm.md +24 -4
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as MigrationStep, D as defineMigration, E as buildMigrationPlan, O as defineMigrationSet, S as MigrationStateMap, T as MigrationWriteMode, _ as MigrationManifestEntry, a as MAX_STATUS_RUN_LIMIT, b as MigrationRunStatus, c as MigrationRunChunkArgs, d as MigrationAppliedState, f as MigrationDefinition, g as MigrationDriftIssue, h as MigrationDocContext, k as detectMigrationDrift, l as MigrationStatusArgs, m as MigrationDoc, o as MigrationCancelArgs, p as MigrationDirection, s as MigrationRunArgs, u as createMigrationHandlers, v as MigrationMigrateOne, w as MigrationTableName, x as MigrationSet, y as MigrationPlan } from "../../capabilities-
|
|
2
|
-
import { Ct as migrationCapability, dt as MIGRATION_STORAGE_TABLE_NAMES, ft as injectMigrationStorageTables, lt as MIGRATION_RUN_TABLE, mt as migrationStorageTables, pt as migrationExtension, ut as MIGRATION_STATE_TABLE } from "../../where-clause-compiler-
|
|
1
|
+
import { C as MigrationStep, D as defineMigration, E as buildMigrationPlan, O as defineMigrationSet, S as MigrationStateMap, T as MigrationWriteMode, _ as MigrationManifestEntry, a as MAX_STATUS_RUN_LIMIT, b as MigrationRunStatus, c as MigrationRunChunkArgs, d as MigrationAppliedState, f as MigrationDefinition, g as MigrationDriftIssue, h as MigrationDocContext, k as detectMigrationDrift, l as MigrationStatusArgs, m as MigrationDoc, o as MigrationCancelArgs, p as MigrationDirection, s as MigrationRunArgs, u as createMigrationHandlers, v as MigrationMigrateOne, w as MigrationTableName, x as MigrationSet, y as MigrationPlan } from "../../capabilities-Bxzoofl4.js";
|
|
2
|
+
import { Ct as migrationCapability, dt as MIGRATION_STORAGE_TABLE_NAMES, ft as injectMigrationStorageTables, lt as MIGRATION_RUN_TABLE, mt as migrationStorageTables, pt as migrationExtension, ut as MIGRATION_STATE_TABLE } from "../../where-clause-compiler-DrV6lQg0.js";
|
|
3
3
|
export { MAX_STATUS_RUN_LIMIT, MIGRATION_RUN_TABLE, MIGRATION_STATE_TABLE, MIGRATION_STORAGE_TABLE_NAMES, type MigrationAppliedState, type MigrationCancelArgs, type MigrationDefinition, type MigrationDirection, type MigrationDoc, type MigrationDocContext, type MigrationDriftIssue, type MigrationManifestEntry, type MigrationMigrateOne, type MigrationPlan, type MigrationRunArgs, type MigrationRunChunkArgs, type MigrationRunStatus, type MigrationSet, type MigrationStateMap, type MigrationStatusArgs, type MigrationStep, type MigrationTableName, type MigrationWriteMode, buildMigrationPlan, createMigrationHandlers, defineMigration, defineMigrationSet, detectMigrationDrift, injectMigrationStorageTables, migrationCapability, migrationExtension, migrationStorageTables };
|
|
@@ -671,6 +671,21 @@ function mergedStream(streams, orderByIndexFields) {
|
|
|
671
671
|
return new MergedStream(streams, orderByIndexFields);
|
|
672
672
|
}
|
|
673
673
|
/**
|
|
674
|
+
* Read streams over disjoint index ranges one after another, as one stream.
|
|
675
|
+
*
|
|
676
|
+
* Produces the same sequence a `mergedStream` over the whole index key would,
|
|
677
|
+
* for sources that cannot interleave — but a source is only opened once the one
|
|
678
|
+
* before it runs dry, so the fan-out a merge pays up front, one live query per
|
|
679
|
+
* source, never happens. That is what makes a very wide union affordable.
|
|
680
|
+
*
|
|
681
|
+
* The caller owes disjointness and index order. Both are checked as rows come
|
|
682
|
+
* out: a key that goes backwards throws rather than silently returning an
|
|
683
|
+
* unordered stream.
|
|
684
|
+
*/
|
|
685
|
+
function concatStreams(streams) {
|
|
686
|
+
return new ConcatStreams(...streams);
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
674
689
|
* Marks sources that are already `OrderByStream`s, so `narrow` reuses them
|
|
675
690
|
* instead of adding a redundant delegation layer per call.
|
|
676
691
|
*
|
|
@@ -1134,6 +1149,24 @@ function* getOrderingIndexFields(stream) {
|
|
|
1134
1149
|
const streamIndexFields = stream.getIndexFields();
|
|
1135
1150
|
for (let i = 0; i <= streamEqualityIndexLength; i++) yield streamIndexFields.slice(i);
|
|
1136
1151
|
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Whether `mergedStream(..., orderByIndexFields)` would accept this stream.
|
|
1154
|
+
*
|
|
1155
|
+
* `MergedStream` wraps every source in an `OrderByStream`, which throws unless
|
|
1156
|
+
* the ordering fields are a suffix of the source's index fields past its
|
|
1157
|
+
* equality-pinned prefix. A caller that is *choosing* which index a source
|
|
1158
|
+
* walks has to know that before it commits to one, so it can fall back instead
|
|
1159
|
+
* of turning a working merge into a runtime error.
|
|
1160
|
+
*
|
|
1161
|
+
* Asks the stream itself rather than re-deriving the rule from an index name,
|
|
1162
|
+
* so the answer cannot drift from what `OrderByStream` actually enforces.
|
|
1163
|
+
*/
|
|
1164
|
+
function streamCanOrderBy(stream, orderByIndexFields) {
|
|
1165
|
+
const normalized = orderByIndexFields.slice();
|
|
1166
|
+
normalizeIndexFields(normalized);
|
|
1167
|
+
for (const orderingIndexFields of getOrderingIndexFields(stream)) if (equalIndexFields(orderingIndexFields, normalized)) return true;
|
|
1168
|
+
return false;
|
|
1169
|
+
}
|
|
1137
1170
|
var OrderByStream = class OrderByStream extends QueryStream {
|
|
1138
1171
|
#staticFilter;
|
|
1139
1172
|
#stream;
|
|
@@ -1351,4 +1384,4 @@ async function getByIdWithOrmQueryFallback(ctx, tableName, id) {
|
|
|
1351
1384
|
}
|
|
1352
1385
|
|
|
1353
1386
|
//#endregion
|
|
1354
|
-
export {
|
|
1387
|
+
export { getIndexFields as a, stream as c, isUnsetToken as d, unsetToken as f, concatStreams as i, streamCanOrderBy as l, EmptyStream as n, indexKeyWithinBounds as o, QueryStream as r, mergedStream as s, getByIdWithOrmQueryFallback as t, streamIndexRange as u };
|
package/dist/react/index.js
CHANGED
|
@@ -1479,11 +1479,12 @@ function createCRPCContext(options) {
|
|
|
1479
1479
|
const tokenReady = token === null || decodeJwtExp(token) !== null;
|
|
1480
1480
|
const identity = resolveAuthIdentity(token);
|
|
1481
1481
|
previousAuthRef.current = {
|
|
1482
|
-
|
|
1483
|
-
|
|
1482
|
+
identity,
|
|
1483
|
+
isAuthenticated
|
|
1484
1484
|
};
|
|
1485
1485
|
if (!previous) return;
|
|
1486
|
-
|
|
1486
|
+
const tokenRejected = previous.isAuthenticated && !isAuthenticated;
|
|
1487
|
+
if (tokenReady && previous.identity !== identity || tokenRejected) convexQueryClient.resetAuthQueries();
|
|
1487
1488
|
}, [
|
|
1488
1489
|
convexQueryClient,
|
|
1489
1490
|
isAuthenticated,
|
|
@@ -1313,6 +1313,327 @@ const ensureAggregateAllowedForRls = (tableConfig, rlsMode, methodName) => {
|
|
|
1313
1313
|
assertAggregateAllowedForRls(tableConfig, rlsMode, AGGREGATE_ERROR.RLS_UNSUPPORTED, methodName);
|
|
1314
1314
|
};
|
|
1315
1315
|
|
|
1316
|
+
//#endregion
|
|
1317
|
+
//#region src/orm/transaction-cache.ts
|
|
1318
|
+
/**
|
|
1319
|
+
* Per-transaction memo storage for the ORM.
|
|
1320
|
+
*
|
|
1321
|
+
* The ORM already has isolate-, execution-, statement- and row-scoped memos.
|
|
1322
|
+
* A hook needs transaction lifetime: the write barrier is built inside
|
|
1323
|
+
* `createOrmDbLifecycle`, which `createOrm` runs at module scope,
|
|
1324
|
+
* so a flag in that closure lives as long as the isolate and would leak an
|
|
1325
|
+
* answer from one transaction into the next.
|
|
1326
|
+
*
|
|
1327
|
+
* Deliberately dependency-free, for the same reason as `write-fanout`:
|
|
1328
|
+
* `aggregate-index/runtime` is contractually unreachable from `orm/index`
|
|
1329
|
+
* (`import-graph.test.ts`), so importing `lifecycle` here to read one symbol
|
|
1330
|
+
* would drag the trigger runtime into the aggregate entry's bundle.
|
|
1331
|
+
* `Symbol.for` is registry-based, so re-declaring the key resolves to the same
|
|
1332
|
+
* symbol `lifecycle` installs.
|
|
1333
|
+
*/
|
|
1334
|
+
const ORMLIFECYCLE_INNER_DB = Symbol.for("kitcn:OrmLifecycleInnerDB");
|
|
1335
|
+
const ORM_TRANSACTION_ANCHOR = Symbol.for("kitcn:OrmTransactionAnchor");
|
|
1336
|
+
/**
|
|
1337
|
+
* Pins `anchor`'s transaction onto `target`, for a db built from a writer that
|
|
1338
|
+
* cannot be resolved back to it.
|
|
1339
|
+
*
|
|
1340
|
+
* `withoutTriggers` re-roots the ORM on the raw `ctx.db`, which is the one
|
|
1341
|
+
* object in the chain that carries no inner-db symbol — so a db derived from it
|
|
1342
|
+
* would otherwise stand for a transaction of its own and miss every memo and
|
|
1343
|
+
* queued write filed under the real one.
|
|
1344
|
+
*/
|
|
1345
|
+
const markOrmTransactionAnchor = (target, db) => {
|
|
1346
|
+
const anchor = resolveOrmTransactionAnchor(db);
|
|
1347
|
+
if (!anchor || Object.hasOwn(target, ORM_TRANSACTION_ANCHOR)) return target;
|
|
1348
|
+
Object.defineProperty(target, ORM_TRANSACTION_ANCHOR, {
|
|
1349
|
+
configurable: false,
|
|
1350
|
+
enumerable: false,
|
|
1351
|
+
value: anchor,
|
|
1352
|
+
writable: false
|
|
1353
|
+
});
|
|
1354
|
+
return target;
|
|
1355
|
+
};
|
|
1356
|
+
/**
|
|
1357
|
+
* The object whose identity stands in for "this transaction".
|
|
1358
|
+
*
|
|
1359
|
+
* Convex builds `ctx.db` fresh on every UDF invocation, so it can never be
|
|
1360
|
+
* shared by two transactions. `getOrmLifecycleInnerDb` cannot be used on its
|
|
1361
|
+
* own: the lifecycle refuses to wrap readers and returns a no-op wrapper for
|
|
1362
|
+
* schemas with no triggers and no aggregate indexes, so the inner-db symbol is
|
|
1363
|
+
* absent for every query and for most mutations.
|
|
1364
|
+
*
|
|
1365
|
+
* Following the symbol to a fixed point, rather than one hop, is what makes the
|
|
1366
|
+
* answer canonical: `orm.with(hookCtx)` wraps a writer that is already a hook
|
|
1367
|
+
* wrapper, so one hop lands on the intermediate wrapper instead of the raw
|
|
1368
|
+
* writer the outer scope filed its work under. A pinned anchor wins outright,
|
|
1369
|
+
* because it is the only way a db rooted on the raw writer can name the
|
|
1370
|
+
* transaction at all.
|
|
1371
|
+
*
|
|
1372
|
+
* A nested `ctx.runMutation` shares the transaction but gets its own writer
|
|
1373
|
+
* and JS context. It cannot see caller-local memos or queued writes, nor can it
|
|
1374
|
+
* invalidate caller snapshots. Callers must flush deferred writes and expire
|
|
1375
|
+
* row snapshots before handing control to arbitrary user code.
|
|
1376
|
+
*/
|
|
1377
|
+
const resolveOrmTransactionAnchor = (db) => {
|
|
1378
|
+
if (typeof db !== "object" || db === null) return;
|
|
1379
|
+
let current = db;
|
|
1380
|
+
const seen = new Set([current]);
|
|
1381
|
+
for (;;) {
|
|
1382
|
+
const pinned = current[ORM_TRANSACTION_ANCHOR];
|
|
1383
|
+
if (typeof pinned === "object" && pinned !== null) return pinned;
|
|
1384
|
+
const inner = current[ORMLIFECYCLE_INNER_DB];
|
|
1385
|
+
if (typeof inner !== "object" || inner === null || seen.has(inner)) return current;
|
|
1386
|
+
seen.add(inner);
|
|
1387
|
+
current = inner;
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
/**
|
|
1391
|
+
* One memo namespace with transaction lifetime.
|
|
1392
|
+
*
|
|
1393
|
+
* The store is a `WeakMap` keyed on the anchor rather than a slot on the db,
|
|
1394
|
+
* because `createDatabase` promises not to mutate the `ctx.db` it was handed.
|
|
1395
|
+
* Entries die with the transaction's db object.
|
|
1396
|
+
*
|
|
1397
|
+
* Callers own staleness: only memoize a fact that nothing inside the
|
|
1398
|
+
* transaction can invalidate.
|
|
1399
|
+
*/
|
|
1400
|
+
const createOrmTransactionMemo = () => {
|
|
1401
|
+
const byTransaction = /* @__PURE__ */ new WeakMap();
|
|
1402
|
+
return {
|
|
1403
|
+
get(db, key) {
|
|
1404
|
+
const anchor = resolveOrmTransactionAnchor(db);
|
|
1405
|
+
return anchor ? byTransaction.get(anchor)?.get(key) : void 0;
|
|
1406
|
+
},
|
|
1407
|
+
set(db, key, value) {
|
|
1408
|
+
const anchor = resolveOrmTransactionAnchor(db);
|
|
1409
|
+
if (!anchor) return;
|
|
1410
|
+
const existing = byTransaction.get(anchor);
|
|
1411
|
+
if (existing) {
|
|
1412
|
+
existing.set(key, value);
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
byTransaction.set(anchor, new Map([[key, value]]));
|
|
1416
|
+
}
|
|
1417
|
+
};
|
|
1418
|
+
};
|
|
1419
|
+
|
|
1420
|
+
//#endregion
|
|
1421
|
+
//#region src/orm/write-cache.ts
|
|
1422
|
+
const scopes = createOrmTransactionMemo();
|
|
1423
|
+
const activeScopes = /* @__PURE__ */ new Set();
|
|
1424
|
+
let suspended$1 = 0;
|
|
1425
|
+
/** Snapshots may survive ORM-owned work, never a return to arbitrary code. */
|
|
1426
|
+
const runInOrmWriteScope = async (db, fn) => {
|
|
1427
|
+
let scope = scopes.get(db, "write");
|
|
1428
|
+
if (!scope) {
|
|
1429
|
+
scope = {
|
|
1430
|
+
depth: 0,
|
|
1431
|
+
entries: /* @__PURE__ */ new Map()
|
|
1432
|
+
};
|
|
1433
|
+
scopes.set(db, "write", scope);
|
|
1434
|
+
}
|
|
1435
|
+
scope.entries.clear();
|
|
1436
|
+
scope.depth += 1;
|
|
1437
|
+
activeScopes.add(scope);
|
|
1438
|
+
try {
|
|
1439
|
+
return await fn();
|
|
1440
|
+
} finally {
|
|
1441
|
+
scope.entries.clear();
|
|
1442
|
+
scope.depth -= 1;
|
|
1443
|
+
if (scope.depth === 0) activeScopes.delete(scope);
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
/**
|
|
1447
|
+
* User code can call a nested UDF whose isolated JS context cannot invalidate
|
|
1448
|
+
* our snapshots. Suspend every active scope, including reentrant statements,
|
|
1449
|
+
* until the callback settles; never restore pre-callback entries.
|
|
1450
|
+
*/
|
|
1451
|
+
const withoutOrmWriteCache = async (fn) => {
|
|
1452
|
+
for (const scope of activeScopes) scope.entries.clear();
|
|
1453
|
+
suspended$1 += 1;
|
|
1454
|
+
try {
|
|
1455
|
+
return await fn();
|
|
1456
|
+
} finally {
|
|
1457
|
+
suspended$1 -= 1;
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
const createOrmWriteMemo = () => {
|
|
1461
|
+
const namespace = Symbol("orm-write-memo");
|
|
1462
|
+
return {
|
|
1463
|
+
get(db, key) {
|
|
1464
|
+
const scope = scopes.get(db, "write");
|
|
1465
|
+
return !suspended$1 && scope?.depth ? scope.entries.get(namespace)?.get(key) : void 0;
|
|
1466
|
+
},
|
|
1467
|
+
set(db, key, value) {
|
|
1468
|
+
const scope = scopes.get(db, "write");
|
|
1469
|
+
if (suspended$1 || !scope?.depth) return;
|
|
1470
|
+
let entries = scope.entries.get(namespace);
|
|
1471
|
+
if (!entries) {
|
|
1472
|
+
entries = /* @__PURE__ */ new Map();
|
|
1473
|
+
scope.entries.set(namespace, entries);
|
|
1474
|
+
}
|
|
1475
|
+
entries.set(key, value);
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
};
|
|
1479
|
+
|
|
1480
|
+
//#endregion
|
|
1481
|
+
//#region src/orm/write-batch.ts
|
|
1482
|
+
/**
|
|
1483
|
+
* Statement-scoped deferral for the ORM write path.
|
|
1484
|
+
*
|
|
1485
|
+
* The lifecycle fires its hooks once per written document, so a subsystem that
|
|
1486
|
+
* maintains derived storage — aggregate index buckets, today — reconciles one
|
|
1487
|
+
* document at a time and rewrites the same storage document once per row. The
|
|
1488
|
+
* fold that would collapse those writes exists; what it lacks is a boundary
|
|
1489
|
+
* wide enough to fold across. A mutation statement is that boundary: one
|
|
1490
|
+
* `update()` / `delete()` / `insert()` is one unit of work whose derived writes
|
|
1491
|
+
* nobody can observe half-applied unless they read them, and a reader can be
|
|
1492
|
+
* made to drain first.
|
|
1493
|
+
*
|
|
1494
|
+
* Depends only on the anchor and row-cache lifetime, for the same reason as
|
|
1495
|
+
* `write-fanout`: the mutation builders are reachable from `orm/index`, the
|
|
1496
|
+
* aggregate runtime contractually is not (`import-graph.test.ts`), and both
|
|
1497
|
+
* ends have to name this module.
|
|
1498
|
+
*
|
|
1499
|
+
* Three invariants make it safe:
|
|
1500
|
+
*
|
|
1501
|
+
* 1. Every queued write is applied by `flushOrmWriteBatch` and nowhere else.
|
|
1502
|
+
* Deltas are commutative but the writes that apply them are absolute
|
|
1503
|
+
* read-modify-writes, so two of them interleaving is a lost update. Callers
|
|
1504
|
+
* that have no batch open still enqueue and flush immediately rather than
|
|
1505
|
+
* writing around the queue, which makes the drain the single writer and
|
|
1506
|
+
* therefore the serialization point — the role `lifecycle`'s write lock
|
|
1507
|
+
* plays for the documents it wraps.
|
|
1508
|
+
* 2. Aggregate readers and user callbacks drain first. Callback execution
|
|
1509
|
+
* suspends deferral because nested UDFs cannot access the caller's queue.
|
|
1510
|
+
* 3. A flush callback must never itself drain. It runs inside the drain, and a
|
|
1511
|
+
* drain in progress is awaited rather than skipped, so re-entering it would
|
|
1512
|
+
* deadlock instead of returning stale rows. Keep the read barrier on the
|
|
1513
|
+
* query-path entry points, which no flush callback calls.
|
|
1514
|
+
*/
|
|
1515
|
+
const batches = /* @__PURE__ */ new WeakMap();
|
|
1516
|
+
const activeBatches = /* @__PURE__ */ new Set();
|
|
1517
|
+
let suspended = 0;
|
|
1518
|
+
const getState = (db) => {
|
|
1519
|
+
const anchor = resolveOrmTransactionAnchor(db);
|
|
1520
|
+
if (!anchor) return;
|
|
1521
|
+
const existing = batches.get(anchor);
|
|
1522
|
+
if (existing) return existing;
|
|
1523
|
+
const created = {
|
|
1524
|
+
depth: 0,
|
|
1525
|
+
flushers: /* @__PURE__ */ new Set(),
|
|
1526
|
+
draining: null
|
|
1527
|
+
};
|
|
1528
|
+
batches.set(anchor, created);
|
|
1529
|
+
return created;
|
|
1530
|
+
};
|
|
1531
|
+
/**
|
|
1532
|
+
* Runs every queued flush callback until none is left.
|
|
1533
|
+
*
|
|
1534
|
+
* A callback is removed before it runs, so work enqueued while the drain is in
|
|
1535
|
+
* flight is picked up by this same loop instead of being dropped or applied
|
|
1536
|
+
* twice. `draining` is published before the first callback and cleared in the
|
|
1537
|
+
* same synchronous step as the emptiness check that ends the loop, so a
|
|
1538
|
+
* caller either sees a drain it can wait for or starts its own — never a
|
|
1539
|
+
* window where its work is queued against a drain that has already left.
|
|
1540
|
+
*/
|
|
1541
|
+
const drain = (state) => {
|
|
1542
|
+
if (state.draining) return state.draining;
|
|
1543
|
+
let settled = false;
|
|
1544
|
+
const running = (async () => {
|
|
1545
|
+
try {
|
|
1546
|
+
for (;;) {
|
|
1547
|
+
const next = state.flushers.values().next();
|
|
1548
|
+
if (next.done) return;
|
|
1549
|
+
state.flushers.delete(next.value);
|
|
1550
|
+
await next.value();
|
|
1551
|
+
}
|
|
1552
|
+
} finally {
|
|
1553
|
+
state.draining = null;
|
|
1554
|
+
settled = true;
|
|
1555
|
+
}
|
|
1556
|
+
})();
|
|
1557
|
+
if (!settled) state.draining = running;
|
|
1558
|
+
return running;
|
|
1559
|
+
};
|
|
1560
|
+
/**
|
|
1561
|
+
* Opens a write batch for the duration of `fn`.
|
|
1562
|
+
*
|
|
1563
|
+
* Nesting is reference-counted: a cascade that runs another statement inside
|
|
1564
|
+
* this one folds into the same batch rather than flushing the outer statement's
|
|
1565
|
+
* pending work early. The flush runs whether or not `fn` threw, so rows that
|
|
1566
|
+
* landed before a mid-statement throw still get their derived writes.
|
|
1567
|
+
*
|
|
1568
|
+
* When both throw, `fn`'s error wins and the flush failure rides along as its
|
|
1569
|
+
* `cause`. A plain `finally { await drain() }` would report the consequence and
|
|
1570
|
+
* swallow the reason — the statement's own failure is what a caller can act on.
|
|
1571
|
+
*/
|
|
1572
|
+
const runInOrmWriteBatch = async (db, fn) => {
|
|
1573
|
+
const state = getState(db);
|
|
1574
|
+
if (!state) return await fn();
|
|
1575
|
+
state.depth += 1;
|
|
1576
|
+
activeBatches.add(state);
|
|
1577
|
+
let failure;
|
|
1578
|
+
let result;
|
|
1579
|
+
try {
|
|
1580
|
+
result = await fn();
|
|
1581
|
+
} catch (error) {
|
|
1582
|
+
failure = { error };
|
|
1583
|
+
}
|
|
1584
|
+
state.depth -= 1;
|
|
1585
|
+
if (state.depth > 0) {
|
|
1586
|
+
if (failure) throw failure.error;
|
|
1587
|
+
return result;
|
|
1588
|
+
}
|
|
1589
|
+
try {
|
|
1590
|
+
await drain(state);
|
|
1591
|
+
} catch (flushError) {
|
|
1592
|
+
if (!failure) throw flushError;
|
|
1593
|
+
if (failure.error instanceof Error && failure.error.cause === void 0) failure.error.cause = flushError;
|
|
1594
|
+
} finally {
|
|
1595
|
+
if (state.depth === 0) activeBatches.delete(state);
|
|
1596
|
+
}
|
|
1597
|
+
if (failure) throw failure.error;
|
|
1598
|
+
return result;
|
|
1599
|
+
};
|
|
1600
|
+
/**
|
|
1601
|
+
* Queues `flush` for the next drain.
|
|
1602
|
+
*
|
|
1603
|
+
* Returns `false` only when `db` has no resolvable transaction, which is the
|
|
1604
|
+
* caller's signal that this module cannot help and the work has to be done
|
|
1605
|
+
* inline.
|
|
1606
|
+
*/
|
|
1607
|
+
const enqueueOrmWriteBatch = (db, flush) => {
|
|
1608
|
+
const state = getState(db);
|
|
1609
|
+
if (!state) return false;
|
|
1610
|
+
state.flushers.add(flush);
|
|
1611
|
+
return true;
|
|
1612
|
+
};
|
|
1613
|
+
/** True while a statement scope is holding the queue back. */
|
|
1614
|
+
const isOrmWriteBatchOpen = (db) => suspended === 0 && (getState(db)?.depth ?? 0) > 0;
|
|
1615
|
+
/** Nested UDFs cannot read or drain queues in the caller's JS context. */
|
|
1616
|
+
const runInOrmUserCallback = async (fn) => await withoutOrmWriteCache(async () => {
|
|
1617
|
+
suspended += 1;
|
|
1618
|
+
try {
|
|
1619
|
+
for (const state of activeBatches) await drain(state);
|
|
1620
|
+
return await fn();
|
|
1621
|
+
} finally {
|
|
1622
|
+
suspended -= 1;
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
/**
|
|
1626
|
+
* Applies everything queued. Both the read barrier and the only writer.
|
|
1627
|
+
*
|
|
1628
|
+
* Callers must be outside any flush callback; see this module's header.
|
|
1629
|
+
*/
|
|
1630
|
+
const flushOrmWriteBatch = async (db) => {
|
|
1631
|
+
const anchor = resolveOrmTransactionAnchor(db);
|
|
1632
|
+
const state = anchor ? batches.get(anchor) : void 0;
|
|
1633
|
+
if (!state || !state.draining && state.flushers.size === 0) return;
|
|
1634
|
+
await drain(state);
|
|
1635
|
+
};
|
|
1636
|
+
|
|
1316
1637
|
//#endregion
|
|
1317
1638
|
//#region src/orm/aggregate-index/definitions.ts
|
|
1318
1639
|
const getAggregateIndexDefinitions = (tableConfig) => {
|
|
@@ -1426,4 +1747,4 @@ function aggregateExtension() {
|
|
|
1426
1747
|
}
|
|
1427
1748
|
|
|
1428
1749
|
//#endregion
|
|
1429
|
-
export {
|
|
1750
|
+
export { getOrmContext as $, canUsePrimaryIdLookupCursor as A, enforceUniqueIndexes as B, createError as C, isConvexEnforceableFilter as Ct, applyIncomingForeignKeyActionsOnDelete as D, applyDefaults as E, deserializeFilterExpression as F, evaluateFilter as G, ensureNonNullValues as H, encodeUndefinedDeep as I, getColumnName as J, extractPrimaryIdLookup as K, enforceCheckConstraints as L, collectPrimaryIdLookupRows as M, createForeignKeyProbeMemo as N, applyIncomingForeignKeyActionsOnUpdate as O, decodeUndefinedDeep as P, getMutationExecutionMode as Q, enforceForeignKeys as R, createCountError as S, convexAnd as St, ensureCountAllowedForRls as T, ensureNullableColumns as U, ensureDefaultColumns as V, evaluateCheckConstraintTriState as W, getMutationAsyncDelayMs as X, getForeignKeys as Y, getMutationCollectionLimits as Z, createOrmTransactionMemo as _, toConvexFilter as _t, AGGREGATE_STATE_TABLE as a, hydrateDateFieldsForRead as at, COUNT_ERROR as b, markLifecycleHookedTables as bt, getAggregateIndexDefinitions as c, patchReferencingRows as ct, flushOrmWriteBatch as d, selectReturningRowWithHydration as dt, getTableColumns as et, isOrmWriteBatchOpen as f, serializeFilterExpression as ft, runInOrmWriteScope as g, takeRowsWithinByteBudget as gt, createOrmWriteMemo as h, stripUnsetFields as ht, AGGREGATE_RANK_TREE_TABLE as i, hardDeleteRow as it, collectMutationRowsBounded as j, buildForeignKeyGraph as k, getRankIndexDefinitions as l, resolveOrmRuntimeDefaults as lt, runInOrmWriteBatch as m, splitReturningSelection as mt, AGGREGATE_EXTREMA_TABLE as n, getTableName as nt, aggregateExtension as o, normalizeDateFieldsForWrite as ot, runInOrmUserCallback as p, softDeleteRow as pt, getChecks as q, AGGREGATE_MEMBER_TABLE as r, getUniqueIndexes as rt, rankAggregateName as s, normalizeTemporalComparableValue as st, AGGREGATE_BUCKET_TABLE as t, getTableDeleteConfig as tt, enqueueOrmWriteBatch as u, returningSelectionReadsCreationTime as ut, markOrmTransactionAnchor as v, unsetFieldsOf as vt, ensureAggregateAllowedForRls as w, mapWithConcurrency as wt, createAggregateError as x, compileConvexFilter as xt, AGGREGATE_ERROR as y, hasLifecycleHooks as yt, enforcePolymorphicWrite as z };
|