@substrat-run/adapter-sqlite 0.144.0 → 0.146.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/dist/checker.d.ts.map +1 -1
- package/dist/checker.js +7 -1
- package/dist/checker.js.map +1 -1
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +166 -63
- package/dist/index.js.map +1 -1
- package/dist/vertical-broker.d.ts.map +1 -1
- package/dist/vertical-broker.js +3 -1
- package/dist/vertical-broker.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import Database from 'better-sqlite3';
|
|
4
|
-
import { accessLogEntry, adminLogEntry, delegatedReadParams, operationSeriesCount, opsFailureEntry, opsFailureFingerprint, issueEntry, FRESHNESS_HEARTBEAT_MINUTES, sweepRunEntry, modelUsageEntry, ATTACHMENT_ADDED, ATTACHMENT_REMOVED, attachmentRecord, createTenantInput, domainEvent, domainEventInput, exportReadInput, importBatch, importCursorMove, exportsOfManifestJson, importsOfManifestJson, entitlementGrant, entitlementGrantInput, eventId, meterReading, platformRequestInput, platformRequestId, MAX_PENDING_PLATFORM_REQUESTS, identityLink, identityPool, instant, isSubstratError, connection, capabilityGrant, connectionGrant, connectionGrantRecord, connectionSecret, systemGrant, systemSwitch, systemSwitchRecord, peerSwitch, verticalCaller, verticalSlug as verticalSlugOf, subjectRef, requestFingerprint, createConnectionInput, connectLinkFilter, connectLinkKey, consumeConnectLinkInput, mintConnectLinkInput, projectedConnectionGrant, projectedConnectionKey, moduleManifest, createOrgInput, promotionAcknowledgement, bindAcknowledgement, bindHostnameInput, channelHistoryEntry, hostnameBinding, publishVersionInput, AUTO_ADMISSION_NOTE, routeTarget, ROUTE_SCOPE_HOSTNAMES_MAX, registerVerticalInput, vertical as verticalSchema, verticalChannel, verticalVersion, objectRef, entityObjectRef, assertModuleEmittableType, assertKernelAuthoredType, grantRefFromProof, org as orgSchema, orgMembership, principalId, resolvedIdentity, identityMembership, roleDefinition, scope as scopeSchema, tenant as tenantSchema, tenantRole, principalBecomeCapabilityInput, subjectShredReceipt, SCOPE_TABLE_PAGE_DEFAULT, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, verticalServingState, callsOfManifestJson, outboundOfManifestJson, listLimitOf, pageOf, countedPageOf, SCOPE_GATE_REASONS, substratError, assertReplayableDump, delegatedReadRecord, ownerTransferAudit, memberChangeAudit, peerBindingAudit, copyMarkAudit, redrainEventsInput, REDRAIN_BATCH, } from '@substrat-run/contracts';
|
|
5
|
-
import { actorOf, asPrincipal, assertAllowed, assertNoSecret, assertPermissionKey, CAPABILITY_DDL, COPY_ORIGIN_DDL, ENTITY_STATE_MOVES_DDL, CAPABILITY_EXCHANGE_OPERATION, CAPABILITY_BECOME_MINT_OPERATION, becomeMintCheck, mintBecomeCapabilityAsPrincipal, revokeBecomeCapabilityAsPrincipal, readBecomeLinkStates, ENTITY_UNBIND_OPERATION, allowedFor, unbindEntityHolderBounded, capabilityAttachmentWriteRefused, capabilityTokenHash, createCapabilityVerbs, createEntityEdgeVerbs, createEntityStateVerbs, createTrashedReads, CONTINUATION_POSITION_CAP, visibleContinuation, searchStateWhere, uncheckedView, addStatePlans, entityStateMigrations, assertNoCallerPurge, isUnreachableParent, heldPurgePass, purgeDueOf, purgeHeldBy, lifecycleRefusal, purgeOnlyKeysOf, purgeReportOf, purgeStillDue, refuseTrashedTarget, assertBoundOperations, registerTrashTargets, runPurgePass, withheldKeysFor, statefulTablesOf, afterMigration, afterRuntimeDdl, derivesAnything, repairDerivedObjects, StateColumnLost, exchangeCapability as exchangeCapabilitySecret, guardSecrets, mintBecomeCapability, plausibleSessionToken, readCapabilityPage, redactSecrets, redactSecretText, moduleLog, consoleLogSink, asyncInvocationId, asyncLinePass, consoleInvocationLineSink, resolveCapabilitySession, revokeCapabilityAsPlatform, assertReadOnlyQuery, attachmentBlobKey, attachmentSha256, entitlementDenial, requiredEntitlementFor, foldMeterReading, guardSpine, guardSqlLimits, DO_SQL_LIMITS, tooManyResultColumns, tooManyTableColumns, parseValidationRecords, resolveScopeRecord, ulid, ISSUE_RETENTION_DAYS, telemetryRetentionStatements, FINDINGS_DDL, SCOPE_STORAGE_DDL, forgetScopeStorage, listScopeStorageAttemptRows, listScopeStorageRows, pruneScopeStorageRows, recordScopeStorageRows, CONNECT_LINKS_DDL, consumeConnectLinkRow, insertConnectLink, listConnectLinks as listConnectLinkRows, readConnectLink, restoreConnectLinkRow, revokeConnectLinkRow, createFindingRule, findingOfOpsFailure, findingOfSweepRun, pruneFindings, listFindingRules, listFindings, observeFinding, revokeFindingRule, setFindingStatus, assertRowLimit, assertRowOffset, OPS_FAILURE_RETENTION_DAYS, SWEEP_RUN_RETENTION_DAYS, SWEEP_RUNS_INTENT_INDEX, sweepRunsIntentHasKind, MODEL_USAGE_RETENTION_DAYS, foldModelUsage, backoffAt, platformRequestHistoryQuery, platformRequestOf, PLATFORM_REQUEST_COLUMNS, PLATFORM_REQUEST_REDACTION_SQL, platformRequestRedactionParams, platformRequestRedactionQuery, intentPayloadCarriesSubject, redactSubjectJobRuns, eraseSubjectFromModules, moduleRowsErased, moduleErasurePlan, redactSubjectScopeText, redactSubjectDirectoryText, ISSUE_EXEMPLAR_OWNER_BACKFILL_SQL, issueExemplarOwner, 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_STEP_RECORD_SQL, JOB_LEASE_EXPIRED_NOTE, assertLeaseMs, DELIVERY_ERROR_REDACTION_SQL, REDACTED_DELIVERY_NOTE, seatScopeTuple, applyScopeRoleChange, changeScopeRole, revokeScopeRoles, scopeRoleHolders, admitPeer, collectPeers, peerSeats, resolveVerticalInstanceFrom, resolvePeerInstanceFrom, peerGrantsStatus, systemGrantsStatus, systemScheduleState, systemSwitchedOff, systemSwitchedOffMessage, tenantSystemSwitchedOffMessage, PEER_SWITCHES_DDL, PEER_BINDINGS_DDL, SWITCH_OWED_DDL, SWITCH_FENCES_DDL, TABLE_OWNERS_DDL, assertMigrationLeavesLedgerAlone, recordOwnershipSteps, runMigrationStatements, executableSqlStatements, blankSqlComments, recordWriteSuperseded, switchFencesOf, switchSupersededMessage, SWITCH_KINDS, SYSTEM_SWITCHES_DDL, forgetSwitchesOf, listSystemSwitchRecords, moveSwitch, reassertActionOf, reassertEntry, reassertOffRow, recordSwitchedOff, recordSwitchedOn, restoreSwitchRecord, scopesSwitchedOffFor, switchActionOf, switchAuditSubject, recordAuditOutcome, auditedCapabilityRevoke, auditWarningOf, switchNotFoundMessage, switchRecordsOf, switchedOffOf, switchesBackfillSqlOf, switchesTableExists, tenantHeldOf, tenantHoldsGrant, inUnitMovesToAudit, staleCarryRevertRow, staleCarryReverts, withRecorded, VERSION_MIGRATIONS_DDL, splitManifestMigrations, splitVersionMigrationsBatch, versionMigrationsOf, writeVersionMigrations, denialListQuery, refusalListQuery, mapRefusalRow, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, IMPERSONATION_COLUMNS, IMPERSONATION_DDL, ImpersonationRefused, assertImpersonationWrites, assertModuleEnqueueableKind, assertSessionUsable, impersonationByIdQuery, impersonationListQuery, impersonationRowValues, impersonationStampOf, mapImpersonationRow, newImpersonationSession, resolveRetryPolicy, executorOutcomeOf, isDeliveryRefusal, refusalJournalText, membershipFencesBackfillSql, MEMBERSHIP_FENCES_DDL, membershipFencesTableExists, MEMBERSHIP_FENCE_SINCE_SQL, RAISE_MEMBERSHIP_FENCE_SQL, memberAddedAudit, orgChangeBound, tenantCoverage, isSecretBoxConfigured, unconfiguredSecretBox, createSubjectKeys, generateSealingKeyPair, noSealingKeyMessage, openSealed, sealTo, ConnectionSealingKeyUnavailableError, PermissionDenied, createAtomic, NotSearchable, NotListable, listIndexMigrations, listIndexPlans, listQuery, cursorOf, moduleMigrations, MIGRATION_DIGEST_FENCE_DDL, MIGRATION_DIGEST_MARK_LEGACY, assertJournalDumpCoherent, assertMigrationSql, migrationDivergence, migrationFailedError, migrationSteps, planMigrations, isSearchIndexTable, searchIndexMigrations, searchIndexPlans, searchLimit, searchMatchExpression, searchQuery, ATTACHMENT_TEXT_DDL, assertAttachmentExtractors, resolveAttachmentTextBounds, assertJobRegistrable, attachmentRecordOfRow, attachmentTextJob, enqueueAttachmentText, ATTACHMENT_TEXT_BACKFILL_JOB, ATTACHMENT_TEXT_JOB, kernelJobFor, queueAttachmentTextBackfill, startAttachmentTextBackfill, attachmentTextBackfillJob, reconcileAttachmentText, recordAttachmentText, searchAttachments, 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, emittedSinceQuery, emittedReportOf, EMITTED_REPORT_CAP, exportedSinceQuery, IMPORT_CURSOR_ADVANCE_SQL, IMPORT_RECORD_SQL, moveImportCursor, importCursorSourceOf, exportBreaksOf, exportBreakRefusal, bindExportBreaksOf, bindExportBreakRefusal, CrossVerticalRegistry, exportReadPlan, exportReadQuery, planExportBatch, withheldNote, JOB_RUN_DDL, JOB_RUN_DUE_AT, jobRunListLimit, jobRunOf, runDueJobRuns, startJobRun, IDEMPOTENCY_DDL, REFUSALS_TABLE_DDL, REFUSALS_INDEX, refusalInsert, refusalOf, markGuardRefusal, REFUSALS_REBUILD, refusalsAdmitGuards, assertIdempotencyKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, globalFetch, createUlid, facetEvents, readHistory, walkEventCause, walkEventEffects, assertRedrainWindow, readInvocation, readDeadLetters, readLifecycleFlow, readOperationSeries, domainEventOf, readUndrainedOutbox, undrainedEventsOf, connectorCallRecord, noopConnectorCallRecorder, recordConnectorCall, settleConnectionUse, unknownRoleError, } from '@substrat-run/kernel';
|
|
4
|
+
import { accessLogEntry, adminLogEntry, delegatedReadParams, operationSeriesCount, opsFailureEntry, opsFailureFingerprint, issueEntry, FRESHNESS_HEARTBEAT_MINUTES, sweepRunEntry, modelUsageEntry, ATTACHMENT_ADDED, ATTACHMENT_REMOVED, attachmentRecord, createTenantInput, domainEvent, domainEventInput, exportReadInput, importBatch, importCursorMove, exportsOfManifestJson, assertNoBindingShapeBreak, importsOfManifestJson, entitlementGrant, entitlementGrantInput, eventId, meterReading, platformRequestInput, platformRequestId, MAX_PENDING_PLATFORM_REQUESTS, identityLink, identityPool, instant, isSubstratError, connection, capabilityGrant, connectionGrant, connectionGrantRecord, connectionSecret, systemGrant, systemSwitch, systemSwitchRecord, peerSwitch, verticalCaller, verticalSlug as verticalSlugOf, subjectRef, requestFingerprint, createConnectionInput, connectLinkFilter, connectLinkKey, consumeConnectLinkInput, mintConnectLinkInput, projectedConnectionGrant, projectedConnectionKey, moduleManifest, createOrgInput, promotionAcknowledgement, bindAcknowledgement, bindHostnameInput, channelHistoryEntry, hostnameBinding, publishVersionInput, AUTO_ADMISSION_NOTE, routeTarget, ROUTE_SCOPE_HOSTNAMES_MAX, registerVerticalInput, vertical as verticalSchema, verticalChannel, verticalVersion, objectRef, entityObjectRef, assertModuleEmittableType, assertKernelAuthoredType, grantRefFromProof, org as orgSchema, orgMembership, principalId, resolvedIdentity, identityMembership, roleDefinition, scope as scopeSchema, tenant as tenantSchema, tenantRole, principalBecomeCapabilityInput, subjectShredReceipt, SCOPE_TABLE_PAGE_DEFAULT, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, verticalServingState, callsOfManifestJson, outboundOfManifestJson, listLimitOf, pageOf, countedPageOf, SCOPE_GATE_REASONS, substratError, assertReplayableDump, delegatedReadRecord, ownerTransferAudit, memberChangeAudit, peerBindingAudit, copyMarkAudit, redrainEventsInput, REDRAIN_BATCH, } from '@substrat-run/contracts';
|
|
5
|
+
import { actorOf, asPrincipal, assertAllowed, assertNoSecret, assertPermissionKey, CAPABILITY_DDL, COPY_ORIGIN_DDL, ENTITY_STATE_MOVES_DDL, CAPABILITY_EXCHANGE_OPERATION, CAPABILITY_BECOME_MINT_OPERATION, becomeMintCheck, mintBecomeCapabilityAsPrincipal, revokeBecomeCapabilityAsPrincipal, readBecomeLinkStates, ENTITY_UNBIND_OPERATION, allowedFor, unbindEntityHolderBounded, capabilityAttachmentWriteRefused, capabilityTokenHash, createCapabilityVerbs, createEntityEdgeVerbs, createEntityStateVerbs, createTrashedReads, CONTINUATION_POSITION_CAP, visibleContinuation, searchStateWhere, uncheckedView, addStatePlans, addUpdatedStampPlans, watchUpdatedStamps, entityStateMigrations, assertNoCallerPurge, isUnreachableParent, heldPurgePass, purgeDueOf, purgeHeldBy, lifecycleRefusal, purgeOnlyKeysOf, purgeReportOf, purgeStillDue, refuseTrashedTarget, assertBoundOperations, registerTrashTargets, runPurgePass, withheldKeysFor, statefulTablesOf, afterMigration, afterRuntimeDdl, derivesAnything, repairDerivedObjects, StateColumnLost, exchangeCapability as exchangeCapabilitySecret, guardSecrets, mintBecomeCapability, plausibleSessionToken, readCapabilityPage, redactSecrets, redactSecretText, moduleLog, consoleLogSink, asyncInvocationId, asyncLinePass, consoleInvocationLineSink, resolveCapabilitySession, revokeCapabilityAsPlatform, assertReadOnlyQuery, attachmentBlobKey, attachmentSha256, entitlementDenial, requiredEntitlementFor, foldMeterReading, guardSpine, guardSqlLimits, DO_SQL_LIMITS, tooManyResultColumns, tooManyTableColumns, parseValidationRecords, resolveScopeRecord, ulid, ISSUE_RETENTION_DAYS, telemetryRetentionStatements, FINDINGS_DDL, SCOPE_STORAGE_DDL, forgetScopeStorage, listScopeStorageAttemptRows, listScopeStorageRows, pruneScopeStorageRows, recordScopeStorageRows, CONNECT_LINKS_DDL, consumeConnectLinkRow, insertConnectLink, listConnectLinks as listConnectLinkRows, readConnectLink, restoreConnectLinkRow, revokeConnectLinkRow, createFindingRule, findingOfOpsFailure, findingOfSweepRun, pruneFindings, listFindingRules, listFindings, observeFinding, revokeFindingRule, setFindingStatus, assertRowLimit, assertRowOffset, OPS_FAILURE_RETENTION_DAYS, SWEEP_RUN_RETENTION_DAYS, SWEEP_RUNS_INTENT_INDEX, sweepRunsIntentHasKind, MODEL_USAGE_RETENTION_DAYS, foldModelUsage, backoffAt, platformRequestHistoryQuery, platformRequestOf, PLATFORM_REQUEST_COLUMNS, PLATFORM_REQUEST_REDACTION_SQL, platformRequestRedactionParams, platformRequestRedactionQuery, intentPayloadCarriesSubject, redactSubjectJobRuns, eraseSubjectFromModules, moduleRowsErased, moduleErasurePlan, redactSubjectScopeText, redactSubjectDirectoryText, ISSUE_EXEMPLAR_OWNER_BACKFILL_SQL, issueExemplarOwner, 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_STEP_RECORD_SQL, JOB_LEASE_EXPIRED_NOTE, assertLeaseMs, DELIVERY_ERROR_REDACTION_SQL, REDACTED_DELIVERY_NOTE, seatScopeTuple, applyScopeRoleChange, changeScopeRole, revokeScopeRoles, scopeRoleHolders, admitPeer, peerCallerAt, collectPeers, peerSeats, resolveVerticalInstanceFrom, resolvePeerInstanceFrom, peerVerticalSpellings, samePeerVertical, peerGrantsStatus, systemGrantsStatus, systemScheduleState, systemSwitchedOff, systemSwitchedOffMessage, tenantSystemSwitchedOffMessage, PEER_SWITCHES_DDL, PEER_BINDINGS_DDL, SWITCH_OWED_DDL, SWITCH_FENCES_DDL, TABLE_OWNERS_DDL, assertMigrationLeavesLedgerAlone, recordOwnershipSteps, runMigrationStatements, executableSqlStatements, blankSqlComments, recordWriteSuperseded, switchFencesOf, switchSupersededMessage, SWITCH_KINDS, SYSTEM_SWITCHES_DDL, forgetSwitchesOf, listSystemSwitchRecords, moveSwitch, reassertActionOf, reassertEntry, reassertOffRow, recordSwitchedOff, recordSwitchedOn, restoreSwitchRecord, scopesSwitchedOffFor, switchActionOf, switchAuditSubject, recordAuditOutcome, auditedCapabilityRevoke, auditWarningOf, switchNotFoundMessage, switchRecordsOf, switchedOffOf, switchesBackfillSqlOf, switchesTableExists, tenantHeldOf, tenantHoldsGrant, inUnitMovesToAudit, staleCarryRevertRow, staleCarryReverts, withRecorded, VERSION_MIGRATIONS_DDL, splitManifestMigrations, splitVersionMigrationsBatch, versionMigrationsOf, writeVersionMigrations, denialListQuery, refusalListQuery, mapRefusalRow, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, IMPERSONATION_COLUMNS, IMPERSONATION_DDL, ImpersonationRefused, assertImpersonationWrites, assertModuleEnqueueableKind, assertSessionUsable, impersonationByIdQuery, impersonationListQuery, impersonationRowValues, impersonationStampOf, mapImpersonationRow, newImpersonationSession, resolveRetryPolicy, executorOutcomeOf, isDeliveryRefusal, refusalJournalText, membershipFencesBackfillSql, MEMBERSHIP_FENCES_DDL, membershipFencesTableExists, MEMBERSHIP_FENCE_SINCE_SQL, RAISE_MEMBERSHIP_FENCE_SQL, memberAddedAudit, orgChangeBound, tenantCoverage, isSecretBoxConfigured, unconfiguredSecretBox, createSubjectKeys, generateSealingKeyPair, noSealingKeyMessage, openSealed, sealTo, ConnectionSealingKeyUnavailableError, PermissionDenied, createAtomic, NotSearchable, NotListable, listIndexMigrations, listIndexPlans, listQuery, cursorOf, moduleMigrations, MIGRATION_DIGEST_FENCE_DDL, MIGRATION_DIGEST_MARK_LEGACY, assertJournalDumpCoherent, assertMigrationSql, migrationDivergence, migrationFailedError, migrationSteps, planMigrations, isSearchIndexTable, searchIndexMigrations, searchIndexPlans, searchLimit, searchMatchExpression, searchQuery, ATTACHMENT_TEXT_DDL, assertAttachmentExtractors, resolveAttachmentTextBounds, assertJobRegistrable, attachmentRecordOfRow, attachmentTextJob, enqueueAttachmentText, ATTACHMENT_TEXT_BACKFILL_JOB, ATTACHMENT_TEXT_JOB, kernelJobFor, queueAttachmentTextBackfill, startAttachmentTextBackfill, attachmentTextBackfillJob, reconcileAttachmentText, recordAttachmentText, searchAttachments, 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, emittedSinceQuery, emittedReportOf, EMITTED_REPORT_CAP, exportedSinceQuery, IMPORT_CURSOR_ADVANCE_SQL, IMPORT_RECORD_SQL, moveImportCursor, importCursorSourceOf, exportBreaksOf, exportBreakRefusal, bindExportBreaksOf, bindExportBreakRefusal, scopeMoveOf, runningVersionOf, isPrimaryScope, PROVISIONED_VERSIONS_SQL, installsReconciledOf, strandJudgmentsOf, provisionedVersionsParams, boundShapesColumn, boundShapesOfColumn, installReconciled, CrossVerticalRegistry, exportReadPlan, exportReadQuery, planExportBatch, withheldNote, JOB_RUN_DDL, JOB_RUN_DUE_AT, jobRunListLimit, jobRunOf, runDueJobRuns, startJobRun, IDEMPOTENCY_DDL, REFUSALS_TABLE_DDL, REFUSALS_INDEX, refusalInsert, refusalOf, markGuardRefusal, REFUSALS_REBUILD, refusalsAdmitGuards, assertIdempotencyKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, globalFetch, createUlid, facetEvents, readHistory, walkEventCause, walkEventEffects, assertRedrainWindow, readInvocation, readDeadLetters, readLifecycleFlow, readOperationSeries, domainEventOf, readUndrainedOutbox, undrainedEventsOf, connectorCallRecord, noopConnectorCallRecorder, recordConnectorCall, settleConnectionUse, unknownRoleError, } from '@substrat-run/kernel';
|
|
6
6
|
import { attributedView, delegatedRevokeSql, writeExplicitTupleIn, grantEntityShapeIn, shapeTopUpBatch, topUpEntityGrantShapes, settlePlatformRequestIn, } from '@substrat-run/kernel';
|
|
7
7
|
import { ADMIN_LOG_INDEXES_SQL, SETTLE_INTENT_SQL, SETTLE_OUTCOME_SQL, settleOutcomeParamsOf, readAuditedOperations, unknownOutcomeOf, } from '@substrat-run/kernel';
|
|
8
8
|
import { INERT_SCOPE_REASON, isPrimaryScopeRow } from '@substrat-run/kernel';
|
|
@@ -318,9 +318,9 @@ const KERNEL_DDL = `
|
|
|
318
318
|
-- #1575: attachment text and its FTS5 index, shared from @substrat-run/kernel. After
|
|
319
319
|
-- _substrat_attachments, whose delete trigger it adds.
|
|
320
320
|
${ATTACHMENT_TEXT_DDL}
|
|
321
|
-
-- #901: an entity's version is the ULID of the last event about it
|
|
322
|
-
--
|
|
323
|
-
--
|
|
321
|
+
-- #901: an entity's version is the ULID of the last event about it (a state move
|
|
322
|
+
-- excepted, #2191), so the newest id per (entity_type, entity_id) is the read. The id
|
|
323
|
+
-- column sits last so SQLite walks the matched range backwards instead of aggregating.
|
|
324
324
|
${OUTBOX_ENTITY_INDEX}
|
|
325
325
|
`;
|
|
326
326
|
/** Row → contract shape. Never reads the secrets table — that is the point of the split. */
|
|
@@ -628,6 +628,8 @@ export class SqliteScopeHost {
|
|
|
628
628
|
listPlans = new Map();
|
|
629
629
|
/** #119: entity type → its archive/trash plan, from every registered module. */
|
|
630
630
|
statePlans = new Map();
|
|
631
|
+
/** #2166: entity type → the updated stamps the host holds, from every registered module. */
|
|
632
|
+
updatedStampPlans = new Map();
|
|
631
633
|
/** #2068: each registered module's erasure, in registration order — what `shredSubject` runs. */
|
|
632
634
|
erasurePlans = [];
|
|
633
635
|
/** entityType → the declared attachment gate (#473): read key + write key (default: read). */
|
|
@@ -1654,6 +1656,7 @@ export class SqliteScopeHost {
|
|
|
1654
1656
|
seen.add(m.version);
|
|
1655
1657
|
}
|
|
1656
1658
|
addStatePlans(this.statePlans, manifest.id, manifest.entityStates, manifest.permissions);
|
|
1659
|
+
addUpdatedStampPlans(this.updatedStampPlans, manifest.id, manifest.updatedStamps);
|
|
1657
1660
|
for (const plan of listIndexPlans(manifest.id, manifest.lists, manifest.entityStates)) {
|
|
1658
1661
|
const existing = this.listPlans.get(plan.entityType);
|
|
1659
1662
|
if (existing) {
|
|
@@ -2905,7 +2908,10 @@ export class SqliteScopeHost {
|
|
|
2905
2908
|
// `{ vertical, scope }` and the authorization they passed (K-34). What it emits was
|
|
2906
2909
|
// emitted BECAUSE of the producer's event (#1237); the id resolves through
|
|
2907
2910
|
// `_substrat_imports`, which the row above has just written.
|
|
2908
|
-
|
|
2911
|
+
const ctx = this.deliveryContext(rt, subject, undefined, { causedBy: e.id, invocationId: unit.invocationId });
|
|
2912
|
+
const stamps = watchUpdatedStamps(spineSql(rt.db), this.updatedStampPlans);
|
|
2913
|
+
await imp.handler(ctx, event);
|
|
2914
|
+
stamps?.assertHeld(ctx, `${imp.moduleId} consuming ${e.type}`);
|
|
2909
2915
|
rt.db
|
|
2910
2916
|
.prepare(`INSERT INTO _substrat_deliveries (event_id, consumer_module, delivered_at, invocation_id)
|
|
2911
2917
|
VALUES (?, ?, ?, NULL)`)
|
|
@@ -2956,24 +2962,63 @@ export class SqliteScopeHost {
|
|
|
2956
2962
|
* move, the one case it decides anything.
|
|
2957
2963
|
*/
|
|
2958
2964
|
bindBreaks(actor, scope, incoming, servingRef) {
|
|
2959
|
-
const vertical = scope.vertical && (scope.servingRef || servingRef)
|
|
2960
|
-
? this.directory
|
|
2961
|
-
.prepare('SELECT serving_ref, serving_version_id FROM verticals WHERE slug = ?')
|
|
2962
|
-
.get(scope.vertical)
|
|
2963
|
-
: undefined;
|
|
2964
2965
|
return bindExportBreaksOf({
|
|
2965
2966
|
admin: this.admin,
|
|
2966
2967
|
actor,
|
|
2967
2968
|
scope,
|
|
2968
2969
|
incoming: { id: incoming.id, verticalSlug: incoming.verticalSlug },
|
|
2969
2970
|
...(servingRef !== undefined ? { servingRef } : {}),
|
|
2970
|
-
serving:
|
|
2971
|
-
? { ref: vertical.serving_ref, versionId: vertical.serving_version_id }
|
|
2972
|
-
: null,
|
|
2971
|
+
serving: this.servingPointerOf(scope, servingRef),
|
|
2973
2972
|
readExports: async (versionId) => exportsOfManifestJson(this.manifestJsonOf(versionId) ?? null),
|
|
2974
2973
|
readImports: async (slug, versionId) => this.versionImports(slug, versionId),
|
|
2975
2974
|
});
|
|
2976
2975
|
}
|
|
2976
|
+
/**
|
|
2977
|
+
* The serving pointer of `scope`'s vertical, read only when the scope is on a serving script
|
|
2978
|
+
* before or after a move (`servingRef`), the one case it decides what the scope runs.
|
|
2979
|
+
*/
|
|
2980
|
+
servingPointerOf(scope, servingRef) {
|
|
2981
|
+
return scope.vertical && (scope.servingRef || servingRef) ? this.servingPointerOfVertical(scope.vertical) : null;
|
|
2982
|
+
}
|
|
2983
|
+
/** What `slug` serves in place, or null when nothing is. */
|
|
2984
|
+
servingPointerOfVertical(slug) {
|
|
2985
|
+
const v = this.directory
|
|
2986
|
+
.prepare('SELECT serving_ref, serving_version_id FROM verticals WHERE slug = ?')
|
|
2987
|
+
.get(slug);
|
|
2988
|
+
return v?.serving_ref && v.serving_version_id ? { ref: v.serving_ref, versionId: v.serving_version_id } : null;
|
|
2989
|
+
}
|
|
2990
|
+
/**
|
|
2991
|
+
* #2178: refuse running `incomingId` where any of `froms` ran, each distinct version judged once,
|
|
2992
|
+
* and where installs last reconciled one of `receipts` — a refusal from those names them.
|
|
2993
|
+
*/
|
|
2994
|
+
assertNoStrandFrom(froms, incomingId, receipts = []) {
|
|
2995
|
+
const incoming = this.manifestJsonOf(incomingId);
|
|
2996
|
+
const judged = [...new Set(froms)].filter((f) => !!f && f !== incomingId);
|
|
2997
|
+
for (const from of judged)
|
|
2998
|
+
assertNoBindingShapeBreak(this.manifestJsonOf(from), incoming);
|
|
2999
|
+
// What each group of installs is judged from, and how (`strandJudgmentsOf`, one rule for both adapters).
|
|
3000
|
+
for (const r of receipts)
|
|
3001
|
+
for (const j of strandJudgmentsOf(r, judged, incomingId))
|
|
3002
|
+
assertNoBindingShapeBreak(this.manifestJsonOf(j.from), incoming, j.opts);
|
|
3003
|
+
}
|
|
3004
|
+
/**
|
|
3005
|
+
* #2178: refuse moving `scope` (a bind, or a route with `servingRef`) onto code that strands
|
|
3006
|
+
* people a link bound — any scope, fork or preview included, since bound people live in its own
|
|
3007
|
+
* spine. No acknowledgement passes it.
|
|
3008
|
+
*/
|
|
3009
|
+
assertBindingShapesKept(scope, incomingId, servingRef) {
|
|
3010
|
+
const move = scopeMoveOf(scope, incomingId, servingRef, this.servingPointerOf(scope, servingRef));
|
|
3011
|
+
// A primary scope is also judged from the version it last reconciled: until then, a retirement
|
|
3012
|
+
// the running version declares has not reached it (`PROVISIONED_VERSIONS_SQL`).
|
|
3013
|
+
if (!move)
|
|
3014
|
+
return;
|
|
3015
|
+
const receipt = isPrimaryScope(scope) ? [installReconciled(scope, move.before)] : [];
|
|
3016
|
+
this.assertNoStrandFrom([move.before], move.after, receipt);
|
|
3017
|
+
}
|
|
3018
|
+
/** `PROVISIONED_VERSIONS_SQL` over the directory. */
|
|
3019
|
+
provisionedVersionsOf(vertical, serving, ownerTenant) {
|
|
3020
|
+
return installsReconciledOf(this.directory.prepare(PROVISIONED_VERSIONS_SQL).all(...provisionedVersionsParams(vertical, serving, ownerTenant)));
|
|
3021
|
+
}
|
|
2977
3022
|
/** What one version of `slug` imports, from the registry; `not_found` for a version it does not know. */
|
|
2978
3023
|
versionImports(slug, versionId) {
|
|
2979
3024
|
const manifest = this.manifestJsonOf(versionId, slug);
|
|
@@ -3022,6 +3067,32 @@ export class SqliteScopeHost {
|
|
|
3022
3067
|
await this.applyPendingMigrations(rt);
|
|
3023
3068
|
return rt;
|
|
3024
3069
|
}
|
|
3070
|
+
/**
|
|
3071
|
+
* The tenant a peer name is read in (#2252): the directory's own slug for that tenant, the one
|
|
3072
|
+
* prefix a push pinned to it registers under. Never parsed out of the name being matched.
|
|
3073
|
+
*/
|
|
3074
|
+
peerTenant(tenantId) {
|
|
3075
|
+
const row = this.directory.prepare('SELECT slug FROM tenants WHERE tenant_id = ?').get(tenantId);
|
|
3076
|
+
return { id: tenantId, slug: row?.slug ?? null };
|
|
3077
|
+
}
|
|
3078
|
+
/**
|
|
3079
|
+
* Every scope of the tenant registered under a spelling of the peer name `vertical` in
|
|
3080
|
+
* `tenant` (`peerVerticalSpellings`), for the kernel's resolvers to choose among (#2252).
|
|
3081
|
+
*/
|
|
3082
|
+
peerCandidates(tenant, vertical) {
|
|
3083
|
+
const rows = this.directory
|
|
3084
|
+
.prepare(`SELECT scope_id, tenant_id, vertical, status, kind, forked_from FROM scopes
|
|
3085
|
+
WHERE tenant_id = ? AND vertical IN (SELECT value FROM json_each(?))`)
|
|
3086
|
+
.all(tenant.id, JSON.stringify(peerVerticalSpellings(vertical, tenant.slug)));
|
|
3087
|
+
return rows.map((r) => ({
|
|
3088
|
+
id: r.scope_id,
|
|
3089
|
+
tenantId: r.tenant_id,
|
|
3090
|
+
vertical: r.vertical,
|
|
3091
|
+
status: r.status,
|
|
3092
|
+
kind: r.kind ?? '',
|
|
3093
|
+
forkedFrom: r.forked_from ?? null,
|
|
3094
|
+
}));
|
|
3095
|
+
}
|
|
3025
3096
|
/** The directory's current hold, read again when background work is about to run. */
|
|
3026
3097
|
scopeWorkRefusal(tenantId, scopeId, allowProvisioning = false) {
|
|
3027
3098
|
const row = this.directory
|
|
@@ -3116,7 +3187,8 @@ export class SqliteScopeHost {
|
|
|
3116
3187
|
* here, and `admitPeer` runs inside the scope's actor task on every invoke (`buildStub`).
|
|
3117
3188
|
*/
|
|
3118
3189
|
async getVerticalScope(caller, tenantId, scopeId, options) {
|
|
3119
|
-
|
|
3190
|
+
// #2252: the caller as this vertical's `peers` names it, as on the hosted door.
|
|
3191
|
+
const parsed = peerCallerAt(this.peerDeclarations(), verticalCaller.parse(caller));
|
|
3120
3192
|
const rt = await this.openActiveScope(tenantId, scopeId);
|
|
3121
3193
|
return this.buildStub(tenantId, scopeId, rt, { kind: 'vertical-peer', caller: parsed }, options);
|
|
3122
3194
|
}
|
|
@@ -3826,15 +3898,18 @@ export class SqliteScopeHost {
|
|
|
3826
3898
|
// so the only loss is effort on a path that was already failing.
|
|
3827
3899
|
const ref = guarded ? this.concurrencyRef(operation, guarded, parsed) : undefined;
|
|
3828
3900
|
const seen = ref && invokeOptions?.ifMatch !== undefined ? this.versionAt(rt, ref) : undefined;
|
|
3901
|
+
// #2166: what the guards and the handler emit about a stamped entity, held below.
|
|
3902
|
+
const stamps = watchUpdatedStamps(spineSql(rt.db), this.updatedStampPlans);
|
|
3829
3903
|
// #119: an operation on a TRASHED entity it did not declare it reaches is refused
|
|
3830
3904
|
// here, before the guards and the handler — the declared key first, so a caller
|
|
3831
3905
|
// without it meets the same `forbidden` an active entity gives (`entity-trash.ts`).
|
|
3832
|
-
await refuseTrashedTarget({ sql: spineSql(rt.db), plans: this.statePlans, check: ctx.check }, operation, target, parsed, purging ? {
|
|
3906
|
+
await refuseTrashedTarget({ sql: spineSql(rt.db), plans: this.statePlans, check: ctx.check, now: ctx.now() }, operation, target, parsed, purging ? { gate: this.purgeGateFacts(rt, subject.id, tenantId, scopeId) } : undefined);
|
|
3833
3907
|
// Manifest guards (K-17): pre-conditions, inside the operation's own
|
|
3834
3908
|
// transaction, before the handler. A throw here blocks the operation
|
|
3835
3909
|
// and rolls back exactly like a handler throw — fail closed.
|
|
3836
3910
|
await this.runGuards(operation, ctx, parsed);
|
|
3837
3911
|
result = await handler(ctx, parsed);
|
|
3912
|
+
stamps?.assertHeld(ctx, operation);
|
|
3838
3913
|
// #119: the purge gate's AUTHORITATIVE read. Lifecycle, primacy and tenant live in the
|
|
3839
3914
|
// directory, which this transaction does not cover and whose writers (a suspend, a
|
|
3840
3915
|
// tenant status, a reclassification) do not take the scope's actor — so a hold could
|
|
@@ -4682,7 +4757,9 @@ export class SqliteScopeHost {
|
|
|
4682
4757
|
});
|
|
4683
4758
|
rt.db.exec('BEGIN IMMEDIATE');
|
|
4684
4759
|
try {
|
|
4760
|
+
const stamps = watchUpdatedStamps(spineSql(rt.db), this.updatedStampPlans);
|
|
4685
4761
|
await consumer.handler(ctx, event);
|
|
4762
|
+
stamps?.assertHeld(ctx, `${mod.id} consuming ${event.type}`);
|
|
4686
4763
|
rt.db
|
|
4687
4764
|
.prepare(`INSERT INTO _substrat_deliveries
|
|
4688
4765
|
(event_id, consumer_module, delivered_at, invocation_id)
|
|
@@ -5226,6 +5303,8 @@ export class SqliteScopeHost {
|
|
|
5226
5303
|
schemaVersion: r.schema_version,
|
|
5227
5304
|
verticalVersionId: r.vertical_version_id,
|
|
5228
5305
|
provisionedVersionId: r.provisioned_version_id ?? null,
|
|
5306
|
+
retirementsFinishedVersionId: r.retirements_finished_version_id ?? null,
|
|
5307
|
+
boundShapes: boundShapesOfColumn(r.bound_shapes),
|
|
5229
5308
|
migrationFailure: mapMigrationFailure(r),
|
|
5230
5309
|
forkedFrom: r.forked_from,
|
|
5231
5310
|
forkedAt: r.forked_at,
|
|
@@ -6078,32 +6157,16 @@ export class SqliteScopeHost {
|
|
|
6078
6157
|
return rows.map(mapHostname);
|
|
6079
6158
|
},
|
|
6080
6159
|
// #1706: the directory's answer to "vertical Y in tenant T", by the kernel's one rule.
|
|
6160
|
+
// Names match EXACTLY: this is the cross-vertical edges' read (the import cursor's
|
|
6161
|
+
// producer). A peer call's tenant-pinned spelling is `resolvePeerInstance`'s (#2252).
|
|
6081
6162
|
resolveVerticalInstance: async (tenantId, vertical) => {
|
|
6082
|
-
const
|
|
6083
|
-
|
|
6084
|
-
WHERE tenant_id = ? AND vertical = ?`)
|
|
6085
|
-
.all(tenantId, vertical);
|
|
6086
|
-
return resolveVerticalInstanceFrom(rows.map((r) => ({
|
|
6087
|
-
id: r.scope_id,
|
|
6088
|
-
tenantId: r.tenant_id,
|
|
6089
|
-
vertical: r.vertical,
|
|
6090
|
-
status: r.status,
|
|
6091
|
-
kind: r.kind ?? '',
|
|
6092
|
-
forkedFrom: r.forked_from ?? null,
|
|
6093
|
-
})), tenantId, vertical);
|
|
6163
|
+
const tenant = { id: tenantId, slug: null };
|
|
6164
|
+
return resolveVerticalInstanceFrom(this.peerCandidates(tenant, vertical), tenant, vertical);
|
|
6094
6165
|
},
|
|
6095
6166
|
resolvePeerInstance: async (tenantId, callerScopeId, vertical) => {
|
|
6096
6167
|
const binding = this.directory.prepare('SELECT target_scope_id, invalidated FROM peer_bindings WHERE tenant_id = ? AND caller_scope_id = ? AND vertical = ?').get(tenantId, callerScopeId, vertical);
|
|
6097
|
-
const
|
|
6098
|
-
|
|
6099
|
-
return resolvePeerInstanceFrom(rows.map((r) => ({
|
|
6100
|
-
id: r.scope_id,
|
|
6101
|
-
tenantId: r.tenant_id,
|
|
6102
|
-
vertical: r.vertical,
|
|
6103
|
-
status: r.status,
|
|
6104
|
-
kind: r.kind ?? '',
|
|
6105
|
-
forkedFrom: r.forked_from,
|
|
6106
|
-
})), tenantId, vertical, binding?.target_scope_id ?? null, binding?.invalidated === 1);
|
|
6168
|
+
const tenant = this.peerTenant(tenantId);
|
|
6169
|
+
return resolvePeerInstanceFrom(this.peerCandidates(tenant, vertical), tenant, vertical, binding?.target_scope_id ?? null, binding?.invalidated === 1);
|
|
6107
6170
|
},
|
|
6108
6171
|
peerBinding: async (actor, tenantId, callerScopeId, vertical) => {
|
|
6109
6172
|
const row = this.directory.prepare('SELECT target_scope_id, invalidated FROM peer_bindings WHERE tenant_id = ? AND caller_scope_id = ? AND vertical = ?').get(tenantId, callerScopeId, vertical);
|
|
@@ -6122,7 +6185,7 @@ export class SqliteScopeHost {
|
|
|
6122
6185
|
}
|
|
6123
6186
|
if (targetScopeId !== null) {
|
|
6124
6187
|
const target = this.directory.prepare('SELECT tenant_id, vertical, status, kind, forked_from FROM scopes WHERE scope_id = ?').get(targetScopeId);
|
|
6125
|
-
if (!target || target.tenant_id !== tenantId || target.vertical
|
|
6188
|
+
if (!target || target.tenant_id !== tenantId || !samePeerVertical(target.vertical, vertical, this.peerTenant(tenantId).slug) || target.status !== 'active' || !isPrimaryScopeRow(target)) {
|
|
6126
6189
|
throw substratError('not_found', 'target is not a live primary instance of this vertical in this tenant');
|
|
6127
6190
|
}
|
|
6128
6191
|
}
|
|
@@ -6484,6 +6547,27 @@ export class SqliteScopeHost {
|
|
|
6484
6547
|
.get(verticalSlug, channel);
|
|
6485
6548
|
const outgoing = current ? readVersion(current.version_id) : undefined;
|
|
6486
6549
|
const ack = promotionAcknowledgement.parse(acknowledge ?? {});
|
|
6550
|
+
// The owning tenant's live scopes a PRIVATE prod promote re-points — below for an embedded
|
|
6551
|
+
// vertical, after the in-place serve for a dispatch-backed one (#321) — read before
|
|
6552
|
+
// anything moves so the #2178 gate can judge each one.
|
|
6553
|
+
const owning = channel === 'prod' ? readVertical(verticalSlug) : undefined;
|
|
6554
|
+
const owned = owning && owning.ownerTenant !== null && !owning.listed
|
|
6555
|
+
? this.directory
|
|
6556
|
+
.prepare(`SELECT * FROM scopes
|
|
6557
|
+
WHERE vertical = ? AND tenant_id = ? AND status = 'active' AND forked_from IS NULL AND kind != 'preview'`)
|
|
6558
|
+
.all(verticalSlug, owning.ownerTenant)
|
|
6559
|
+
: [];
|
|
6560
|
+
// #2178, ahead of the acknowledgements below because none of them passes it. What the promote
|
|
6561
|
+
// moves something from: the channel's version, the version served in place (a prod promote
|
|
6562
|
+
// serves the incoming one next, and a failed serve leaves it behind the channel), and what each
|
|
6563
|
+
// re-pointed scope RUNS (`runningVersionOf`: on the serving script, the served version).
|
|
6564
|
+
const served = channel === 'prod' ? this.servingPointerOfVertical(verticalSlug) : null;
|
|
6565
|
+
// And what each scope it moves last reconciled (`PROVISIONED_VERSIONS_SQL`).
|
|
6566
|
+
this.assertNoStrandFrom([
|
|
6567
|
+
outgoing?.id,
|
|
6568
|
+
served?.versionId,
|
|
6569
|
+
...owned.map((s) => runningVersionOf(mapScope(s), served)),
|
|
6570
|
+
], versionId, served || owned.length > 0 ? this.provisionedVersionsOf(verticalSlug, served, owned.length > 0 ? owning.ownerTenant : null) : []);
|
|
6487
6571
|
// §4's two checkpoints, fired where the blast radius is. A FIRST promotion
|
|
6488
6572
|
// has nothing to diff against, so there is nothing to acknowledge — the
|
|
6489
6573
|
// gate is about change, not about existence.
|
|
@@ -6532,26 +6616,17 @@ export class SqliteScopeHost {
|
|
|
6532
6616
|
// adopt-then-rebind for those (serve → adopt legacy scopes → advance versions),
|
|
6533
6617
|
// so we skip the rebind here. An EMBEDDED vertical (deploymentRef null) has no
|
|
6534
6618
|
// per-version script and keeps the rebind here. See adapter-cloudflare parity.
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
.all(verticalSlug, owning.ownerTenant);
|
|
6542
|
-
for (const s of bound) {
|
|
6543
|
-
if (s.vertical_version_id === versionId)
|
|
6544
|
-
continue;
|
|
6545
|
-
const prev = s.vertical_version_id ? readVersion(s.vertical_version_id) : undefined;
|
|
6546
|
-
if (prev && prev.migrationDigest !== incoming.migrationDigest) {
|
|
6547
|
-
await this.snapshotScope(actor, s.tenant_id, s.scope_id);
|
|
6548
|
-
}
|
|
6549
|
-
this.directory
|
|
6550
|
-
.prepare('UPDATE scopes SET vertical_version_id = ?, vertical = ? WHERE scope_id = ?')
|
|
6551
|
-
.run(versionId, verticalSlug, s.scope_id);
|
|
6552
|
-
this.recordAdmin(actor, 'bindScopeVersion', { tenantId: s.tenant_id, scopeId: s.scope_id }, prev ? { versionId: prev.id, version: prev.version } : null, { versionId, vertical: verticalSlug, version: incoming.version, via: 'promoteVersion' });
|
|
6553
|
-
}
|
|
6619
|
+
for (const s of incoming.deploymentRef ? [] : owned) {
|
|
6620
|
+
if (s.vertical_version_id === versionId)
|
|
6621
|
+
continue;
|
|
6622
|
+
const prev = s.vertical_version_id ? readVersion(s.vertical_version_id) : undefined;
|
|
6623
|
+
if (prev && prev.migrationDigest !== incoming.migrationDigest) {
|
|
6624
|
+
await this.snapshotScope(actor, s.tenant_id, s.scope_id);
|
|
6554
6625
|
}
|
|
6626
|
+
this.directory
|
|
6627
|
+
.prepare('UPDATE scopes SET vertical_version_id = ?, vertical = ? WHERE scope_id = ?')
|
|
6628
|
+
.run(versionId, verticalSlug, s.scope_id);
|
|
6629
|
+
this.recordAdmin(actor, 'bindScopeVersion', { tenantId: s.tenant_id, scopeId: s.scope_id }, prev ? { versionId: prev.id, version: prev.version } : null, { versionId, vertical: verticalSlug, version: incoming.version, via: 'promoteVersion' });
|
|
6555
6630
|
}
|
|
6556
6631
|
// The acknowledgement is recorded, not just enforced: that is what turns
|
|
6557
6632
|
// "someone reviewed the permission change" into evidence.
|
|
@@ -6607,6 +6682,9 @@ export class SqliteScopeHost {
|
|
|
6607
6682
|
},
|
|
6608
6683
|
bindingImpact: async (actor, tenantId, scopeId, versionId, opts) => {
|
|
6609
6684
|
const { v, scope } = bindTarget(tenantId, scopeId, versionId);
|
|
6685
|
+
// #2178: a move no acknowledgement passes is refused here too, so a caller asking first
|
|
6686
|
+
// (an adopt, an Update) stops before anything is copied.
|
|
6687
|
+
this.assertBindingShapesKept(mapScope(scope), versionId, opts?.servingRef);
|
|
6610
6688
|
const breaks = await this.bindBreaks(actor, mapScope(scope), v, opts?.servingRef);
|
|
6611
6689
|
this.recordAccess(actor, 'bindingImpact', { tenantId, scopeId }, { versionId, ...opts }, breaks.length);
|
|
6612
6690
|
return breaks;
|
|
@@ -6622,6 +6700,8 @@ export class SqliteScopeHost {
|
|
|
6622
6700
|
throw substratError('precondition_failed', 'scope erasure changed; reload the scope and retry');
|
|
6623
6701
|
}
|
|
6624
6702
|
const ack = bindAcknowledgement.parse(opts?.acknowledge ?? {});
|
|
6703
|
+
// #2178 first: no acknowledgement passes it, so none should be asked for ahead of it.
|
|
6704
|
+
this.assertBindingShapesKept(mapScope(scope), versionId);
|
|
6625
6705
|
// #1756: an export an app in this tenant imports, dropped or re-versioned by what this
|
|
6626
6706
|
// scope would run. Before the snapshot, so a refused bind leaves nothing behind.
|
|
6627
6707
|
if (!ack.exportBreak) {
|
|
@@ -6753,17 +6833,25 @@ export class SqliteScopeHost {
|
|
|
6753
6833
|
* Record that this scope's provision has now run against `versionId` (#1172).
|
|
6754
6834
|
* Written only after a provision or reconcile succeeded — a receipt, never a hope.
|
|
6755
6835
|
*/
|
|
6756
|
-
markScopeProvisioned: async (actor, tenantId, scopeId, versionId) => {
|
|
6836
|
+
markScopeProvisioned: async (actor, tenantId, scopeId, versionId, outcome) => {
|
|
6757
6837
|
const scope = this.directory
|
|
6758
6838
|
.prepare('SELECT tenant_id FROM scopes WHERE scope_id = ?')
|
|
6759
6839
|
.get(scopeId);
|
|
6760
6840
|
if (!scope || scope.tenant_id !== tenantId) {
|
|
6761
6841
|
throw substratError('not_found', `unknown scope ${scopeId} in tenant ${tenantId}`);
|
|
6762
6842
|
}
|
|
6843
|
+
// #2185: both in one write — the retirements count as finished only for the version
|
|
6844
|
+
// this receipt names, and a receipt written without that answer clears them.
|
|
6845
|
+
const finished = versionId !== null && outcome?.retirementsFinished === true ? versionId : null;
|
|
6846
|
+
// #2184: and the binding shapes it reported bound, paired with this receipt's version.
|
|
6763
6847
|
this.directory
|
|
6764
|
-
.prepare('UPDATE scopes SET provisioned_version_id = ? WHERE scope_id = ?')
|
|
6765
|
-
.run(versionId, scopeId);
|
|
6766
|
-
this.recordAdmin(actor, 'markScopeProvisioned', { tenantId, scopeId }, null, {
|
|
6848
|
+
.prepare('UPDATE scopes SET provisioned_version_id = ?, retirements_finished_version_id = ?, bound_shapes = ? WHERE scope_id = ?')
|
|
6849
|
+
.run(versionId, finished, boundShapesColumn(versionId, outcome?.boundShapes), scopeId);
|
|
6850
|
+
this.recordAdmin(actor, 'markScopeProvisioned', { tenantId, scopeId }, null, {
|
|
6851
|
+
versionId,
|
|
6852
|
+
...(finished ? { retirementsFinished: true } : {}),
|
|
6853
|
+
...(versionId !== null && outcome?.boundShapes ? { boundShapes: outcome.boundShapes } : {}),
|
|
6854
|
+
});
|
|
6767
6855
|
},
|
|
6768
6856
|
verticalServing: async (actor, verticalSlug) => {
|
|
6769
6857
|
const r = this.directory
|
|
@@ -6788,6 +6876,8 @@ export class SqliteScopeHost {
|
|
|
6788
6876
|
.get(verticalSlug);
|
|
6789
6877
|
if (!r)
|
|
6790
6878
|
throw substratError('not_found', `unknown vertical '${verticalSlug}'`);
|
|
6879
|
+
// #2178: every install on the serving script runs the new version from this moment.
|
|
6880
|
+
this.assertNoStrandFrom([r.serving_version_id], parsed.versionId, r.serving_ref ? this.provisionedVersionsOf(verticalSlug, r.serving_version_id ? { ref: r.serving_ref, versionId: r.serving_version_id } : null, null) : []);
|
|
6791
6881
|
this.directory
|
|
6792
6882
|
.prepare(`UPDATE verticals SET serving_ref = ?, serving_version_id = ?,
|
|
6793
6883
|
serving_do_classes = ?, serving_migration_tag = ? WHERE slug = ?`)
|
|
@@ -6821,6 +6911,8 @@ export class SqliteScopeHost {
|
|
|
6821
6911
|
// #1756: onto (or off) a serving script, the scope runs other code before its pointer
|
|
6822
6912
|
// moves. Judged here, so no caller that routes first and binds second passes unasked.
|
|
6823
6913
|
const bound = scope.vertical_version_id ? readVersion(scope.vertical_version_id) : undefined;
|
|
6914
|
+
if (bound)
|
|
6915
|
+
this.assertBindingShapesKept(mapScope(scope), bound.id, servingRef);
|
|
6824
6916
|
if (!ack.exportBreak && bound) {
|
|
6825
6917
|
const breaks = await this.bindBreaks(actor, mapScope(scope), bound, servingRef);
|
|
6826
6918
|
if (breaks.length > 0)
|
|
@@ -9112,6 +9204,11 @@ export class SqliteScopeHost {
|
|
|
9112
9204
|
// #1172: which version this scope's provision hook last ran against. Null on every
|
|
9113
9205
|
// existing row, which reads as "unknown, reconcile once" rather than "up to date".
|
|
9114
9206
|
this.ensureColumn(this.directory, 'scopes', 'provisioned_version_id', 'provisioned_version_id TEXT');
|
|
9207
|
+
// #2185: the version whose reconcile finished its retirements here. Null on every existing
|
|
9208
|
+
// row, which reads as "not finished": the sweep reconciles each such install once more.
|
|
9209
|
+
this.ensureColumn(this.directory, 'scopes', 'retirements_finished_version_id', 'retirements_finished_version_id TEXT');
|
|
9210
|
+
// #2184: null on every existing row, which reads as unknown (the sweep reconciles it once more).
|
|
9211
|
+
this.ensureColumn(this.directory, 'scopes', 'bound_shapes', 'bound_shapes TEXT');
|
|
9115
9212
|
this.ensureColumn(this.directory, 'scopes', 'erasure_epoch', 'erasure_epoch INTEGER');
|
|
9116
9213
|
this.ensureColumn(this.directory, 'scopes', 'reap_claimed_at', 'reap_claimed_at TEXT');
|
|
9117
9214
|
// §4.8's grace-window timestamp on tenants (mirrors scopes' archived_at).
|
|
@@ -9421,6 +9518,8 @@ export class SqliteScopeHost {
|
|
|
9421
9518
|
// envelope data module code could set or branch on.
|
|
9422
9519
|
rt.invocationId, full.payload === undefined ? null : JSON.stringify(full.payload));
|
|
9423
9520
|
};
|
|
9521
|
+
// #2192: the call's invocation id, one reader for `ctx.log` and `ctx.requestId`.
|
|
9522
|
+
const requestIdNow = () => rt.invocationId ?? delivery?.invocationId ?? null;
|
|
9424
9523
|
const ctxRef = {
|
|
9425
9524
|
tenantId: rt.tenantId,
|
|
9426
9525
|
scopeId: rt.scopeId,
|
|
@@ -9440,13 +9539,17 @@ export class SqliteScopeHost {
|
|
|
9440
9539
|
tenantId: rt.tenantId,
|
|
9441
9540
|
scopeId: rt.scopeId,
|
|
9442
9541
|
operation: operation ?? null,
|
|
9443
|
-
invocationId:
|
|
9542
|
+
invocationId: requestIdNow,
|
|
9444
9543
|
// A consumer runs under the system override, so it logs as `system`.
|
|
9445
9544
|
principalKind: overrideActor ? 'system' : subject.kind,
|
|
9446
9545
|
// The string-safe redaction: `redactSecrets` parses its serialization back, and a
|
|
9447
9546
|
// log's text is not JSON.
|
|
9448
9547
|
redact: (text) => redactSecretText(text, minted),
|
|
9449
9548
|
}, this.logSink),
|
|
9549
|
+
// #2192: the same id the log lines carry.
|
|
9550
|
+
get requestId() {
|
|
9551
|
+
return requestIdNow();
|
|
9552
|
+
},
|
|
9450
9553
|
emit: (event) => writeEvent(event, 'module'),
|
|
9451
9554
|
requestPlatform: (request) => {
|
|
9452
9555
|
assertImpersonationWrites(impersonation, 'ctx.requestPlatform');
|