@substrat-run/adapter-cloudflare 0.174.0 → 0.176.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":"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"}
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, 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, denialListQuery, refusalListQuery, mapRefusalRow, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, PermissionDenied, assertImpersonationAdmitted, 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';
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';
@@ -442,16 +442,22 @@ const STAGE_IN_PROGRESS = 'a different staged load is in progress; retry after i
442
442
  const STAGE_MAX_AGE_MS = 6 * 3_600_000 + 3 * SCOPE_COPY_LEASE_MS;
443
443
  /**
444
444
  * 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.
445
+ * `_substrat_stage_tables` (each staged table's name, DDL and columns), `_substrat_stage_objects`
446
+ * (below) and one `_substrat_stage_rows_<n>` per staged table. They hold a carry's pages until the
447
+ * load commits from them, so they are this store's, never its data: no dump carries them, and a
448
+ * load's drops leave them to the commit that reads them.
449
449
  */
450
450
  const STAGE_GLOB = '_substrat_stage*';
451
451
  const STAGE_TABLES_DDL = [
452
452
  `CREATE TABLE _substrat_stage (stage_id TEXT NOT NULL, next_seq INTEGER NOT NULL)`,
453
453
  `CREATE TABLE _substrat_stage_tables (ord INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE, ddl TEXT NOT NULL, columns TEXT NOT NULL)`,
454
454
  ];
455
+ /**
456
+ * #2820: each staged table's own indexes and triggers, for a table whose pages carried them. Created
457
+ * by every page if missing, so a stage opened before it existed still takes pages, and commits with
458
+ * none: as a dump written before objects were carried.
459
+ */
460
+ const STAGE_OBJECTS_DDL = `CREATE TABLE IF NOT EXISTS _substrat_stage_objects (name TEXT PRIMARY KEY, objects TEXT NOT NULL)`;
455
461
  const stageRowsTable = (ord) => `_substrat_stage_rows_${ord}`;
456
462
  /** How many staged rows a commit reads at a time. */
457
463
  const STAGE_READ_BATCH = 500;
