@substrat-run/adapter-cloudflare 0.175.0 → 0.177.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.
@@ -1 +1 @@
1
- {"version":3,"file":"scope-do.d.ts","sourceRoot":"","sources":["../src/scope-do.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scope-do.d.ts","sourceRoot":"","sources":["../src/scope-do.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAuDL,KAAK,QAAQ,EAmBd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EA+DL,KAAK,mBAAmB,EASxB,KAAK,SAAS,EA2Bd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EA8HtB,MAAM,sBAAsB,CAAC;AAoD9B,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAwBL,kBAAkB,EAiFnB,MAAM,sBAAsB,CAAC;AA2B9B;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAoFD,oFAAoF;AACpF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AAgB5E;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AASnE,iDAAiD;AACjD,KAAK,gBAAgB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAA;CAAE,CAAC;AAEvH;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;IACtH,YAAY,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;IAClH,UAAU,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;IACvG,cAAc,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAClG,KAAK,EAAE,SAAS,CAAC;CAClB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AA00BjG;;;GAGG;AACH,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;AAmBtD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,CAOxD;AAED,gHAAgH;AAChH,OAAO,EAAE,kBAAkB,EAAE,CAAC;AA2B9B,wBAAgB,aAAa,CAC3B,OAAO,EAAE,kBAAkB,EAAE,EAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACzD;;;;GAIG;AACH,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,mBAAmB,CAAA;CAAO,GAClD,KAAK,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,UAAU,KAAK,aAAa,CAAC,UAAU,CAAC,CAyjR7E"}
package/dist/scope-do.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { REWIND_REFUSED } from './rewind-refusal.js';
2
2
  import { SYSTEM_DOOR_MOVED } from './system-door.js';
3
3
  import { DurableObject } from 'cloudflare:workers';
4
- import { ATTACHMENT_ADDED, ATTACHMENT_REMOVED, CAPTURE_REFUSALS, attachmentRecord, domainEvent, domainEventInput, eventId, instant, objectRef, entityObjectRef, assertModuleEmittableType, assertKernelAuthoredType, toWireFailure, grantRefFromProof, principalId, principalBecomeCapabilityInput, scopeId as scopeIdOf, tenantId as tenantIdOf, platformRequestInput, platformRequestId, MAX_PENDING_PLATFORM_REQUESTS, MAX_PENDING_SWEEP_RUNS, SWEEP_RUNS_KIND, SCOPE_DUMP_PAGE_MAX_BYTES, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, listLimitOf, pageOf, countedPageOf, requestFingerprint, substratError, errorCodeOf, assertReplayableDump, REDRAIN_BATCH, } from '@substrat-run/contracts';
5
- import { ulid, DO_SQL_LIMITS, unknownRoleError, createUlid, assertAllowed, ConnectionSealingKeyUnavailableError, noSealingKeyMessage, sealTo, assertReadOnlyQuery, entitlementDenial, platformRequestHistoryQuery, platformRequestOf, PLATFORM_REQUEST_COLUMNS, PLATFORM_REQUEST_REDACTION_SQL, platformRequestRedactionParams, platformRequestRedactionQuery, intentPayloadCarriesSubject, redactSubjectJobRuns, eraseSubjectFromModules, moduleErasurePlan, redactSubjectScopeText, assertRowLimit, assertRowOffset, JOB_RUN_PATCH_SQL, JOB_RUN_CLAIM_SQL, JOB_RUN_RENEW_SQL, JOB_RUN_BEGIN_SQL, JOB_RUN_MISS_SQL, JOB_RUN_MISS_SETTLE_SQL, admissionMissOutcome, JOB_LEASE_EXPIRED_NOTE, JOB_STEP_RECORD_SQL, DELIVERY_ERROR_REDACTION_SQL, REDACTED_DELIVERY_NOTE, seatScopeTuple, delegatedRevokeSql, writeExplicitTupleIn, grantEntityShapeIn, topUpEntityGrantShapes, applyScopeRoleChange, changeScopeRole, revokeScopeRoles, scopeRoleHolders, assertModuleDeclared, noteDeclaredKeys, noteReservedKeys, SCOPE_SEATED_ROLES_SQL, inUnitRoleRefusal, loadedRoleSetRefusedRole, reservedRoleSetRefusedRole, reservedGrantRefusal, reservedRoleRefusal, reservedShapeRefusal, effectiveRoleGrantQuery, switchRecordedOff, switchSystemSchedules, peerGrantsStatus, systemGrantsStatus, systemScheduleState, systemSwitchedOff, denialListQuery, refusalListQuery, mapRefusalRow, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, PermissionDenied, assertImpersonationWrites, assertModuleEnqueueableKind, impersonationStampOf, createAtomic, NotSearchable, isSearchIndexTable, searchIndexPlans, NotListable, listIndexPlans, moduleMigrations, MIGRATION_DIGEST_FENCE_DDL, MIGRATION_DIGEST_MARK_LEGACY, assertJournalDumpCoherent, assertMigrationSql, migrationDivergence, migrationFailedError, migrationSetDivergence, migrationSteps, planMigrations, listQuery, cursorOf, searchLimit, searchMatchExpression, searchQuery, ATTACHMENT_TEXT_DDL, ATTACHMENT_BYTES_SQL, ATTACHMENT_CONDEMNED_DDL, ATTACHMENT_SWEEP_LICENCE_DDL, ATTACHMENT_SWEEP_LICENCE_TABLE, WIRE_NONCE_DDL, WIRE_NONCE_FULL, claimWireNonceIn, isWireNonceTable, isWireNonceWrite, clearOrphanSweepLicence, condemnLicensedOrphanAttachments, consumeOrphanSweepLicence, licensedOrphanIds, recordOrphanSweepLicence, refuseUnlicensedOrphanSweep, refuseCondemnedAttachment, refuseCondemnedInDump, rowlessAttachmentIds, attachmentRecordOfRow, enqueueAttachmentText, reconcileAttachmentText, queueAttachmentTextBackfill, startAttachmentTextBackfill, recordAttachmentText, searchAttachments, IDEMPOTENCY_DDL, REFUSALS_TABLE_DDL, REFUSALS_INDEX, refusalInsert, refusalOf, markGuardRefusal, REFUSALS_REBUILD, refusalsAdmitGuards, assertIdempotencyKey, assertPermissionKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, entityVersionQuery, entityVersionOf, assertIfMatch, OUTBOX_ENTITY_INDEX, SCHEDULE_STATE_DDL, SCHEDULE_STATE_REBUILD, scheduleStateHasKind, VERTICAL_EVENTS_DDL, EXPORT_HOPS_SQL, emptyImportResult, IMPORT_CURSORS_SQL, IMPORT_CURSOR_OF_SQL, OUTBOX_MARK_SQL, exportedSinceQuery, emittedSinceQuery, emittedReportOf, EMITTED_REPORT_CAP, noteTransitionInto, withTransitions, IMPORT_CURSOR_ADVANCE_SQL, IMPORT_RECORD_SQL, moveImportCursor, CrossVerticalRegistry, exportReadPlan, exportReadQuery, planExportBatch, withheldNote, JOB_RUN_DDL, JOB_RUN_DUE_AT, jobRunListLimit, SYSTEM_DOOR_WAIT, ancestorsWithin, } from '@substrat-run/kernel';
4
+ import { ATTACHMENT_ADDED, ATTACHMENT_REMOVED, CAPTURE_REFUSALS, attachmentRecord, domainEvent, domainEventInput, eventId, instant, objectRef, entityObjectRef, assertModuleEmittableType, assertKernelAuthoredType, toWireFailure, grantRefFromProof, principalId, principalBecomeCapabilityInput, scopeId as scopeIdOf, tenantId as tenantIdOf, platformRequestInput, platformRequestId, MAX_PENDING_PLATFORM_REQUESTS, MAX_PENDING_SWEEP_RUNS, SWEEP_RUNS_KIND, SCOPE_DUMP_PAGE_MAX_BYTES, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, listLimitOf, pageOf, countedPageOf, requestFingerprint, substratError, errorCodeOf, assertReplayableDump, DUMP_OBJECTS_QUERY, dumpObjectsOf, replayDumpObjects, dumpObjectsAudit, REDRAIN_BATCH, } from '@substrat-run/contracts';
5
+ import { ulid, DO_SQL_LIMITS, unknownRoleError, createUlid, assertAllowed, ConnectionSealingKeyUnavailableError, noSealingKeyMessage, sealTo, assertReadOnlyQuery, entitlementDenial, platformRequestHistoryQuery, platformRequestOf, PLATFORM_REQUEST_COLUMNS, PLATFORM_REQUEST_REDACTION_SQL, platformRequestRedactionParams, platformRequestRedactionQuery, intentPayloadCarriesSubject, redactSubjectJobRuns, eraseSubjectFromModules, moduleErasurePlan, redactSubjectScopeText, assertRowLimit, assertRowOffset, JOB_RUN_PATCH_SQL, JOB_RUN_CLAIM_SQL, JOB_RUN_RENEW_SQL, JOB_RUN_BEGIN_SQL, JOB_RUN_MISS_SQL, JOB_RUN_MISS_SETTLE_SQL, admissionMissOutcome, JOB_LEASE_EXPIRED_NOTE, JOB_STEP_RECORD_SQL, DELIVERY_ERROR_REDACTION_SQL, REDACTED_DELIVERY_NOTE, seatScopeTuple, delegatedRevokeSql, writeExplicitTupleIn, grantEntityShapeIn, topUpEntityGrantShapes, applyScopeRoleChange, changeScopeRole, revokeScopeRoles, scopeRoleHolders, assertModuleDeclared, noteDeclaredKeys, noteReservedKeys, SCOPE_SEATED_ROLES_SQL, inUnitRoleRefusal, loadedRoleSetRefusedRole, reservedRoleSetRefusedRole, reservedGrantRefusal, reservedRoleRefusal, reservedShapeRefusal, effectiveRoleGrantQuery, switchRecordedOff, switchSystemSchedules, peerGrantsStatus, systemGrantsStatus, systemScheduleState, systemSwitchedOff, OUTBOX_PRUNED_DDL, outboxSubscribersOf, pruneDrainedOutbox, denialListQuery, refusalListQuery, mapRefusalRow, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, PermissionDenied, assertImpersonationAdmitted, assertImpersonationWrites, assertModuleEnqueueableKind, impersonationStampOf, createAtomic, NotSearchable, isSearchIndexTable, searchIndexPlans, NotListable, listIndexPlans, moduleMigrations, REGISTERED_MIGRATIONS, registeredMigrationsOf, MIGRATION_DIGEST_FENCE_DDL, MIGRATION_DIGEST_MARK_LEGACY, assertJournalDumpCoherent, assertMigrationSql, migrationDivergence, migrationFailedError, migrationSetDivergence, migrationSteps, planMigrations, listQuery, cursorOf, searchLimit, searchMatchExpression, searchQuery, ATTACHMENT_TEXT_DDL, ATTACHMENT_BYTES_SQL, ATTACHMENT_CONDEMNED_DDL, ATTACHMENT_SWEEP_LICENCE_DDL, ATTACHMENT_SWEEP_LICENCE_TABLE, WIRE_NONCE_DDL, WIRE_NONCE_FULL, claimWireNonceIn, isWireNonceTable, isWireNonceWrite, clearOrphanSweepLicence, condemnLicensedOrphanAttachments, consumeOrphanSweepLicence, licensedOrphanIds, recordOrphanSweepLicence, refuseUnlicensedOrphanSweep, refuseCondemnedAttachment, refuseCondemnedInDump, rowlessAttachmentIds, attachmentRecordOfRow, enqueueAttachmentText, reconcileAttachmentText, queueAttachmentTextBackfill, startAttachmentTextBackfill, recordAttachmentText, searchAttachments, IDEMPOTENCY_DDL, REFUSALS_TABLE_DDL, REFUSALS_INDEX, refusalInsert, refusalOf, markGuardRefusal, REFUSALS_REBUILD, refusalsAdmitGuards, assertIdempotencyKey, assertPermissionKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, entityVersionQuery, entityVersionOf, assertIfMatch, OUTBOX_ENTITY_INDEX, SCHEDULE_STATE_DDL, SCHEDULE_STATE_REBUILD, scheduleStateHasKind, VERTICAL_EVENTS_DDL, EXPORT_HOPS_SQL, emptyImportResult, IMPORT_CURSORS_SQL, IMPORT_CURSOR_OF_SQL, OUTBOX_MARK_SQL, exportedSinceQuery, emittedSinceQuery, emittedReportOf, EMITTED_REPORT_CAP, noteTransitionInto, withTransitions, IMPORT_CURSOR_ADVANCE_SQL, IMPORT_RECORD_SQL, moveImportCursor, CrossVerticalRegistry, exportReadPlan, exportReadQuery, planExportBatch, withheldNote, JOB_RUN_DDL, JOB_RUN_DUE_AT, jobRunListLimit, SYSTEM_DOOR_WAIT, ancestorsWithin, } from '@substrat-run/kernel';
6
6
  import { isUpgradeRequest, readSubscription, LIVE_DECIDE_ATTEMPTS, LIVE_FANOUT_LIMIT, LIVE_PRINCIPAL_HEADER, LIVE_SCOPE_HEADER, LIVE_SUBSCRIBE_PATH, LIVE_TENANT_HEADER, LIVE_WITHIN_HEADER, LIVE_EXPIRES_HEADER, LIVE_CLOSE, LIVE_SOCKETS_PER_PRINCIPAL, decodeLiveWithin, liveInstant, liveRefusal, liveRefusalStatus, } from './live-reads.js';
7
7
  import { replyOf } from './do-reply.js';
8
8
  import { OperationQueue } from './serialization.js';
@@ -387,6 +387,7 @@ const KERNEL_DDL = `
387
387
  -- #1686: where a copied scope's data came from, and the event id its own events start above.
388
388
  -- Shared with the other adapter from @substrat-run/kernel; the column comments are there.
389
389
  ${COPY_ORIGIN_DDL}
390
+ ${OUTBOX_PRUNED_DDL}
390
391
  -- #119: the kernel's authorization for one archive/trash move, read by the derived update
391
392
  -- trigger. Shared with the other adapter from @substrat-run/kernel; the comments are there.
392
393
  ${ENTITY_STATE_MOVES_DDL}
@@ -442,16 +443,22 @@ const STAGE_IN_PROGRESS = 'a different staged load is in progress; retry after i
442
443
  const STAGE_MAX_AGE_MS = 6 * 3_600_000 + 3 * SCOPE_COPY_LEASE_MS;
443
444
  /**
444
445
  * A staged load's own tables (#2450): `_substrat_stage` (which load, and the next page it takes),
445
- * `_substrat_stage_tables` (each staged table's name, DDL and columns) and one
446
- * `_substrat_stage_rows_<n>` per staged table. They hold a carry's pages until the load commits
447
- * from them, so they are this store's, never its data: no dump carries them, and a load's drops
448
- * leave them to the commit that reads them.
446
+ * `_substrat_stage_tables` (each staged table's name, DDL and columns), `_substrat_stage_objects`
447
+ * (below) and one `_substrat_stage_rows_<n>` per staged table. They hold a carry's pages until the
448
+ * load commits from them, so they are this store's, never its data: no dump carries them, and a
449
+ * load's drops leave them to the commit that reads them.
449
450
  */
450
451
  const STAGE_GLOB = '_substrat_stage*';
451
452
  const STAGE_TABLES_DDL = [
452
453
  `CREATE TABLE _substrat_stage (stage_id TEXT NOT NULL, next_seq INTEGER NOT NULL)`,
453
454
  `CREATE TABLE _substrat_stage_tables (ord INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE, ddl TEXT NOT NULL, columns TEXT NOT NULL)`,
454
455
  ];
456
+ /**
457
+ * #2820: each staged table's own indexes and triggers, for a table whose pages carried them. Created
458
+ * by every page if missing, so a stage opened before it existed still takes pages, and commits with
459
+ * none: as a dump written before objects were carried.
460
+ */
461
+ const STAGE_OBJECTS_DDL = `CREATE TABLE IF NOT EXISTS _substrat_stage_objects (name TEXT PRIMARY KEY, objects TEXT NOT NULL)`;
455
462
  const stageRowsTable = (ord) => `_substrat_stage_rows_${ord}`;
456
463
  /** How many staged rows a commit reads at a time. */
457
464
  const STAGE_READ_BATCH = 500;
@@ -892,6 +899,14 @@ options = {}) {
892
899
  noteDeclaredKeys(declaredListed, m.manifest);
893
900
  }
894
901
  return class ScopeDO extends DurableObject {
902
+ /**
903
+ * #2351: the migrations this class registers, read by the control plane, which evaluates the
904
+ * bundle in an isolate with no bindings and calls this before it stores the version. The same
905
+ * list `registerModule` keeps for each module (`moduleMigrations`).
906
+ */
907
+ static [REGISTERED_MIGRATIONS]() {
908
+ return registeredMigrationsOf(modules);
909
+ }
895
910
  sql;
896
911
  continuationOrder = 0;
897
912
  /** DO storage KV is private to the adapter; neither module SQL nor dumps can read it. */
@@ -944,6 +959,8 @@ options = {}) {
944
959
  purgeSchedules = new Map();
945
960
  /** #116: the operations that declared `idempotency: false` — refusals, not participants. */
946
961
  operationIdempotencyOptOut = new Set();
962
+ /** #2821: the operations that declared `impersonation: 'refused'` — no session may invoke them. */
963
+ operationImpersonationRefused = new Set();
947
964
  /** #2387: the operations held to a `ctx.check` — the pure adapter's set, from the same kernel read. */
948
965
  operationCheckHeld = new Set();
949
966
  modules = new Map();
@@ -1222,6 +1239,7 @@ options = {}) {
1222
1239
  // #116: same rule again for an opt-out — one on an unbound name reads as a
1223
1240
  // deliberate exclusion of an operation that is not there.
1224
1241
  const declaredOptOuts = registration.operations?.idempotencyOptOuts ?? [];
1242
+ const impersonationRefused = registration.operations?.impersonationRefused ?? [];
1225
1243
  const unboundOptOuts = declaredOptOuts.filter((name) => !ownOps.has(name));
1226
1244
  if (unboundOptOuts.length > 0) {
1227
1245
  throw new Error(`${manifest.id} declares idempotency: false for unbound operation(s): ` +
@@ -1244,6 +1262,9 @@ options = {}) {
1244
1262
  if (declaredOptOuts.includes(name) && this.operations.has(name)) {
1245
1263
  this.operationIdempotencyOptOut.add(name);
1246
1264
  }
1265
+ if (impersonationRefused.includes(name) && this.operations.has(name)) {
1266
+ this.operationImpersonationRefused.add(name);
1267
+ }
1247
1268
  const target = trashTargets.get(name);
1248
1269
  if (target && this.operations.has(name))
1249
1270
  this.operationTarget.set(name, target);
@@ -1807,6 +1828,20 @@ options = {}) {
1807
1828
  return drained;
1808
1829
  });
1809
1830
  }
1831
+ /**
1832
+ * The retention prune (#1334): the kernel's `pruneDrainedOutbox`, on the queue and in one
1833
+ * transaction, so the rows, their deliveries and the pruned-through floor go together.
1834
+ */
1835
+ async pruneDrainedEvents(drainedBefore, limit, executors, due = true) {
1836
+ return this.queue.enqueue(() => this.revision.transactionSync(() => pruneDrainedOutbox(this.switchSql(), {
1837
+ drainedBefore,
1838
+ limit,
1839
+ due,
1840
+ // Executors run on the coordinator, which names them; the consumers are this DO's own.
1841
+ subscribers: [...outboxSubscribersOf(this.modules.values(), []), ...executors],
1842
+ exportTypes: this.crossVertical.exportTypes(),
1843
+ })));
1844
+ }
1810
1845
  /**
1811
1846
  * Reopen rows stamped strictly before `drainedBefore`, so the drain ships them again
1812
1847
  * (#1334). The kernel contract says why the instant is required; this is the mechanism.
@@ -2266,6 +2301,8 @@ options = {}) {
2266
2301
  // else. Naming the code once here is what lets those patterns go.
2267
2302
  if (!handler)
2268
2303
  throw substratError('not_found', `unknown operation: ${operation}`);
2304
+ // #2821: before the parse, the guards and the handler — and under read-only too.
2305
+ assertImpersonationAdmitted(impersonation, operation, this.operationImpersonationRefused);
2269
2306
  // #304 entitlement gate, scope-local path: fail closed on the projected view exactly as
2270
2307
  // the coordinator fails closed against the CP. Only active once entitlements have been
2271
2308
  // projected (the `entitlements_enforced` marker) — before that the scope trusts upstream,
@@ -4364,11 +4401,12 @@ options = {}) {
4364
4401
  return await this.queue.enqueue(async () => {
4365
4402
  const liveSince = this.liveHighWaterMark();
4366
4403
  const now = instant.parse(new Date().toISOString());
4367
- let answer = { ok: true, unbound: [] };
4404
+ let answer = { ok: true, unbound: [], revokedLinks: [] };
4368
4405
  await this.revision.transaction(async () => {
4369
4406
  answer = await unbindEntityHolderBounded({
4370
4407
  sql: doSpineSql(this.sql),
4371
4408
  now,
4409
+ caller: by,
4372
4410
  allowed: allowedFor(this.checker, by, { tenantId, scopeId }),
4373
4411
  emit: (event) => kernelEmit(this.operationContext(by, tenantId, scopeId, undefined, undefined, undefined, undefined, undefined, ENTITY_UNBIND_OPERATION, undefined, [], now), event),
4374
4412
  }, entity);
@@ -5488,6 +5526,7 @@ options = {}) {
5488
5526
  * with the scope's identity after the K-3 check (host.ts admin.exportScope).
5489
5527
  */
5490
5528
  exportDump() {
5529
+ const objects = this.dumpObjects();
5491
5530
  return this.dumpTableDefs().map(({ name, sql }) => {
5492
5531
  // Raw positional rows, cells as-is (blobs kept as bytes, not nulled like a UI
5493
5532
  // read) so the dump reloads faithfully. The name is from the live schema.
@@ -5495,9 +5534,13 @@ options = {}) {
5495
5534
  const cursor = this.sql.exec(`SELECT * FROM "${name}" WHERE ${where}`, ...bindings);
5496
5535
  const columns = cursor.columnNames;
5497
5536
  const rows = Array.from(cursor.raw(), (row) => row);
5498
- return { name, ddl: sql, columns, rows };
5537
+ return { name, ddl: sql, columns, rows, ...objects(name) };
5499
5538
  });
5500
5539
  }
5540
+ /** #2820: each module table's own indexes and triggers, as a dump carries them beside the table. */
5541
+ dumpObjects() {
5542
+ return dumpObjectsOf(this.sql.exec(DUMP_OBJECTS_QUERY).toArray());
5543
+ }
5501
5544
  /**
5502
5545
  * The tables a dump carries, by name. The derived search index (#827) is excluded: its shadow
5503
5546
  * tables cannot be replayed — this host answers "object name reserved for internal use" — and
@@ -5537,6 +5580,7 @@ options = {}) {
5537
5580
  this.sql.exec(`INSERT INTO _substrat_meta (key, value) VALUES (?, ?)`, LOAD_STAMP_KEY, loadStamp);
5538
5581
  }
5539
5582
  const defs = this.dumpTableDefs();
5583
+ const objects = this.dumpObjects();
5540
5584
  if (capture !== undefined) {
5541
5585
  if (cursor === null)
5542
5586
  this.openCapture(capture, defs);
@@ -5584,7 +5628,7 @@ options = {}) {
5584
5628
  if (full || read.length < DUMP_READ_BATCH)
5585
5629
  break;
5586
5630
  }
5587
- tables.push({ name, ddl: sql, columns, rows, ...(capture !== undefined && byRowid ? { rowids } : {}) });
5631
+ tables.push({ name, ddl: sql, columns, rows, ...objects(name), ...(capture !== undefined && byRowid ? { rowids } : {}) });
5588
5632
  if (full) {
5589
5633
  next = encodeDumpCursor({ table: name, after: last ?? 0 });
5590
5634
  break;
@@ -5677,11 +5721,21 @@ options = {}) {
5677
5721
  return key.map((c) => ({ column: c.name, collation: c.coll }));
5678
5722
  });
5679
5723
  }
5680
- /** What a capture's pages read the schema as (#2450): the dumped tables and their explicit indexes. */
5724
+ /**
5725
+ * What a capture's pages read the schema as (#2450): the dumped tables, their explicit indexes and
5726
+ * (#2820) their triggers, which the pages carry as the tables' own objects. The capture's own
5727
+ * triggers are not part of it: they come and go with captures.
5728
+ */
5681
5729
  captureSchema(defs) {
5682
5730
  const names = new Set(defs.map((d) => d.name));
5683
- const indexes = this.sql.exec(`SELECT tbl_name, name, sql FROM sqlite_master WHERE type = 'index' AND sql IS NOT NULL ORDER BY name`).toArray().filter((i) => names.has(i.tbl_name));
5684
- return JSON.stringify({ tables: defs, indexes: indexes.map((i) => [i.name, i.sql]) });
5731
+ const objects = this.sql
5732
+ .exec(`SELECT type, tbl_name, name, sql FROM sqlite_master
5733
+ WHERE (type = 'index' OR (type = 'trigger' AND name NOT GLOB '${CAPTURE_GLOB}')) AND sql IS NOT NULL ORDER BY name`)
5734
+ .toArray().filter((o) => names.has(o.tbl_name));
5735
+ const of = (type) => objects.filter((o) => o.type === type).map((o) => [o.name, o.sql]);
5736
+ // Named only when there are some, so a capture opened before triggers were part of it still reads alike.
5737
+ const triggers = of('trigger');
5738
+ return JSON.stringify({ tables: defs, indexes: of('index'), ...(triggers.length ? { triggers } : {}) });
5685
5739
  }
5686
5740
  /**
5687
5741
  * The capture `copyId`, renewed, as its pages read the schema (#2450). Refused when it is gone.
@@ -6076,7 +6130,7 @@ options = {}) {
6076
6130
  });
6077
6131
  }
6078
6132
  async importDump(tables, destScopeId, opts = {}) {
6079
- return this.loadTables(tables.map((t) => ({ name: t.name, ddl: t.ddl, columns: t.columns, rows: () => t.rows })), destScopeId, opts);
6133
+ return this.loadTables(tables.map((t) => ({ name: t.name, ddl: t.ddl, columns: t.columns, objects: t.objects, rows: () => t.rows })), destScopeId, opts);
6080
6134
  }
6081
6135
  /**
6082
6136
  * The load `importDump` runs, over tables whose rows are read lazily (#2450): from the dump's
@@ -6084,7 +6138,7 @@ options = {}) {
6084
6138
  * in memory at once. `stageId` names the staged load the rows come from: checked inside the
6085
6139
  * load's transaction, and its tables dropped in it once the rows are in.
6086
6140
  */
6087
- async loadTables(tables, destScopeId, { switchOff, sourceScopeId, exact, loadStamp, expect, resolveKept, markCopy, provisionedFor, fence, roleRepair, onRolesProjected, projection, keepPermissionFlags, } = {}, stageId) {
6141
+ async loadTables(tables, destScopeId, { switchOff, sourceScopeId, exact, loadStamp, expect, resolveKept, markCopy, provisionedFor, fence, roleRepair, onRolesProjected, projection, keepPermissionFlags, onObjects, } = {}, stageId) {
6088
6142
  // The WHOLE drop-then-replay runs under deferred foreign keys, in one transaction.
6089
6143
  //
6090
6144
  // Two distinct FK hazards, and both need the deferral — the second is why it cannot
@@ -6118,7 +6172,8 @@ options = {}) {
6118
6172
  // amount of name checking reaches it. There is no prepare step here to compile
6119
6173
  // only the first statement, so the text itself has to be the one statement.
6120
6174
  // Pure input validation, so it runs before the first DROP: a refused dump touches nothing.
6121
- assertReplayableDump(replayable, { maxColumns: DO_SQL_LIMITS.columns });
6175
+ const objects = assertReplayableDump(replayable, { maxColumns: DO_SQL_LIMITS.columns });
6176
+ onObjects?.(objects);
6122
6177
  // #1869: `exact` vouches for a named source; without one it would silently fall back.
6123
6178
  if (exact && !sourceScopeId) {
6124
6179
  throw substratError('validation_failed', 'restore refused: `exact` needs the scope the dump came from');
@@ -6306,6 +6361,10 @@ options = {}) {
6306
6361
  // derived (#2090). Inside the replay's transaction: a dump whose journal owes a table a
6307
6362
  // state column it does not carry rolls the whole load back. A plan whose table this dump
6308
6363
  // did not carry is skipped: a restore must not invent one.
6364
+ // #2820: a module's own indexes and triggers went with the dropped tables, and its journal
6365
+ // says the migrations that made them ran. Put back from the dump, after the rows they judge
6366
+ // and before the kernel derives its own.
6367
+ replayDumpObjects(objects, (ddl) => this.sql.exec(blankSqlComments(ddl)));
6309
6368
  repairDerivedObjects(doSpineSql(this.sql), (ddl) => this.runScript(ddl), this.derivedPlans(), {
6310
6369
  after: 'the restored dump',
6311
6370
  absentTable: 'skip',
@@ -6405,7 +6464,7 @@ options = {}) {
6405
6464
  * (`changed`, 412) and "this store is a kept copy" (`kept`, 409) from a failure.
6406
6465
  */
6407
6466
  async importDumpChecked(tables, destScopeId, opts) {
6408
- return this.checkedLoad(() => this.importDump(tables, destScopeId, opts));
6467
+ return this.checkedLoad((onObjects) => this.importDump(tables, destScopeId, { ...opts, onObjects }));
6409
6468
  }
6410
6469
  /**
6411
6470
  * #2531: `importDumpChecked` with the role repair judged and projected in the load's own
@@ -6415,7 +6474,7 @@ options = {}) {
6415
6474
  * rather than loading with the repair silently dropped.
6416
6475
  */
6417
6476
  async importDumpJudged(tables, destScopeId, opts, roleRepair) {
6418
- return this.judgedLoad((onRolesProjected) => this.checkedLoad(() => this.importDump(tables, destScopeId, { ...opts, roleRepair, onRolesProjected })));
6477
+ return this.judgedLoad((onRolesProjected) => this.checkedLoad((onObjects) => this.importDump(tables, destScopeId, { ...opts, roleRepair, onRolesProjected, onObjects })));
6419
6478
  }
6420
6479
  /**
6421
6480
  * #2555: a control-plane-full restore or import. `importDump`, with this store's permission flags
@@ -6429,15 +6488,16 @@ options = {}) {
6429
6488
  importDumpProjected(tables, destScopeId, opts, projection) {
6430
6489
  return replyOf(async () => {
6431
6490
  try {
6432
- return {
6433
- switchedOff: await this.importDump(tables, destScopeId, {
6434
- switchOff: opts.switchOff,
6435
- sourceScopeId: opts.sourceScopeId,
6436
- exact: opts.exact,
6437
- keepPermissionFlags: true,
6438
- ...(projection ? { projection } : {}),
6439
- }),
6440
- };
6491
+ let plan;
6492
+ const switchedOff = await this.importDump(tables, destScopeId, {
6493
+ switchOff: opts.switchOff,
6494
+ sourceScopeId: opts.sourceScopeId,
6495
+ exact: opts.exact,
6496
+ keepPermissionFlags: true,
6497
+ ...(projection ? { projection } : {}),
6498
+ onObjects: (judged) => (plan = judged),
6499
+ });
6500
+ return { switchedOff, ...(plan ? { moduleObjects: dumpObjectsAudit(plan) } : {}) };
6441
6501
  }
6442
6502
  catch (e) {
6443
6503
  if (e instanceof Error && roleRefusals.has(e))
@@ -6508,19 +6568,31 @@ options = {}) {
6508
6568
  if (seq > head.nextSeq) {
6509
6569
  throw substratError('precondition_failed', `stage page ${seq} arrived before page ${head.nextSeq}`);
6510
6570
  }
6571
+ // A stage opened before objects were staged takes their table now: its earlier pages
6572
+ // staged none, so a later page that carries them reads as the table having changed.
6573
+ this.sql.exec(STAGE_OBJECTS_DDL);
6511
6574
  for (const t of tables) {
6512
6575
  const columns = JSON.stringify(t.columns);
6576
+ const objects = t.objects === undefined ? null : JSON.stringify(t.objects);
6513
6577
  let held = this.sql
6514
- .exec(`SELECT ord, ddl, columns FROM _substrat_stage_tables WHERE name = ?`, t.name)
6578
+ .exec(`SELECT s.ord, s.ddl, s.columns, o.objects FROM _substrat_stage_tables s
6579
+ LEFT JOIN _substrat_stage_objects o ON o.name = s.name WHERE s.name = ?`, t.name)
6515
6580
  .toArray()[0];
6516
6581
  if (!held) {
6582
+ // #2820: an export carries objects for every module table or for none. A page that
6583
+ // disagrees with the tables already staged was read across a change of version.
6584
+ if (!isSpineTable(t.name) && this.stagedModuleObjects().some((has) => has !== (objects !== null))) {
6585
+ throw substratError('precondition_failed', `table ${t.name} changed between dump pages; nothing more was staged`);
6586
+ }
6517
6587
  const ord = Number(this.sql.exec(`SELECT COUNT(*) AS n FROM _substrat_stage_tables`).toArray()[0].n);
6518
6588
  this.sql.exec(`INSERT INTO _substrat_stage_tables (ord, name, ddl, columns) VALUES (?, ?, ?, ?)`, ord, t.name, t.ddl, columns);
6589
+ if (objects !== null)
6590
+ this.sql.exec(`INSERT INTO _substrat_stage_objects (name, objects) VALUES (?, ?)`, t.name, objects);
6519
6591
  // Untyped columns: no affinity, so a cell is stored exactly as it came.
6520
6592
  this.sql.exec(`CREATE TABLE "${stageRowsTable(ord)}" (${t.columns.map((_, i) => `c${i}`).join(', ')})`);
6521
- held = { ord, ddl: t.ddl, columns };
6593
+ held = { ord, ddl: t.ddl, columns, objects };
6522
6594
  }
6523
- else if (held.ddl !== t.ddl || held.columns !== columns) {
6595
+ else if (held.ddl !== t.ddl || held.columns !== columns || held.objects !== objects) {
6524
6596
  throw substratError('precondition_failed', `table ${t.name} changed between dump pages; nothing more was staged`);
6525
6597
  }
6526
6598
  this.stageRows(held.ord, t);
@@ -6614,21 +6686,27 @@ options = {}) {
6614
6686
  */
6615
6687
  async commitStagedDumpChecked(stageId, destScopeId, opts) {
6616
6688
  let count = 0;
6617
- const out = await this.checkedLoad(() => {
6689
+ const out = await this.checkedLoad((onObjects) => {
6618
6690
  const head = this.stageHead();
6619
6691
  if (head?.stageId !== stageId)
6620
6692
  throw substratError('precondition_failed', STAGE_GONE);
6693
+ // Read only: a stage opened before objects were staged has no table for them, and loads none.
6694
+ const withObjects = this.hasTable('_substrat_stage_objects');
6621
6695
  const staged = this.sql
6622
- .exec(`SELECT ord, name, ddl, columns FROM _substrat_stage_tables ORDER BY name`)
6696
+ .exec(withObjects
6697
+ ? `SELECT s.ord, s.name, s.ddl, s.columns, o.objects FROM _substrat_stage_tables s
6698
+ LEFT JOIN _substrat_stage_objects o ON o.name = s.name ORDER BY s.name`
6699
+ : `SELECT ord, name, ddl, columns, NULL AS objects FROM _substrat_stage_tables ORDER BY name`)
6623
6700
  .toArray();
6624
6701
  const tables = staged.map((t) => ({
6625
6702
  name: t.name,
6626
6703
  ddl: t.ddl,
6627
6704
  columns: JSON.parse(t.columns),
6705
+ ...(t.objects === null ? {} : { objects: JSON.parse(t.objects) }),
6628
6706
  rows: () => this.stagedRows(t.ord),
6629
6707
  }));
6630
6708
  count = tables.length;
6631
- return this.loadTables(tables, destScopeId, opts, stageId);
6709
+ return this.loadTables(tables, destScopeId, { ...opts, onObjects }, stageId);
6632
6710
  });
6633
6711
  return out.refused === false ? { ...out, tables: count } : out;
6634
6712
  }
@@ -6664,6 +6742,14 @@ options = {}) {
6664
6742
  after = Number(batch[batch.length - 1][0]);
6665
6743
  }
6666
6744
  }
6745
+ /** #2820: for each module table staged so far, whether its pages carried its objects. */
6746
+ stagedModuleObjects() {
6747
+ return this.sql
6748
+ .exec(`SELECT s.name, o.name IS NOT NULL AS has FROM _substrat_stage_tables s LEFT JOIN _substrat_stage_objects o ON o.name = s.name`)
6749
+ .toArray()
6750
+ .filter((r) => !isSpineTable(r.name))
6751
+ .map((r) => Boolean(r.has));
6752
+ }
6667
6753
  /** The staged load this store holds (#2450), or null. */
6668
6754
  stageHead() {
6669
6755
  const present = this.sql.exec(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_substrat_stage'`).toArray();
@@ -6683,7 +6769,9 @@ options = {}) {
6683
6769
  /** A load's refusals answered as values (#1722): across the RPC a throw carries only its message. */
6684
6770
  async checkedLoad(run) {
6685
6771
  try {
6686
- return { refused: false, switchedOff: await run() };
6772
+ let plan;
6773
+ const switchedOff = await run((judged) => (plan = judged));
6774
+ return { refused: false, switchedOff, ...(plan ? { moduleObjects: dumpObjectsAudit(plan) } : {}) };
6687
6775
  }
6688
6776
  catch (e) {
6689
6777
  if (e instanceof Error && fenceRefusals.has(e))