@substrat-run/adapter-sqlite 0.112.0 → 0.114.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/index.d.ts.map +1 -1
- package/dist/index.js +224 -122
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAcL,KAAK,eAAe,EAuDpB,KAAK,YAAY,EAGjB,KAAK,QAAQ,EAWb,KAAK,WAAW,EAIhB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAI3B,KAAK,wBAAwB,EAW7B,KAAK,eAAe,EACpB,KAAK,WAAW,EAehB,KAAK,SAAS,EAEd,KAAK,OAAO,EAYZ,KAAK,sBAAsB,EAG3B,KAAK,QAAQ,EAGb,KAAK,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import Database from 'better-sqlite3';
|
|
4
|
-
import { accessLogEntry, adminLogEntry, opsFailureEntry, opsFailureFingerprint, issueEntry, FRESHNESS_HEARTBEAT_MINUTES, sweepRunEntry, modelUsageEntry, ATTACHMENT_ADDED, ATTACHMENT_REMOVED, attachmentRecord, createTenantInput, domainEvent, domainEventInput, entitlementGrant, entitlementGrantInput, eventId, meterReading, platformRequestInput, platformRequestId, platformRequest, MAX_PENDING_PLATFORM_REQUESTS, identityLink, identityPool, instant, connection, capabilityGrant, connectionGrant, connectionGrantRecord, connectionSecret, systemGrant, subjectRef, requestFingerprint, createConnectionInput, projectedConnectionGrant, projectedConnectionKey, moduleManifest, createOrgInput, promotionAcknowledgement, bindHostnameInput, channelHistoryEntry, hostnameBinding, publishVersionInput, AUTO_ADMISSION_NOTE, routeTarget, registerVerticalInput, vertical as verticalSchema, verticalChannel, verticalVersion, objectRef, grantRefFromProof, org as orgSchema, orgMembership, principalId, resolvedIdentity, roleDefinition, scope as scopeSchema, tenant as tenantSchema, tenantRole, subjectShredReceipt, SCOPE_TABLE_PAGE_DEFAULT, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, verticalServingState, outboundOfManifestJson, listLimitOf, substratError, assertReplayableDump, delegatedReadRecord, } from '@substrat-run/contracts';
|
|
5
|
-
import { asPrincipal, assertAllowed, assertReadOnlyQuery, attachmentBlobKey, entitlementDenial, foldMeterReading, guardSpine, parseValidationRecords, resolveScopeRecord, ulid, ISSUE_RETENTION_DAYS, OPS_FAILURE_RETENTION_DAYS, SWEEP_RUN_RETENTION_DAYS, MODEL_USAGE_RETENTION_DAYS, foldModelUsage, backoffAt, platformRequestHistoryQuery, PLATFORM_REQUEST_COLUMNS, denialListQuery, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, IMPERSONATION_COLUMNS, IMPERSONATION_DDL, ImpersonationRefused, assertImpersonationWrites, assertModuleEnqueueableKind, assertSessionUsable, impersonationByIdQuery, impersonationListQuery, impersonationRowValues, impersonationStampOf, mapImpersonationRow, newImpersonationSession, resolveRetryPolicy, isSecretBoxConfigured, unconfiguredSecretBox, createSubjectKeys, generateSealingKeyPair, noSealingKeyMessage, openSealed, sealTo, ConnectionSealingKeyUnavailableError, PermissionDenied, createAtomic, NotSearchable, NotListable, listIndexMigrations, listIndexPlans, listQuery, cursorOf, isSearchIndexTable, searchIndexDdl, searchIndexMigrations, searchIndexPlans, searchLimit, searchMatchExpression, searchQuery, entityVersionQuery, entityVersionOf, assertIfMatch, OUTBOX_ENTITY_INDEX, IDEMPOTENCY_DDL, assertIdempotencyKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, globalFetch, createUlid, facetEvents, readHistory, walkEventCause, walkEventEffects, } from '@substrat-run/kernel';
|
|
4
|
+
import { accessLogEntry, adminLogEntry, opsFailureEntry, opsFailureFingerprint, issueEntry, FRESHNESS_HEARTBEAT_MINUTES, sweepRunEntry, modelUsageEntry, ATTACHMENT_ADDED, ATTACHMENT_REMOVED, attachmentRecord, createTenantInput, domainEvent, domainEventInput, entitlementGrant, entitlementGrantInput, eventId, meterReading, platformRequestInput, platformRequestId, platformRequest, MAX_PENDING_PLATFORM_REQUESTS, identityLink, identityPool, instant, connection, capabilityGrant, connectionGrant, connectionGrantRecord, connectionSecret, systemGrant, subjectRef, requestFingerprint, createConnectionInput, projectedConnectionGrant, projectedConnectionKey, moduleManifest, createOrgInput, promotionAcknowledgement, bindHostnameInput, channelHistoryEntry, hostnameBinding, publishVersionInput, AUTO_ADMISSION_NOTE, routeTarget, registerVerticalInput, vertical as verticalSchema, verticalChannel, verticalVersion, objectRef, grantRefFromProof, org as orgSchema, orgMembership, principalId, resolvedIdentity, roleDefinition, scope as scopeSchema, tenant as tenantSchema, tenantRole, subjectShredReceipt, SCOPE_TABLE_PAGE_DEFAULT, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, verticalServingState, outboundOfManifestJson, listLimitOf, substratError, assertReplayableDump, delegatedReadRecord, redrainEventsInput, REDRAIN_BATCH, } from '@substrat-run/contracts';
|
|
5
|
+
import { asPrincipal, assertAllowed, assertReadOnlyQuery, attachmentBlobKey, entitlementDenial, foldMeterReading, guardSpine, parseValidationRecords, resolveScopeRecord, ulid, ISSUE_RETENTION_DAYS, OPS_FAILURE_RETENTION_DAYS, SWEEP_RUN_RETENTION_DAYS, MODEL_USAGE_RETENTION_DAYS, foldModelUsage, backoffAt, platformRequestHistoryQuery, PLATFORM_REQUEST_COLUMNS, denialListQuery, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, IMPERSONATION_COLUMNS, IMPERSONATION_DDL, ImpersonationRefused, assertImpersonationWrites, assertModuleEnqueueableKind, assertSessionUsable, impersonationByIdQuery, impersonationListQuery, impersonationRowValues, impersonationStampOf, mapImpersonationRow, newImpersonationSession, resolveRetryPolicy, isSecretBoxConfigured, unconfiguredSecretBox, createSubjectKeys, generateSealingKeyPair, noSealingKeyMessage, openSealed, sealTo, ConnectionSealingKeyUnavailableError, PermissionDenied, createAtomic, NotSearchable, NotListable, listIndexMigrations, listIndexPlans, listQuery, cursorOf, isSearchIndexTable, searchIndexDdl, searchIndexMigrations, searchIndexPlans, searchLimit, searchMatchExpression, searchQuery, entityVersionQuery, entityVersionOf, assertIfMatch, OUTBOX_ENTITY_INDEX, IDEMPOTENCY_DDL, assertIdempotencyKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, globalFetch, createUlid, facetEvents, readHistory, walkEventCause, walkEventEffects, assertRedrainWindow, readInvocation, readDeadLetters, } from '@substrat-run/kernel';
|
|
6
6
|
import { ScopeActor } from './actor.js';
|
|
7
7
|
import { createTupleChecker } from './checker.js';
|
|
8
8
|
/**
|
|
@@ -62,6 +62,12 @@ const KERNEL_DDL = `
|
|
|
62
62
|
-- nothing was being delivered (an operation emitted it directly), or the row
|
|
63
63
|
-- predates the column.
|
|
64
64
|
caused_by TEXT,
|
|
65
|
+
-- #1237: the INVOCATION this event belongs to, minted by the transport and carried
|
|
66
|
+
-- on InvokeOptions. The spine could say what caused an event and which operation
|
|
67
|
+
-- emitted it, and still not say which two events came from the same call — the
|
|
68
|
+
-- runtime's request id is stamped by the log platform at ingestion, so no vertical
|
|
69
|
+
-- code can read it. NULL = the transport minted none, or the row predates the column.
|
|
70
|
+
invocation_id TEXT,
|
|
65
71
|
drained_at TEXT
|
|
66
72
|
);
|
|
67
73
|
-- #1232: the freshness evaluator's read — MAX(occurred_at) per type, every pass,
|
|
@@ -2359,130 +2365,146 @@ export class SqliteScopeHost {
|
|
|
2359
2365
|
// about the response, and the response is not one until it is returned.
|
|
2360
2366
|
let replayed = false;
|
|
2361
2367
|
const invoked = await rt.actor.enqueue(async () => {
|
|
2362
|
-
//
|
|
2363
|
-
//
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
//
|
|
2367
|
-
|
|
2368
|
-
// transaction, and before the guards, so a K-17 pre-condition reads the
|
|
2369
|
-
// same typed input the handler will.
|
|
2370
|
-
//
|
|
2371
|
-
// Every caller passes here: the HTTP mount, a scenario test, a seed, a
|
|
2372
|
-
// schedule. That is the point — parsing at the mount alone would leave
|
|
2373
|
-
// the demos' own suites exercising an unparsed path, and the wire is
|
|
2374
|
-
// where the untrusted input actually arrives.
|
|
2375
|
-
const parsed = this.operationInput.has(operation)
|
|
2376
|
-
? this.operationInput.get(operation).parse(clonedInput)
|
|
2377
|
-
: clonedInput;
|
|
2378
|
-
// #116: fingerprinted from the PARSED input, so a retry that omits an
|
|
2379
|
-
// optional field the original sent at its default value is still the
|
|
2380
|
-
// same request. Computed before `BEGIN` — it is a pure hash of what the
|
|
2381
|
-
// caller sent and has no business inside a transaction.
|
|
2382
|
-
const fingerprint = idempotencyKey === undefined
|
|
2383
|
-
? undefined
|
|
2384
|
-
: await requestFingerprint(operation, parsed);
|
|
2385
|
-
rt.db.exec('BEGIN IMMEDIATE');
|
|
2386
|
-
let result;
|
|
2368
|
+
// #1237: the invocation this call belongs to, for the duration of it. Set
|
|
2369
|
+
// INSIDE the actor task — the actor serializes invoke and dispatch alike, so
|
|
2370
|
+
// nothing else in this scope runs between here and the clear below. Cleared in
|
|
2371
|
+
// `finally` so a later consumer carries none rather than the last caller's,
|
|
2372
|
+
// which is the leak `causedBy` had to be moved off the host to avoid.
|
|
2373
|
+
rt.invocationId = invokeOptions?.invocationId ?? null;
|
|
2387
2374
|
try {
|
|
2388
|
-
//
|
|
2389
|
-
//
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
//
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2375
|
+
// Fresh per operation: the context carries the K-34 authorization accumulator,
|
|
2376
|
+
// which must not leak across operations (invokes are serialized per scope).
|
|
2377
|
+
const ctx = this.operationContext(rt, subject, undefined, signals, session, operation);
|
|
2378
|
+
const clonedInput = structuredClone(input);
|
|
2379
|
+
// #893: parse, don't trust — at the scope door, from the operation's own
|
|
2380
|
+
// declaration. BEFORE `BEGIN`, so a malformed call never opens a
|
|
2381
|
+
// transaction, and before the guards, so a K-17 pre-condition reads the
|
|
2382
|
+
// same typed input the handler will.
|
|
2383
|
+
//
|
|
2384
|
+
// Every caller passes here: the HTTP mount, a scenario test, a seed, a
|
|
2385
|
+
// schedule. That is the point — parsing at the mount alone would leave
|
|
2386
|
+
// the demos' own suites exercising an unparsed path, and the wire is
|
|
2387
|
+
// where the untrusted input actually arrives.
|
|
2388
|
+
const parsed = this.operationInput.has(operation)
|
|
2389
|
+
? this.operationInput.get(operation).parse(clonedInput)
|
|
2390
|
+
: clonedInput;
|
|
2391
|
+
// #116: fingerprinted from the PARSED input, so a retry that omits an
|
|
2392
|
+
// optional field the original sent at its default value is still the
|
|
2393
|
+
// same request. Computed before `BEGIN` — it is a pure hash of what the
|
|
2394
|
+
// caller sent and has no business inside a transaction.
|
|
2395
|
+
const fingerprint = idempotencyKey === undefined
|
|
2396
|
+
? undefined
|
|
2397
|
+
: await requestFingerprint(operation, parsed);
|
|
2398
|
+
rt.db.exec('BEGIN IMMEDIATE');
|
|
2399
|
+
let result;
|
|
2400
|
+
try {
|
|
2401
|
+
// #116: a retry is answered from the recording, and nothing else runs
|
|
2402
|
+
// — not the guards, not the handler, not the permission check inside
|
|
2403
|
+
// it. The row is keyed by SUBJECT, so a caller can only ever reach a
|
|
2404
|
+
// response it received itself; `idempotency.ts` states what that does
|
|
2405
|
+
// and does not promise about authorization.
|
|
2406
|
+
if (idempotencyKey !== undefined && fingerprint !== undefined) {
|
|
2407
|
+
const lookup = idempotencyLookupQuery(subject, idempotencyKey);
|
|
2408
|
+
const prior = rt.db.prepare(lookup.sql).all(...lookup.params)[0];
|
|
2409
|
+
if (prior) {
|
|
2410
|
+
const replay = replayFor(idempotencyKey, fingerprint, prior);
|
|
2411
|
+
rt.db.exec('COMMIT');
|
|
2412
|
+
// Only a guarded operation may report a tag (#129); an unguarded
|
|
2413
|
+
// one recorded null and must stay silent rather than announce it.
|
|
2414
|
+
if (guarded)
|
|
2415
|
+
committedVersion = replay.entityVersion;
|
|
2416
|
+
replayed = true;
|
|
2417
|
+
return replay.result;
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
// #129, and the SPLIT here is the whole subtlety: the version is
|
|
2421
|
+
// snapshotted before the handler, and compared after it.
|
|
2422
|
+
//
|
|
2423
|
+
// **Read before**, because the handler's own `emit` moves the version —
|
|
2424
|
+
// comparing afterwards against a value the operation itself just
|
|
2425
|
+
// changed would refuse every write. Inside the transaction, because a
|
|
2426
|
+
// version read outside the write's own transaction is
|
|
2427
|
+
// time-of-check/time-of-use: the value can move between the comparison
|
|
2428
|
+
// and the commit, and the write lands anyway.
|
|
2429
|
+
//
|
|
2430
|
+
// **Compare after**, because the permission check lives INSIDE the
|
|
2431
|
+
// handler (`assertAllowed(await ctx.check(…))`) and must be what
|
|
2432
|
+
// answers first. Refusing on the version before the handler runs makes
|
|
2433
|
+
// this operation a version oracle: a principal with no permission on
|
|
2434
|
+
// the entity sends `If-Match: *` and learns whether it exists, or sends
|
|
2435
|
+
// a tag and learns whether it has changed. Found by driving Callout's
|
|
2436
|
+
// two-dispatcher scenario over HTTP with the wrong principal, which
|
|
2437
|
+
// answered 412 where it owed 403.
|
|
2438
|
+
//
|
|
2439
|
+
// The cost is a refused write having done the handler's work first. It
|
|
2440
|
+
// is discarded with everything else — same transaction, same rollback —
|
|
2441
|
+
// so the only loss is effort on a path that was already failing.
|
|
2442
|
+
const ref = guarded ? this.concurrencyRef(operation, guarded, parsed) : undefined;
|
|
2443
|
+
const seen = ref && invokeOptions?.ifMatch !== undefined ? this.versionAt(rt, ref) : undefined;
|
|
2444
|
+
// Manifest guards (K-17): pre-conditions, inside the operation's own
|
|
2445
|
+
// transaction, before the handler. A throw here blocks the operation
|
|
2446
|
+
// and rolls back exactly like a handler throw — fail closed.
|
|
2447
|
+
await this.runGuards(operation, ctx, parsed);
|
|
2448
|
+
result = await handler(ctx, parsed);
|
|
2449
|
+
if (ref && invokeOptions?.ifMatch !== undefined) {
|
|
2450
|
+
assertIfMatch(ref, invokeOptions.ifMatch, seen ?? null);
|
|
2451
|
+
}
|
|
2452
|
+
// The tag the caller gets back describes the row as THIS write left
|
|
2453
|
+
// it, so it is read after the handler and still under `BEGIN`.
|
|
2454
|
+
if (ref)
|
|
2455
|
+
committedVersion = this.versionAt(rt, ref);
|
|
2456
|
+
// #116: recorded INSIDE the transaction, which is what makes a failed
|
|
2457
|
+
// request retried rather than replayed — the operation threw, this row
|
|
2458
|
+
// rolls back with the writes it describes, and there is nothing for the
|
|
2459
|
+
// retry to find. The prune rides along: bounded work, on the only path
|
|
2460
|
+
// that adds a row.
|
|
2461
|
+
if (idempotencyKey !== undefined && fingerprint !== undefined) {
|
|
2462
|
+
const at = this.clock();
|
|
2463
|
+
const record = idempotencyRecordStatement(subject, idempotencyKey, operation, fingerprint, result, committedVersion ?? null, at);
|
|
2464
|
+
rt.db.prepare(record.sql).run(...record.params);
|
|
2465
|
+
const prune = idempotencyPruneStatement(at);
|
|
2466
|
+
rt.db.prepare(prune.sql).run(...prune.params);
|
|
2405
2467
|
}
|
|
2468
|
+
// K-42: a read-only session's transaction is ROLLED BACK, never
|
|
2469
|
+
// committed — the half of read-only that is a mechanism rather than a
|
|
2470
|
+
// promise. `ctx.emit` and the other effecting verbs refuse outright,
|
|
2471
|
+
// which is what a support engineer sees; this is what holds when a
|
|
2472
|
+
// handler writes a row with plain `ctx.sql.exec` and calls none of
|
|
2473
|
+
// them. Same shape as the scope console's read-only query on the DO.
|
|
2474
|
+
if (session?.mode === 'read-only')
|
|
2475
|
+
rt.db.exec('ROLLBACK');
|
|
2476
|
+
else
|
|
2477
|
+
rt.db.exec('COMMIT');
|
|
2406
2478
|
}
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
// time-of-check/time-of-use: the value can move between the comparison
|
|
2415
|
-
// and the commit, and the write lands anyway.
|
|
2416
|
-
//
|
|
2417
|
-
// **Compare after**, because the permission check lives INSIDE the
|
|
2418
|
-
// handler (`assertAllowed(await ctx.check(…))`) and must be what
|
|
2419
|
-
// answers first. Refusing on the version before the handler runs makes
|
|
2420
|
-
// this operation a version oracle: a principal with no permission on
|
|
2421
|
-
// the entity sends `If-Match: *` and learns whether it exists, or sends
|
|
2422
|
-
// a tag and learns whether it has changed. Found by driving Callout's
|
|
2423
|
-
// two-dispatcher scenario over HTTP with the wrong principal, which
|
|
2424
|
-
// answered 412 where it owed 403.
|
|
2425
|
-
//
|
|
2426
|
-
// The cost is a refused write having done the handler's work first. It
|
|
2427
|
-
// is discarded with everything else — same transaction, same rollback —
|
|
2428
|
-
// so the only loss is effort on a path that was already failing.
|
|
2429
|
-
const ref = guarded ? this.concurrencyRef(operation, guarded, parsed) : undefined;
|
|
2430
|
-
const seen = ref && invokeOptions?.ifMatch !== undefined ? this.versionAt(rt, ref) : undefined;
|
|
2431
|
-
// Manifest guards (K-17): pre-conditions, inside the operation's own
|
|
2432
|
-
// transaction, before the handler. A throw here blocks the operation
|
|
2433
|
-
// and rolls back exactly like a handler throw — fail closed.
|
|
2434
|
-
await this.runGuards(operation, ctx, parsed);
|
|
2435
|
-
result = await handler(ctx, parsed);
|
|
2436
|
-
if (ref && invokeOptions?.ifMatch !== undefined) {
|
|
2437
|
-
assertIfMatch(ref, invokeOptions.ifMatch, seen ?? null);
|
|
2479
|
+
catch (err) {
|
|
2480
|
+
rt.db.exec('ROLLBACK');
|
|
2481
|
+
// K-35: a refused check rolled the operation back. Record it now — a fresh
|
|
2482
|
+
// statement in autocommit, AFTER the rollback, so the denial survives it.
|
|
2483
|
+
if (err instanceof PermissionDenied)
|
|
2484
|
+
this.recordDenial(rt, subject, operation, err, session);
|
|
2485
|
+
throw err;
|
|
2438
2486
|
}
|
|
2439
|
-
//
|
|
2440
|
-
//
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
//
|
|
2444
|
-
//
|
|
2445
|
-
//
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
const at = this.clock();
|
|
2450
|
-
const record = idempotencyRecordStatement(subject, idempotencyKey, operation, fingerprint, result, committedVersion ?? null, at);
|
|
2451
|
-
rt.db.prepare(record.sql).run(...record.params);
|
|
2452
|
-
const prune = idempotencyPruneStatement(at);
|
|
2453
|
-
rt.db.prepare(prune.sql).run(...prune.params);
|
|
2487
|
+
// Post-commit, still inside the actor task: drain outbox → consumers,
|
|
2488
|
+
// then → executors. Prompt dispatch (K-22 §4.2): the common case
|
|
2489
|
+
// completes inside this request, with the outbox as the retry backstop
|
|
2490
|
+
// if it does not.
|
|
2491
|
+
// A read-only session committed nothing, so there is nothing of its to
|
|
2492
|
+
// drain — and draining anyway would run consumers as a side effect of a
|
|
2493
|
+
// session that may not have side effects.
|
|
2494
|
+
if (session?.mode !== 'read-only') {
|
|
2495
|
+
await this.dispatch(rt);
|
|
2496
|
+
await this.dispatchExecutors(rt);
|
|
2454
2497
|
}
|
|
2455
|
-
|
|
2456
|
-
// committed — the half of read-only that is a mechanism rather than a
|
|
2457
|
-
// promise. `ctx.emit` and the other effecting verbs refuse outright,
|
|
2458
|
-
// which is what a support engineer sees; this is what holds when a
|
|
2459
|
-
// handler writes a row with plain `ctx.sql.exec` and calls none of
|
|
2460
|
-
// them. Same shape as the scope console's read-only query on the DO.
|
|
2461
|
-
if (session?.mode === 'read-only')
|
|
2462
|
-
rt.db.exec('ROLLBACK');
|
|
2463
|
-
else
|
|
2464
|
-
rt.db.exec('COMMIT');
|
|
2465
|
-
}
|
|
2466
|
-
catch (err) {
|
|
2467
|
-
rt.db.exec('ROLLBACK');
|
|
2468
|
-
// K-35: a refused check rolled the operation back. Record it now — a fresh
|
|
2469
|
-
// statement in autocommit, AFTER the rollback, so the denial survives it.
|
|
2470
|
-
if (err instanceof PermissionDenied)
|
|
2471
|
-
this.recordDenial(rt, subject, operation, err, session);
|
|
2472
|
-
throw err;
|
|
2498
|
+
return structuredClone(result);
|
|
2473
2499
|
}
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
if (session?.mode !== 'read-only') {
|
|
2482
|
-
await this.dispatch(rt);
|
|
2483
|
-
await this.dispatchExecutors(rt);
|
|
2500
|
+
finally {
|
|
2501
|
+
// Cleared on BOTH paths, inside the task. The actor runs one task at a
|
|
2502
|
+
// time, so a value left set here would be read by whichever task ran next
|
|
2503
|
+
// and did not set its own — a connector drain, say — stamping its events
|
|
2504
|
+
// with a call they had nothing to do with. The same leak `causedBy` was
|
|
2505
|
+
// moved off the host to avoid.
|
|
2506
|
+
rt.invocationId = null;
|
|
2484
2507
|
}
|
|
2485
|
-
return structuredClone(result);
|
|
2486
2508
|
});
|
|
2487
2509
|
if (signals.platformRequests > 0)
|
|
2488
2510
|
options?.onPlatformRequests?.(signals.platformRequests);
|
|
@@ -4197,6 +4219,10 @@ export class SqliteScopeHost {
|
|
|
4197
4219
|
// exists on the outbox but not on the envelope `parseOutboxRow` returns,
|
|
4198
4220
|
// whose `domainEvent.parse` strips anything it does not declare.
|
|
4199
4221
|
causedBy: r.caused_by ?? null,
|
|
4222
|
+
// Same lift, same reason. `drainedEvent` requires this field, and the `as never`
|
|
4223
|
+
// below is what let it be missed: without it a SQLite-backed lake loses the
|
|
4224
|
+
// call grouping permanently, while a Cloudflare-backed one keeps it.
|
|
4225
|
+
invocationId: r.invocation_id ?? null,
|
|
4200
4226
|
}));
|
|
4201
4227
|
},
|
|
4202
4228
|
markEventsDrained: async (actor, tenantId, scopeId, eventIds) => {
|
|
@@ -4235,6 +4261,48 @@ export class SqliteScopeHost {
|
|
|
4235
4261
|
}
|
|
4236
4262
|
return drained;
|
|
4237
4263
|
},
|
|
4264
|
+
redrainEvents: async (actor, tenantId, scopeId, input) => {
|
|
4265
|
+
// Directory check first, on mark's reasoning: "nothing to reopen" and "you may not
|
|
4266
|
+
// address this scope" are different answers.
|
|
4267
|
+
this.assertScopeReachable(tenantId, scopeId);
|
|
4268
|
+
const { drainedBefore } = redrainEventsInput.parse(input);
|
|
4269
|
+
// The window rule, enforced HERE and not only at the control-plane door: this verb
|
|
4270
|
+
// is public `HostAdmin`, so the fleet script reaches it without passing that route.
|
|
4271
|
+
// The injected clock, so a test pins the boundary rather than racing it.
|
|
4272
|
+
assertRedrainWindow(drainedBefore, this.clock());
|
|
4273
|
+
const db = this.scopeDbFor(tenantId, scopeId);
|
|
4274
|
+
// Audit FIRST, matching the Cloudflare adapter and `rewindScope` (K-33): the row is
|
|
4275
|
+
// a separate statement outside the update's transaction, so a failure between them
|
|
4276
|
+
// would leave a reopen with no receipt — and the retry cannot repair it, because the
|
|
4277
|
+
// stamps are already clear and a second call returns 0. The outcome row below still
|
|
4278
|
+
// records how much moved.
|
|
4279
|
+
this.recordAdmin(actor, 'redrainEvents', { tenantId, scopeId }, null, {
|
|
4280
|
+
intent: 'redrain',
|
|
4281
|
+
drainedBefore,
|
|
4282
|
+
});
|
|
4283
|
+
// `drained_at` is ISO 8601 text written by `toISOString()` on both adapters — one
|
|
4284
|
+
// fixed-width UTC format — so a string comparison is a time comparison. STRICTLY
|
|
4285
|
+
// before: a row stamped at exactly `drainedBefore` went to the new table.
|
|
4286
|
+
//
|
|
4287
|
+
// BOUNDED, and the caller loops until this returns 0 (`REDRAIN_BATCH`). The outbox
|
|
4288
|
+
// is never pruned, so the unbounded form scales with the scope's whole history.
|
|
4289
|
+
// SQLite is built here without UPDATE...LIMIT, so the batch is a subselect over the
|
|
4290
|
+
// primary key — oldest first, so a partial run leaves a prefix rather than holes.
|
|
4291
|
+
const redrained = db
|
|
4292
|
+
.prepare(`UPDATE _substrat_outbox SET drained_at = NULL
|
|
4293
|
+
WHERE id IN (
|
|
4294
|
+
SELECT id FROM _substrat_outbox
|
|
4295
|
+
WHERE drained_at IS NOT NULL AND drained_at < ?
|
|
4296
|
+
ORDER BY id LIMIT ?
|
|
4297
|
+
)`)
|
|
4298
|
+
.run(drainedBefore, REDRAIN_BATCH).changes;
|
|
4299
|
+
// The outcome beside the intent above — only when something changed, so a re-run
|
|
4300
|
+
// over an already reopened window leaves no row claiming it did something.
|
|
4301
|
+
if (redrained > 0) {
|
|
4302
|
+
this.recordAdmin(actor, 'redrainEvents', { tenantId, scopeId }, null, { redrained, drainedBefore });
|
|
4303
|
+
}
|
|
4304
|
+
return redrained;
|
|
4305
|
+
},
|
|
4238
4306
|
facetEvents: async (actor, tenantId, scopeId, input) => {
|
|
4239
4307
|
// `facetEvents` is the sanctioned read: an erased payload yields the same
|
|
4240
4308
|
// NULL a missing field does, and only the helper counts them apart.
|
|
@@ -4270,6 +4338,20 @@ export class SqliteScopeHost {
|
|
|
4270
4338
|
this.recordAccess(actor, 'eventEffects', { tenantId, scopeId }, { eventId: input.eventId }, tree.count);
|
|
4271
4339
|
return tree;
|
|
4272
4340
|
},
|
|
4341
|
+
invocationEvents: async (actor, tenantId, scopeId, input) => {
|
|
4342
|
+
// #1237: one call's events, siblings included — what neither walk reaches.
|
|
4343
|
+
const db = this.scopeDbFor(tenantId, scopeId);
|
|
4344
|
+
const read = readInvocation({ sql: scopedSql(db) }, input.invocationId, input.limit);
|
|
4345
|
+
this.recordAccess(actor, 'invocationEvents', { tenantId, scopeId }, { invocationId: input.invocationId }, read.events.length);
|
|
4346
|
+
return read;
|
|
4347
|
+
},
|
|
4348
|
+
deadLetters: async (actor, tenantId, scopeId, input) => {
|
|
4349
|
+
// #1525: every delivery that gave up, scope-wide — what the walks reach only by event.
|
|
4350
|
+
const db = this.scopeDbFor(tenantId, scopeId);
|
|
4351
|
+
const page = readDeadLetters({ sql: scopedSql(db) }, { limit: input.limit, cursor: input.cursor });
|
|
4352
|
+
this.recordAccess(actor, 'deadLetters', { tenantId, scopeId }, null, page.entries.length);
|
|
4353
|
+
return page;
|
|
4354
|
+
},
|
|
4273
4355
|
scopeAppliedMigrations: async (actor, tenantId, scopeId) => {
|
|
4274
4356
|
const db = this.scopeDbFor(tenantId, scopeId);
|
|
4275
4357
|
const rows = db
|
|
@@ -6093,8 +6175,8 @@ export class SqliteScopeHost {
|
|
|
6093
6175
|
.prepare(`INSERT INTO _substrat_outbox
|
|
6094
6176
|
(id, type, schema_version, occurred_at, tenant_id, scope_id, actor,
|
|
6095
6177
|
entity_type, entity_id, pii_class, subject_id, authorization,
|
|
6096
|
-
impersonation, operation, version, caused_by, payload)
|
|
6097
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
6178
|
+
impersonation, operation, version, caused_by, invocation_id, payload)
|
|
6179
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
6098
6180
|
.run(full.id, full.type, full.schemaVersion, full.occurredAt, full.tenantId, full.scopeId, JSON.stringify(full.actor), full.entity.entityType, full.entity.entityId, full.piiClass, full.subjectId ?? null, full.authorization ? JSON.stringify(full.authorization) : null, full.impersonation ? JSON.stringify(full.impersonation) : null, full.operation ?? null,
|
|
6099
6181
|
// #1242: host configuration, not envelope data — the version is a fact
|
|
6100
6182
|
// about the process, so it never rides `DomainEvent` for module code to
|
|
@@ -6104,7 +6186,10 @@ export class SqliteScopeHost {
|
|
|
6104
6186
|
// the runtime, not the host, because the host serves every scope at once
|
|
6105
6187
|
// and this context outlives an `await`. A fact about the surrounding
|
|
6106
6188
|
// dispatch, never envelope data module code could set or branch on.
|
|
6107
|
-
rt.causedBy,
|
|
6189
|
+
rt.causedBy,
|
|
6190
|
+
// #1237: a fact about the surrounding CALL, like the version above — never
|
|
6191
|
+
// envelope data module code could set or branch on.
|
|
6192
|
+
rt.invocationId, full.payload === undefined ? null : JSON.stringify(full.payload));
|
|
6108
6193
|
},
|
|
6109
6194
|
requestPlatform: (request) => {
|
|
6110
6195
|
assertImpersonationWrites(impersonation, 'ctx.requestPlatform');
|
|
@@ -6456,6 +6541,22 @@ export class SqliteScopeHost {
|
|
|
6456
6541
|
// above. Rows already written keep NULL, which is honestly "unrecorded" — the
|
|
6457
6542
|
// reason the column is nullable rather than defaulted.
|
|
6458
6543
|
this.ensureColumn(db, '_substrat_outbox', 'caused_by', 'caused_by TEXT');
|
|
6544
|
+
// #1237: existing scopes get it on next wake. Rows already written keep NULL, which
|
|
6545
|
+
// honestly means "no invocation id was carried" — nothing can decide one afterwards.
|
|
6546
|
+
this.ensureColumn(db, '_substrat_outbox', 'invocation_id', 'invocation_id TEXT');
|
|
6547
|
+
// #1237: `readInvocation`'s lookup — WHERE invocation_id = ? ORDER BY id — over an outbox
|
|
6548
|
+
// that is never pruned. No index leads with invocation_id, so without this one SQLite
|
|
6549
|
+
// walks the PRIMARY KEY from the oldest event until it reaches the call, and reading a
|
|
6550
|
+
// recent invocation costs the scope's lifetime event count. The trailing id gives the
|
|
6551
|
+
// ORDER BY for free, as it does on `_substrat_outbox_drained`.
|
|
6552
|
+
//
|
|
6553
|
+
// HERE, after the column is ensured, and deliberately NOT in KERNEL_DDL beside the
|
|
6554
|
+
// other outbox indexes. KERNEL_DDL runs FIRST on every wake, and on a scope created
|
|
6555
|
+
// before #1237 its `CREATE TABLE IF NOT EXISTS` does not add the column — so an index
|
|
6556
|
+
// naming invocation_id there throws "no such column" and every existing scope fails to
|
|
6557
|
+
// boot. `lint:spine-ddl` compares KERNEL_DDL's indexes only, so this one is held to
|
|
6558
|
+
// both adapters by the query-plan test rather than by that gate.
|
|
6559
|
+
db.exec('CREATE INDEX IF NOT EXISTS _substrat_outbox_invocation ON _substrat_outbox (invocation_id, id)');
|
|
6459
6560
|
}
|
|
6460
6561
|
runtime(tenantId, scopeId) {
|
|
6461
6562
|
const key = `${tenantId}/${scopeId}`;
|
|
@@ -6484,6 +6585,7 @@ export class SqliteScopeHost {
|
|
|
6484
6585
|
appliedMigrations,
|
|
6485
6586
|
mintEventId,
|
|
6486
6587
|
causedBy: null,
|
|
6588
|
+
invocationId: null,
|
|
6487
6589
|
};
|
|
6488
6590
|
this.scopes.set(key, created);
|
|
6489
6591
|
this.scopesById.set(scopeId, created);
|