@@ -944,6 +950,8 @@ options = {}) {
944
950
  purgeSchedules = new Map();
945
951
  /** #116: the operations that declared `idempotency: false` — refusals, not participants. */
946
952
  operationIdempotencyOptOut = new Set();
953
+ /** #2821: the operations that declared `impersonation: 'refused'` — no session may invoke them. */
954
+ operationImpersonationRefused = new Set();
947
955
  /** #2387: the operations held to a `ctx.check` — the pure adapter's set, from the same kernel read. */
948
956
  operationCheckHeld = new Set();
949
957
  modules = new Map();
@@ -1222,6 +1230,7 @@ options = {}) {
1222
1230
  // #116: same rule again for an opt-out — one on an unbound name reads as a
1223
1231
  // deliberate exclusion of an operation that is not there.
1224
1232
  const declaredOptOuts = registration.operations?.idempotencyOptOuts ?? [];
1233
+ const impersonationRefused = registration.operations?.impersonationRefused ?? [];
1225
1234
  const unboundOptOuts = declaredOptOuts.filter((name) => !ownOps.has(name));
1226
1235
  if (unboundOptOuts.length > 0) {
1227
1236
  throw new Error(`${manifest.id} declares idempotency: false for unbound operation(s): ` +
@@ -1244,6 +1253,9 @@ options = {}) {
1244
1253
  if (declaredOptOuts.includes(name) && this.operations.has(name)) {
1245
1254
  this.operationIdempotencyOptOut.add(name);
1246
1255
  }
1256
+ if (impersonationRefused.includes(name) && this.operations.has(name)) {
1257
+ this.operationImpersonationRefused.add(name);
1258
+ }
1247
1259
  const target = trashTargets.get(name);
1248
1260
  if (target && this.operations.has(name))
1249
1261
  this.operationTarget.set(name, target);
@@ -2266,6 +2278,8 @@ options = {}) {
2266
2278
  // else. Naming the code once here is what lets those patterns go.
2267
2279
  if (!handler)
2268
2280
  throw substratError('not_found', `unknown operation: ${operation}`);
2281
+ // #2821: before the parse, the guards and the handler — and under read-only too.
2282
+ assertImpersonationAdmitted(impersonation, operation, this.operationImpersonationRefused);
2269
2283
  // #304 entitlement gate, scope-local path: fail closed on the projected view exactly as
2270
2284
  // the coordinator fails closed against the CP. Only active once entitlements have been
2271
2285
  // projected (the `entitlements_enforced` marker) — before that the scope trusts upstream,
@@ -2409,7 +2423,12 @@ options = {}) {
2409
2423
  // transaction, reported only after commit — a rolled-back intent is no signal.
2410
2424
  // The envelope return (below) is the DO↔coordinator wire for it; both sides
2411
2425
  // live in this package and deploy as one script, so the shape never skews.
2412
- const signals = { platformRequests: 0 };
2426
+ // #2762: and the lifecycle moves `ctx.noteTransition` notes, when the caller asked for the
2427
+ // report they ride on. `ctx.atomic` restores both.
2428
+ const signals = {
2429
+ platformRequests: 0,
2430
+ ...(invokeOptions?.onEmitted !== undefined ? { transitions: [] } : {}),
2431
+ };
2413
2432
  /**
2414
2433
  * K-42: how a read-only session is made to BE read-only.
2415
2434
  *
@@ -2560,7 +2579,7 @@ options = {}) {
2560
2579
  let emitted;
2561
2580
  if (emittedMark !== null && !replayed) {
2562
2581
  const q = emittedSinceQuery(emittedMark, EMITTED_REPORT_CAP);
2563
- emitted = emittedReportOf(this.sql.exec(q.sql, ...q.params).toArray());
2582
+ emitted = withTransitions(emittedReportOf(this.sql.exec(q.sql, ...q.params).toArray()), signals.transitions);
2564
2583
  }
2565
2584
  if (!replayed)
2566
2585
  await this.settleCommitted(tenantId, scopeId, liveSince, this.invocationId);
@@ -4359,11 +4378,12 @@ options = {}) {
4359
4378
  return await this.queue.enqueue(async () => {
4360
4379
  const liveSince = this.liveHighWaterMark();
4361
4380
  const now = instant.parse(new Date().toISOString());
4362
- let answer = { ok: true, unbound: [] };
4381
+ let answer = { ok: true, unbound: [], revokedLinks: [] };
4363
4382
  await this.revision.transaction(async () => {
4364
4383
  answer = await unbindEntityHolderBounded({
4365
4384
  sql: doSpineSql(this.sql),
4366
4385
  now,
4386
+ caller: by,
4367
4387
  allowed: allowedFor(this.checker, by, { tenantId, scopeId }),
4368
4388
  emit: (event) => kernelEmit(this.operationContext(by, tenantId, scopeId, undefined, undefined, undefined, undefined, undefined, ENTITY_UNBIND_OPERATION, undefined, [], now), event),
4369
4389
  }, entity);
@@ -5483,6 +5503,7 @@ options = {}) {
5483
5503
  * with the scope's identity after the K-3 check (host.ts admin.exportScope).
5484
5504
  */
5485
5505
  exportDump() {
5506
+ const objects = this.dumpObjects();
5486
5507
  return this.dumpTableDefs().map(({ name, sql }) => {
5487
5508
  // Raw positional rows, cells as-is (blobs kept as bytes, not nulled like a UI
5488
5509
  // read) so the dump reloads faithfully. The name is from the live schema.
@@ -5490,9 +5511,13 @@ options = {}) {
5490
5511
  const cursor = this.sql.exec(`SELECT * FROM "${name}" WHERE ${where}`, ...bindings);
5491
5512
  const columns = cursor.columnNames;
5492
5513
  const rows = Array.from(cursor.raw(), (row) => row);
5493
- return { name, ddl: sql, columns, rows };
5514
+ return { name, ddl: sql, columns, rows, ...objects(name) };
5494
5515
  });
5495
5516
  }
5517
+ /** #2820: each module table's own indexes and triggers, as a dump carries them beside the table. */
5518
+ dumpObjects() {
5519
+ return dumpObjectsOf(this.sql.exec(DUMP_OBJECTS_QUERY).toArray());
5520
+ }
5496
5521
  /**
5497
5522
  * The tables a dump carries, by name. The derived search index (#827) is excluded: its shadow
5498
5523
  * tables cannot be replayed — this host answers "object name reserved for internal use" — and
@@ -5532,6 +5557,7 @@ options = {}) {
5532
5557
  this.sql.exec(`INSERT INTO _substrat_meta (key, value) VALUES (?, ?)`, LOAD_STAMP_KEY, loadStamp);
5533
5558
  }
5534
5559
  const defs = this.dumpTableDefs();
5560
+ const objects = this.dumpObjects();
5535
5561
  if (capture !== undefined) {
5536
5562
  if (cursor === null)
5537
5563
  this.openCapture(capture, defs);
@@ -5579,7 +5605,7 @@ options = {}) {
5579
5605
  if (full || read.length < DUMP_READ_BATCH)
5580
5606
  break;
5581
5607
  }
5582
- tables.push({ name, ddl: sql, columns, rows, ...(capture !== undefined && byRowid ? { rowids } : {}) });
5608
+ tables.push({ name, ddl: sql, columns, rows, ...objects(name), ...(capture !== undefined && byRowid ? { rowids } : {}) });
5583
5609
  if (full) {
5584
5610
  next = encodeDumpCursor({ table: name, after: last ?? 0 });
5585
5611
  break;
@@ -5672,11 +5698,21 @@ options = {}) {
5672
5698
  return key.map((c) => ({ column: c.name, collation: c.coll }));
5673
5699
  });
5674
5700
  }
5675
- /** What a capture's pages read the schema as (#2450): the dumped tables and their explicit indexes. */
5701
+ /**
5702
+ * What a capture's pages read the schema as (#2450): the dumped tables, their explicit indexes and
5703
+ * (#2820) their triggers, which the pages carry as the tables' own objects. The capture's own
5704
+ * triggers are not part of it: they come and go with captures.
5705
+ */
5676
5706
  captureSchema(defs) {
5677
5707
  const names = new Set(defs.map((d) => d.name));
5678
- 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));
5679
- return JSON.stringify({ tables: defs, indexes: indexes.map((i) => [i.name, i.sql]) });
5708
+ const objects = this.sql
5709
+ .exec(`SELECT type, tbl_name, name, sql FROM sqlite_master
5710
+ WHERE (type = 'index' OR (type = 'trigger' AND name NOT GLOB '${CAPTURE_GLOB}')) AND sql IS NOT NULL ORDER BY name`)
5711
+ .toArray().filter((o) => names.has(o.tbl_name));
5712
+ const of = (type) => objects.filter((o) => o.type === type).map((o) => [o.name, o.sql]);
5713
+ // Named only when there are some, so a capture opened before triggers were part of it still reads alike.
5714
+ const triggers = of('trigger');
5715
+ return JSON.stringify({ tables: defs, indexes: of('index'), ...(triggers.length ? { triggers } : {}) });
5680
5716
  }
5681
5717
  /**
5682
5718
  * The capture `copyId`, renewed, as its pages read the schema (#2450). Refused when it is gone.
@@ -6071,7 +6107,7 @@ options = {}) {
6071
6107
  });
6072
6108
  }
6073
6109
  async importDump(tables, destScopeId, opts = {}) {
6074
- return this.loadTables(tables.map((t) => ({ name: t.name, ddl: t.ddl, columns: t.columns, rows: () => t.rows })), destScopeId, opts);
6110
+ return this.loadTables(tables.map((t) => ({ name: t.name, ddl: t.ddl, columns: t.columns, objects: t.objects, rows: () => t.rows })), destScopeId, opts);
6075
6111
  }
6076
6112
  /**
6077
6113
  * The load `importDump` runs, over tables whose rows are read lazily (#2450): from the dump's
@@ -6079,7 +6115,7 @@ options = {}) {
6079
6115
  * in memory at once. `stageId` names the staged load the rows come from: checked inside the
6080
6116
  * load's transaction, and its tables dropped in it once the rows are in.
6081
6117
  */
6082
- async loadTables(tables, destScopeId, { switchOff, sourceScopeId, exact, loadStamp, expect, resolveKept, markCopy, provisionedFor, fence, roleRepair, onRolesProjected, projection, keepPermissionFlags, } = {}, stageId) {
6118
+ async loadTables(tables, destScopeId, { switchOff, sourceScopeId, exact, loadStamp, expect, resolveKept, markCopy, provisionedFor, fence, roleRepair, onRolesProjected, projection, keepPermissionFlags, onObjects, } = {}, stageId) {
6083
6119
  // The WHOLE drop-then-replay runs under deferred foreign keys, in one transaction.
6084
6120
  //
6085
6121
  // Two distinct FK hazards, and both need the deferral — the second is why it cannot
@@ -6113,7 +6149,8 @@ options = {}) {
6113
6149
  // amount of name checking reaches it. There is no prepare step here to compile
6114
6150
  // only the first statement, so the text itself has to be the one statement.
6115
6151
  // Pure input validation, so it runs before the first DROP: a refused dump touches nothing.
6116
- assertReplayableDump(replayable, { maxColumns: DO_SQL_LIMITS.columns });
6152
+ const objects = assertReplayableDump(replayable, { maxColumns: DO_SQL_LIMITS.columns });
6153
+ onObjects?.(objects);
6117
6154
  // #1869: `exact` vouches for a named source; without one it would silently fall back.
6118
6155
  if (exact && !sourceScopeId) {
6119
6156
  throw substratError('validation_failed', 'restore refused: `exact` needs the scope the dump came from');
@@ -6301,6 +6338,10 @@ options = {}) {
6301
6338
  // derived (#2090). Inside the replay's transaction: a dump whose journal owes a table a
6302
6339
  // state column it does not carry rolls the whole load back. A plan whose table this dump
6303
6340
  // did not carry is skipped: a restore must not invent one.
6341
+ // #2820: a module's own indexes and triggers went with the dropped tables, and its journal
6342
+ // says the migrations that made them ran. Put back from the dump, after the rows they judge
6343
+ // and before the kernel derives its own.
6344
+ replayDumpObjects(objects, (ddl) => this.sql.exec(blankSqlComments(ddl)));
6304
6345
  repairDerivedObjects(doSpineSql(this.sql), (ddl) => this.runScript(ddl), this.derivedPlans(), {
6305
6346
  after: 'the restored dump',
6306
6347
  absentTable: 'skip',
@@ -6400,7 +6441,7 @@ options = {}) {
6400
6441
  * (`changed`, 412) and "this store is a kept copy" (`kept`, 409) from a failure.
6401
6442
  */
6402
6443
  async importDumpChecked(tables, destScopeId, opts) {
6403
- return this.checkedLoad(() => this.importDump(tables, destScopeId, opts));
6444
+ return this.checkedLoad((onObjects) => this.importDump(tables, destScopeId, { ...opts, onObjects }));
6404
6445
  }
6405
6446
  /**
6406
6447
  * #2531: `importDumpChecked` with the role repair judged and projected in the load's own
@@ -6410,7 +6451,7 @@ options = {}) {
6410
6451
  * rather than loading with the repair silently dropped.
6411
6452
  */
6412
6453
  async importDumpJudged(tables, destScopeId, opts, roleRepair) {
6413
- return this.judgedLoad((onRolesProjected) => this.checkedLoad(() => this.importDump(tables, destScopeId, { ...opts, roleRepair, onRolesProjected })));
6454
+ return this.judgedLoad((onRolesProjected) => this.checkedLoad((onObjects) => this.importDump(tables, destScopeId, { ...opts, roleRepair, onRolesProjected, onObjects })));
6414
6455
  }
6415
6456
  /**
6416
6457
  * #2555: a control-plane-full restore or import. `importDump`, with this store's permission flags
@@ -6424,15 +6465,16 @@ options = {}) {
6424
6465
  importDumpProjected(tables, destScopeId, opts, projection) {
6425
6466
  return replyOf(async () => {
6426
6467
  try {
6427
- return {
6428
- switchedOff: await this.importDump(tables, destScopeId, {
6429
- switchOff: opts.switchOff,
6430
- sourceScopeId: opts.sourceScopeId,
6431
- exact: opts.exact,
6432
- keepPermissionFlags: true,
6433
- ...(projection ? { projection } : {}),
6434
- }),
6435
- };
6468
+ let plan;
6469
+ const switchedOff = await this.importDump(tables, destScopeId, {
6470
+ switchOff: opts.switchOff,
6471
+ sourceScopeId: opts.sourceScopeId,
6472
+ exact: opts.exact,
6473
+ keepPermissionFlags: true,
6474
+ ...(projection ? { projection } : {}),
6475
+ onObjects: (judged) => (plan = judged),
6476
+ });
6477
+ return { switchedOff, ...(plan ? { moduleObjects: dumpObjectsAudit(plan) } : {}) };
6436
6478
  }
6437
6479
  catch (e) {
6438
6480
  if (e instanceof Error && roleRefusals.has(e))
@@ -6503,19 +6545,31 @@ options = {}) {
6503
6545
  if (seq > head.nextSeq) {
6504
6546
  throw substratError('precondition_failed', `stage page ${seq} arrived before page ${head.nextSeq}`);
6505
6547
  }
6548
+ // A stage opened before objects were staged takes their table now: its earlier pages
6549
+ // staged none, so a later page that carries them reads as the table having changed.
6550
+ this.sql.exec(STAGE_OBJECTS_DDL);
6506
6551
  for (const t of tables) {
6507
6552
  const columns = JSON.stringify(t.columns);
6553
+ const objects = t.objects === undefined ? null : JSON.stringify(t.objects);
6508
6554
  let held = this.sql
6509
- .exec(`SELECT ord, ddl, columns FROM _substrat_stage_tables WHERE name = ?`, t.name)
6555
+ .exec(`SELECT s.ord, s.ddl, s.columns, o.objects FROM _substrat_stage_tables s
6556
+ LEFT JOIN _substrat_stage_objects o ON o.name = s.name WHERE s.name = ?`, t.name)
6510
6557
  .toArray()[0];
6511
6558
  if (!held) {
6559
+ // #2820: an export carries objects for every module table or for none. A page that
6560
+ // disagrees with the tables already staged was read across a change of version.
6561
+ if (!isSpineTable(t.name) && this.stagedModuleObjects().some((has) => has !== (objects !== null))) {
6562
+ throw substratError('precondition_failed', `table ${t.name} changed between dump pages; nothing more was staged`);
6563
+ }
6512
6564
  const ord = Number(this.sql.exec(`SELECT COUNT(*) AS n FROM _substrat_stage_tables`).toArray()[0].n);
6513
6565
  this.sql.exec(`INSERT INTO _substrat_stage_tables (ord, name, ddl, columns) VALUES (?, ?, ?, ?)`, ord, t.name, t.ddl, columns);
6566
+ if (objects !== null)
6567
+ this.sql.exec(`INSERT INTO _substrat_stage_objects (name, objects) VALUES (?, ?)`, t.name, objects);
6514
6568
  // Untyped columns: no affinity, so a cell is stored exactly as it came.
6515
6569
  this.sql.exec(`CREATE TABLE "${stageRowsTable(ord)}" (${t.columns.map((_, i) => `c${i}`).join(', ')})`);
6516
- held = { ord, ddl: t.ddl, columns };
6570
+ held = { ord, ddl: t.ddl, columns, objects };
6517
6571
  }
6518
- else if (held.ddl !== t.ddl || held.columns !== columns) {
6572
+ else if (held.ddl !== t.ddl || held.columns !== columns || held.objects !== objects) {
6519
6573
  throw substratError('precondition_failed', `table ${t.name} changed between dump pages; nothing more was staged`);
6520
6574
  }
6521
6575
  this.stageRows(held.ord, t);
@@ -6609,21 +6663,27 @@ options = {}) {
6609
6663
  */
6610
6664
  async commitStagedDumpChecked(stageId, destScopeId, opts) {
6611
6665
  let count = 0;
6612
- const out = await this.checkedLoad(() => {
6666
+ const out = await this.checkedLoad((onObjects) => {
6613
6667
  const head = this.stageHead();
6614
6668
  if (head?.stageId !== stageId)
6615
6669
  throw substratError('precondition_failed', STAGE_GONE);
6670
+ // Read only: a stage opened before objects were staged has no table for them, and loads none.
6671
+ const withObjects = this.hasTable('_substrat_stage_objects');
6616
6672
  const staged = this.sql
6617
- .exec(`SELECT ord, name, ddl, columns FROM _substrat_stage_tables ORDER BY name`)
6673
+ .exec(withObjects
6674
+ ? `SELECT s.ord, s.name, s.ddl, s.columns, o.objects FROM _substrat_stage_tables s
6675
+ LEFT JOIN _substrat_stage_objects o ON o.name = s.name ORDER BY s.name`
6676
+ : `SELECT ord, name, ddl, columns, NULL AS objects FROM _substrat_stage_tables ORDER BY name`)
6618
6677
  .toArray();
6619
6678
  const tables = staged.map((t) => ({
6620
6679
  name: t.name,
6621
6680
  ddl: t.ddl,
6622
6681
  columns: JSON.parse(t.columns),
6682
+ ...(t.objects === null ? {} : { objects: JSON.parse(t.objects) }),
6623
6683
  rows: () => this.stagedRows(t.ord),
6624
6684
  }));
6625
6685
  count = tables.length;
6626
- return this.loadTables(tables, destScopeId, opts, stageId);
6686
+ return this.loadTables(tables, destScopeId, { ...opts, onObjects }, stageId);
6627
6687
  });
6628
6688
  return out.refused === false ? { ...out, tables: count } : out;
6629
6689
  }
@@ -6659,6 +6719,14 @@ options = {}) {
6659
6719
  after = Number(batch[batch.length - 1][0]);
6660
6720
  }
6661
6721
  }
6722
+ /** #2820: for each module table staged so far, whether its pages carried its objects. */
6723
+ stagedModuleObjects() {
6724
+ return this.sql
6725
+ .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`)
6726
+ .toArray()
6727
+ .filter((r) => !isSpineTable(r.name))
6728
+ .map((r) => Boolean(r.has));
6729
+ }
6662
6730
  /** The staged load this store holds (#2450), or null. */
6663
6731
  stageHead() {
6664
6732
  const present = this.sql.exec(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_substrat_stage'`).toArray();
@@ -6678,7 +6746,9 @@ options = {}) {
6678
6746
  /** A load's refusals answered as values (#1722): across the RPC a throw carries only its message. */
6679
6747
  async checkedLoad(run) {
6680
6748
  try {
6681
- return { refused: false, switchedOff: await run() };
6749
+ let plan;
6750
+ const switchedOff = await run((judged) => (plan = judged));
6751
+ return { refused: false, switchedOff, ...(plan ? { moduleObjects: dumpObjectsAudit(plan) } : {}) };
6682
6752
  }
6683
6753
  catch (e) {
6684
6754
  if (e instanceof Error && fenceRefusals.has(e))
@@ -7531,6 +7601,7 @@ options = {}) {
7531
7601
  sql.exec(r.sql, ...r.params);
7532
7602
  },
7533
7603
  atomic: createAtomic(runSub, { passed, signals }),
7604
+ noteTransition: noteTransitionInto(signals),
7534
7605
  // #1672: mint / revoke / list, written once in the kernel — the pure adapter hands
7535
7606
  // the same function the same four things. The raw spine seam (the kernel's own write
7536
7607
  // to `_substrat_capabilities`), the operation's OWN check, and `ctx.emit`. A