@substrat-run/adapter-cloudflare 0.98.0 → 0.102.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/control-plane-do.d.ts +73 -1
- package/dist/control-plane-do.d.ts.map +1 -1
- package/dist/control-plane-do.js +231 -3
- package/dist/control-plane-do.js.map +1 -1
- package/dist/d1.d.ts +1 -1
- package/dist/d1.d.ts.map +1 -1
- package/dist/d1.js +2 -1
- package/dist/d1.js.map +1 -1
- package/dist/host.d.ts +23 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +145 -4
- package/dist/host.js.map +1 -1
- package/dist/platform-sweeper-do.d.ts +2 -1
- package/dist/platform-sweeper-do.d.ts.map +1 -1
- package/dist/platform-sweeper-do.js +2 -1
- package/dist/platform-sweeper-do.js.map +1 -1
- package/dist/r2.d.ts +1 -1
- package/dist/r2.d.ts.map +1 -1
- package/dist/r2.js +2 -1
- package/dist/r2.js.map +1 -1
- package/dist/scope-do.d.ts +8 -0
- package/dist/scope-do.d.ts.map +1 -1
- package/dist/scope-do.js +143 -44
- package/dist/scope-do.js.map +1 -1
- package/dist/scope-sweeper-do.d.ts +20 -3
- package/dist/scope-sweeper-do.d.ts.map +1 -1
- package/dist/scope-sweeper-do.js +53 -0
- package/dist/scope-sweeper-do.js.map +1 -1
- package/package.json +4 -4
|
@@ -87,7 +87,8 @@ export interface PlatformSweeperDo {
|
|
|
87
87
|
* sweep: (env) =>
|
|
88
88
|
* runPlatformSweep(hostFor(env), {
|
|
89
89
|
* actor: SWEEP_ACTOR,
|
|
90
|
-
*
|
|
90
|
+
* // bound: workerd refuses the bare global called as `opts.fetch(…)`
|
|
91
|
+
* fetch: globalThis.fetch.bind(globalThis) as unknown as FetchLike,
|
|
91
92
|
* sweepers: { scrive: sweepScriveReconciliations },
|
|
92
93
|
* }),
|
|
93
94
|
* });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-sweeper-do.d.ts","sourceRoot":"","sources":["../src/platform-sweeper-do.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,WAAW,uBAAuB,CAAC,GAAG;IAC1C;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;OASG;IACH,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC9C,kFAAkF;IAClF,MAAM,CAAC,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;CACxD;AAED,+EAA+E;AAC/E,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,WAAW,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,gEAAgE;IAChE,QAAQ,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC3C;AAED
|
|
1
|
+
{"version":3,"file":"platform-sweeper-do.d.ts","sourceRoot":"","sources":["../src/platform-sweeper-do.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,WAAW,uBAAuB,CAAC,GAAG;IAC1C;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;OASG;IACH,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC9C,kFAAkF;IAClF,MAAM,CAAC,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;CACxD;AAED,+EAA+E;AAC/E,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,WAAW,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,gEAAgE;IAChE,QAAQ,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EACzC,MAAM,EAAE,uBAAuB,CAAC,GAAG,CAAC,GACnC,KAAK,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,GAAG,KAAK,aAAa,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAqDnF"}
|
|
@@ -50,7 +50,8 @@ export const PLATFORM_SWEEPER_NAME = 'platform-sweeper';
|
|
|
50
50
|
* sweep: (env) =>
|
|
51
51
|
* runPlatformSweep(hostFor(env), {
|
|
52
52
|
* actor: SWEEP_ACTOR,
|
|
53
|
-
*
|
|
53
|
+
* // bound: workerd refuses the bare global called as `opts.fetch(…)`
|
|
54
|
+
* fetch: globalThis.fetch.bind(globalThis) as unknown as FetchLike,
|
|
54
55
|
* sweepers: { scrive: sweepScriveReconciliations },
|
|
55
56
|
* }),
|
|
56
57
|
* });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-sweeper-do.js","sourceRoot":"","sources":["../src/platform-sweeper-do.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAoCxD
|
|
1
|
+
{"version":3,"file":"platform-sweeper-do.js","sourceRoot":"","sources":["../src/platform-sweeper-do.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAoCxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAoC;IAEpC,OAAO,MAAM,iBAAkB,SAAQ,aAAkB;QACvD,mEAAmE;QACnE,KAAK,GAAyC,IAAI,CAAC;QAEnD;;;WAGG;QACH,KAAK,CAAC,WAAW;YACf,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACnD,IAAI,QAAQ,KAAK,IAAI;gBAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;YAClE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC;YAC/C,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACzC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAClC,CAAC;QAED;;;;WAIG;QACH,KAAK,CAAC,QAAQ;YACZ,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;QACrB,CAAC;QAED,yEAAyE;QACzE,KAAK,CAAC,KAAK;YACT,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACpB,CAAC;QAED,KAAK,CAAC,IAAI;YACR,IAAI,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAC;YAClC,IAAI,CAAC,KAAK,GAAG,CAAC,KAAK,IAAmC,EAAE;gBACtD,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBAC5C,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;oBAClC,OAAO,MAAM,CAAC;gBAChB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC5E,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;oBACnC,OAAO,OAAO,CAAC;gBACjB,CAAC;YACH,CAAC,CAAC,EAAE,CAAC;YACL,IAAI,CAAC;gBACH,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC;YAC1B,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;gBAClB,yEAAyE;gBACzE,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/r2.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type FetchLike, type TenantBlobStore } from '@substrat-run/kernel';
|
|
2
2
|
/**
|
|
3
3
|
* The live R2 side of per-tenant blob stores (#473): a minimal client over Cloudflare's
|
|
4
4
|
* R2 REST API — create and delete buckets. Pure web-standard `fetch`, injected for tests,
|
package/dist/r2.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r2.d.ts","sourceRoot":"","sources":["../src/r2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"r2.d.ts","sourceRoot":"","sources":["../src/r2.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEjH;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtC,8EAA8E;IAC9E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAaD;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,GAAG,YAAY,CA2D1E;AAqBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,CA2BlE"}
|
package/dist/r2.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { globalFetch } from '@substrat-run/kernel';
|
|
1
2
|
/** Bound an untrusted upstream body for an error message, marking the cut (#307). */
|
|
2
3
|
function bounded(body, max = 2000) {
|
|
3
4
|
return body.length <= max ? body : `${body.slice(0, max)} … [truncated, ${body.length - max} chars omitted]`;
|
|
@@ -24,7 +25,7 @@ export async function blobStoreBucketName(tenantId, vertical, binding) {
|
|
|
24
25
|
return `${raw.slice(0, 46)}-${tail}`;
|
|
25
26
|
}
|
|
26
27
|
export function createR2BlobStores(opts) {
|
|
27
|
-
const fetchImpl = opts.fetch ??
|
|
28
|
+
const fetchImpl = opts.fetch ?? globalFetch;
|
|
28
29
|
const base = `https://api.cloudflare.com/client/v4/accounts/${opts.accountId}/r2/buckets`;
|
|
29
30
|
const auth = { authorization: `Bearer ${opts.apiToken}` };
|
|
30
31
|
const readEnvelope = async (res, what) => {
|
package/dist/r2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r2.js","sourceRoot":"","sources":["../src/r2.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"r2.js","sourceRoot":"","sources":["../src/r2.ts"],"names":[],"mappings":"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"}
|
package/dist/scope-do.d.ts
CHANGED
|
@@ -22,6 +22,14 @@ export interface ScopeDoEnv {
|
|
|
22
22
|
* permissions.md) evaluates permissions locally and needs no binding.
|
|
23
23
|
*/
|
|
24
24
|
CONTROL_PLANE?: DurableObjectNamespace;
|
|
25
|
+
/**
|
|
26
|
+
* The version REGISTRY id of the vertical version this script serves (#1242) —
|
|
27
|
+
* injected at deploy as a `plain_text` binding, refreshed by every in-place serve,
|
|
28
|
+
* and stamped into the outbox `version` column at emit (the signals dimension,
|
|
29
|
+
* #1231). Optional: a script deployed before the binding existed, or a test
|
|
30
|
+
* worker, stamps NULL.
|
|
31
|
+
*/
|
|
32
|
+
SUBSTRAT_VERSION_ID?: string;
|
|
25
33
|
}
|
|
26
34
|
/** A raw `_substrat_platform_requests` row as stored — snake_case, JSON columns still strings.
|
|
27
35
|
* The coordinator maps it to the `PlatformRequest` contract shape. */
|
package/dist/scope-do.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-do.d.ts","sourceRoot":"","sources":["../src/scope-do.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"scope-do.d.ts","sourceRoot":"","sources":["../src/scope-do.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAgDnD,OAAO,EAwBL,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EAsCtB,MAAM,sBAAsB,CAAC;AAM9B;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AA2VD;uEACuE;AACvE,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AA6DD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAwDxD;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,kBAAkB,EAAE,EAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,GACxD,KAAK,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,UAAU,KAAK,aAAa,CA8oFjE"}
|
package/dist/scope-do.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DurableObject } from 'cloudflare:workers';
|
|
2
|
-
import { ATTACHMENT_ADDED, ATTACHMENT_REMOVED, attachmentRecord, domainEvent, domainEventInput, eventId, instant, objectRef, toWireFailure, grantRefFromProof, principalId, platformRequestInput, platformRequestId, MAX_PENDING_PLATFORM_REQUESTS, platformRequest, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, listLimitOf, requestFingerprint, substratError, assertReplayableDump, } from '@substrat-run/contracts';
|
|
2
|
+
import { ATTACHMENT_ADDED, ATTACHMENT_REMOVED, attachmentRecord, domainEvent, domainEventInput, eventId, instant, objectRef, toWireFailure, grantRefFromProof, principalId, platformRequestInput, platformRequestId, MAX_PENDING_PLATFORM_REQUESTS, MAX_PENDING_SWEEP_RUNS, SWEEP_RUNS_KIND, platformRequest, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, listLimitOf, requestFingerprint, substratError, assertReplayableDump, } from '@substrat-run/contracts';
|
|
3
3
|
import { ulid, assertAllowed, ConnectionSealingKeyUnavailableError, noSealingKeyMessage, sealTo, assertReadOnlyQuery, entitlementDenial, platformRequestHistoryQuery, PLATFORM_REQUEST_COLUMNS, denialListQuery, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialBucketRow, PermissionDenied, assertImpersonationWrites, impersonationStampOf, createAtomic, NotSearchable, isSearchIndexTable, searchIndexDdl, searchIndexMigrations, searchIndexPlans, NotListable, listIndexMigrations, listIndexPlans, listQuery, cursorOf, searchLimit, searchMatchExpression, searchQuery, IDEMPOTENCY_DDL, assertIdempotencyKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, entityVersionQuery, entityVersionOf, assertIfMatch, OUTBOX_ENTITY_INDEX, } from '@substrat-run/kernel';
|
|
4
4
|
import { OperationQueue } from './serialization.js';
|
|
5
5
|
import { doScopedSql } from './sql.js';
|
|
@@ -27,8 +27,19 @@ const KERNEL_DDL = `
|
|
|
27
27
|
-- session/by). NULL is the ordinary case -- nobody was impersonating -- and the
|
|
28
28
|
-- actor column above stays the principal the permission model answered about.
|
|
29
29
|
impersonation TEXT,
|
|
30
|
+
-- #1231: the invoke() string this event was emitted from. NULL is two facts the
|
|
31
|
+
-- spine cannot tell apart afterwards: a consumer emit (no operation ran), and a
|
|
32
|
+
-- row written before the column.
|
|
33
|
+
operation TEXT,
|
|
34
|
+
-- #1242: the version REGISTRY id of the vertical version the script served when
|
|
35
|
+
-- this event was emitted (the signals version dimension, #1231). NULL = the
|
|
36
|
+
-- script was deployed without the binding, or the row predates the column.
|
|
37
|
+
version TEXT,
|
|
30
38
|
drained_at TEXT
|
|
31
39
|
);
|
|
40
|
+
-- #1232: the freshness evaluator's read - MAX(occurred_at) per type, every pass,
|
|
41
|
+
-- over a table that is never pruned. Unindexed, that is a full history scan.
|
|
42
|
+
CREATE INDEX IF NOT EXISTS _substrat_outbox_type_at ON _substrat_outbox (type, occurred_at);
|
|
32
43
|
-- platform-intents.md: durable intents a vertical enqueues (ctx.requestPlatform) for the platform
|
|
33
44
|
-- to drain and execute with HostAdmin authority -- the sandbox-clean way a vertical asks for a
|
|
34
45
|
-- privileged action. Written by the kernel (spine), settled by the platform drain.
|
|
@@ -445,41 +456,7 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
445
456
|
for (const stmt of splitSqlStatements(KERNEL_DDL)) {
|
|
446
457
|
this.sql.exec(stmt);
|
|
447
458
|
}
|
|
448
|
-
|
|
449
|
-
// the old shape. Attempt-and-tolerate: DO SQLite restricts PRAGMA, so there
|
|
450
|
-
// is no column probe, and a duplicate is the steady state after the first
|
|
451
|
-
// cold start (same argument as ControlPlaneDO.addColumn).
|
|
452
|
-
for (const alter of [
|
|
453
|
-
'ALTER TABLE _substrat_tuples ADD COLUMN revoked_at TEXT',
|
|
454
|
-
// Executor retry state (#100). The defaults read as "terminal", which is
|
|
455
|
-
// right for every row already there: each is a completed delivery or a
|
|
456
|
-
// consumer dead-letter.
|
|
457
|
-
'ALTER TABLE _substrat_deliveries ADD COLUMN attempts INTEGER NOT NULL DEFAULT 0',
|
|
458
|
-
'ALTER TABLE _substrat_deliveries ADD COLUMN next_attempt_at TEXT',
|
|
459
|
-
// K-34: the authorization column on a scope DO created before it existed. Nullable,
|
|
460
|
-
// so legacy outbox rows read as "unrecorded". (_substrat_denials is a new table,
|
|
461
|
-
// covered by KERNEL_DDL's IF NOT EXISTS with no ALTER.)
|
|
462
|
-
'ALTER TABLE _substrat_outbox ADD COLUMN authorization TEXT',
|
|
463
|
-
// #841: refusal attribution on a scope DO that predates it. Nullable, so an
|
|
464
|
-
// intent settled before this column reads as "nobody classified this" rather
|
|
465
|
-
// than claiming an origin the drain never decided.
|
|
466
|
-
'ALTER TABLE _substrat_platform_requests ADD COLUMN last_failure TEXT',
|
|
467
|
-
// K-42: the two-actor stamp on the three spine tables that record who did
|
|
468
|
-
// what, for a scope DO created before impersonation existed. Nullable
|
|
469
|
-
// everywhere, and the null means "nobody was impersonating" rather than
|
|
470
|
-
// "unrecorded" — every one of those rows predates the possibility.
|
|
471
|
-
'ALTER TABLE _substrat_outbox ADD COLUMN impersonation TEXT',
|
|
472
|
-
'ALTER TABLE _substrat_platform_requests ADD COLUMN impersonation TEXT',
|
|
473
|
-
'ALTER TABLE _substrat_denials ADD COLUMN impersonation TEXT',
|
|
474
|
-
]) {
|
|
475
|
-
try {
|
|
476
|
-
this.sql.exec(alter);
|
|
477
|
-
}
|
|
478
|
-
catch (err) {
|
|
479
|
-
if (!/duplicate column name/i.test(err.message))
|
|
480
|
-
throw err;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
459
|
+
this.applySpineColumnAdditions();
|
|
483
460
|
for (const registration of modules)
|
|
484
461
|
this.registerModule(registration);
|
|
485
462
|
for (const [name, handler] of Object.entries(bareOps))
|
|
@@ -972,7 +949,7 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
972
949
|
// emitted events back as one — verified across `await` in workerd.
|
|
973
950
|
try {
|
|
974
951
|
await this.ctx.storage.transaction(async () => {
|
|
975
|
-
const ctx = this.operationContext(principal, tenantId, scopeId, undefined, connectionId, systemModuleId, signals, impersonation);
|
|
952
|
+
const ctx = this.operationContext(principal, tenantId, scopeId, undefined, connectionId, systemModuleId, signals, impersonation, operation);
|
|
976
953
|
// #116: a retry is answered from the recording, and nothing else runs
|
|
977
954
|
// — not the guards, not the handler, not the permission check inside
|
|
978
955
|
// it. Keyed by SUBJECT, so a caller only ever reaches its own
|
|
@@ -1135,7 +1112,7 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
1135
1112
|
return this.queue.enqueue(async () => {
|
|
1136
1113
|
try {
|
|
1137
1114
|
await this.ctx.storage.transaction(async () => {
|
|
1138
|
-
const ctx = this.operationContext(principal, tenantId, scopeId, undefined, connectionId);
|
|
1115
|
+
const ctx = this.operationContext(principal, tenantId, scopeId, undefined, connectionId, undefined, undefined, undefined, 'attachments.upload');
|
|
1139
1116
|
assertAllowed(await ctx.check(gate.write, parsed.entity));
|
|
1140
1117
|
this.sql.exec(`INSERT INTO _substrat_attachments
|
|
1141
1118
|
(id, entity_type, entity_id, filename, content_type, size, sha256,
|
|
@@ -1165,7 +1142,7 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
1165
1142
|
await this.ensureMigrations();
|
|
1166
1143
|
const gate = this.attachmentGate(entity.entityType);
|
|
1167
1144
|
try {
|
|
1168
|
-
const ctx = this.operationContext(principal, tenantId, scopeId, undefined, connectionId);
|
|
1145
|
+
const ctx = this.operationContext(principal, tenantId, scopeId, undefined, connectionId, undefined, undefined, undefined, 'attachments.list');
|
|
1169
1146
|
assertAllowed(await ctx.check(gate.read, entity));
|
|
1170
1147
|
}
|
|
1171
1148
|
catch (err) {
|
|
@@ -1209,7 +1186,7 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
1209
1186
|
}
|
|
1210
1187
|
const gate = this.attachmentGate(record.entity.entityType);
|
|
1211
1188
|
try {
|
|
1212
|
-
const ctx = this.operationContext(principal, tenantId, scopeId, undefined, connectionId);
|
|
1189
|
+
const ctx = this.operationContext(principal, tenantId, scopeId, undefined, connectionId, undefined, undefined, undefined, 'attachments.open');
|
|
1213
1190
|
assertAllowed(await ctx.check(mode === 'read' ? gate.read : gate.write, record.entity));
|
|
1214
1191
|
}
|
|
1215
1192
|
catch (err) {
|
|
@@ -1230,7 +1207,7 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
1230
1207
|
const gate = this.attachmentGate(record.entity.entityType);
|
|
1231
1208
|
try {
|
|
1232
1209
|
await this.ctx.storage.transaction(async () => {
|
|
1233
|
-
const ctx = this.operationContext(principal, tenantId, scopeId, undefined, connectionId);
|
|
1210
|
+
const ctx = this.operationContext(principal, tenantId, scopeId, undefined, connectionId, undefined, undefined, undefined, 'attachments.remove');
|
|
1234
1211
|
assertAllowed(await ctx.check(gate.write, record.entity));
|
|
1235
1212
|
this.sql.exec('DELETE FROM _substrat_attachments WHERE id = ?', attachmentId);
|
|
1236
1213
|
ctx.emit({
|
|
@@ -1273,6 +1250,38 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
1273
1250
|
.toArray()[0];
|
|
1274
1251
|
return row?.last_run_at ?? null;
|
|
1275
1252
|
}
|
|
1253
|
+
/**
|
|
1254
|
+
* #1232: everything the freshness evaluator needs about this scope, one round
|
|
1255
|
+
* trip regardless of how many types are declared — the newest matching event
|
|
1256
|
+
* per type, plus each type's recorded evaluator state (last recorded at +
|
|
1257
|
+
* outcome, kept in `_substrat_schedule_state` under a `freshness:` prefix that
|
|
1258
|
+
* cannot collide with operation names).
|
|
1259
|
+
*/
|
|
1260
|
+
async freshnessProbe(types) {
|
|
1261
|
+
const out = {};
|
|
1262
|
+
for (const t of types)
|
|
1263
|
+
out[t] = { observedAt: null, stateAt: null, stateOutcome: null };
|
|
1264
|
+
if (types.length === 0)
|
|
1265
|
+
return out;
|
|
1266
|
+
const marks = types.map(() => '?').join(', ');
|
|
1267
|
+
for (const row of this.sql
|
|
1268
|
+
.exec(`SELECT type, MAX(occurred_at) AS at FROM _substrat_outbox WHERE type IN (${marks}) GROUP BY type`, ...types)
|
|
1269
|
+
.toArray()) {
|
|
1270
|
+
if (row.at !== null)
|
|
1271
|
+
out[row.type].observedAt = row.at;
|
|
1272
|
+
}
|
|
1273
|
+
const keys = types.map((t) => `freshness:${t}`);
|
|
1274
|
+
for (const row of this.sql
|
|
1275
|
+
.exec(`SELECT schedule_op, last_run_at, last_status FROM _substrat_schedule_state WHERE schedule_op IN (${marks})`, ...keys)
|
|
1276
|
+
.toArray()) {
|
|
1277
|
+
const t = row.schedule_op.slice('freshness:'.length);
|
|
1278
|
+
if (out[t]) {
|
|
1279
|
+
out[t].stateAt = row.last_run_at;
|
|
1280
|
+
out[t].stateOutcome = row.last_status;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
return out;
|
|
1284
|
+
}
|
|
1276
1285
|
/** Record a schedule run's timestamp + outcome (#383). Spine, kernel-written. */
|
|
1277
1286
|
async recordScheduleRun(operation, at, status) {
|
|
1278
1287
|
this.sql.exec(`INSERT INTO _substrat_schedule_state (schedule_op, last_run_at, last_status)
|
|
@@ -1496,6 +1505,27 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
1496
1505
|
this.recordExecutorAttempt(eventId, deliveryId, null, null);
|
|
1497
1506
|
return id;
|
|
1498
1507
|
}
|
|
1508
|
+
/**
|
|
1509
|
+
* #1232: a CP-less pass's ONLY exit for its schedule outcomes — `admin.recordSweepRun`
|
|
1510
|
+
* is a control-plane write the null-CP proxy refuses. One batched intent per pass,
|
|
1511
|
+
* under its own low sub-cap, and a full journal DROPS the report (null) instead of
|
|
1512
|
+
* throwing: telemetry must never sink a pass, and must never starve the shared
|
|
1513
|
+
* 32-slot budget a provision-sibling needs. The next drained pass reports again.
|
|
1514
|
+
*/
|
|
1515
|
+
enqueueSweepRuns(payload, requestedBy) {
|
|
1516
|
+
const counts = this.sql
|
|
1517
|
+
.exec(`SELECT COUNT(*) AS c, SUM(CASE WHEN kind = ? THEN 1 ELSE 0 END) AS k
|
|
1518
|
+
FROM _substrat_platform_requests WHERE status = 'pending'`, SWEEP_RUNS_KIND)
|
|
1519
|
+
.toArray()[0];
|
|
1520
|
+
if (counts.c >= MAX_PENDING_PLATFORM_REQUESTS || (counts.k ?? 0) >= MAX_PENDING_SWEEP_RUNS) {
|
|
1521
|
+
return null;
|
|
1522
|
+
}
|
|
1523
|
+
const id = platformRequestId.parse(ulid());
|
|
1524
|
+
this.sql.exec(`INSERT INTO _substrat_platform_requests
|
|
1525
|
+
(id, kind, payload, requested_by, impersonation, status, attempts, requested_at)
|
|
1526
|
+
VALUES (?, ?, ?, ?, NULL, 'pending', 0, ?)`, id, SWEEP_RUNS_KIND, payload, requestedBy, instant.parse(new Date().toISOString()));
|
|
1527
|
+
return id;
|
|
1528
|
+
}
|
|
1499
1529
|
/** Executor deliveries that exhausted their attempts. */
|
|
1500
1530
|
executorDeadLetters() {
|
|
1501
1531
|
const rows = this.sql
|
|
@@ -1646,6 +1676,54 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
1646
1676
|
* naming the scope it was captured from, so restoring one anywhere else needs them
|
|
1647
1677
|
* re-pointed — see `rewriteScopeTuples`.
|
|
1648
1678
|
*/
|
|
1679
|
+
/**
|
|
1680
|
+
* The additive spine-column migrations. KERNEL_DDL is all IF NOT EXISTS, so a
|
|
1681
|
+
* scope DO created before a column keeps the old shape — and so does a table a
|
|
1682
|
+
* DUMP replay just recreated from legacy DDL, which is why `importDump` re-runs
|
|
1683
|
+
* this after the replay: without it, the next INSERT naming the column fails in
|
|
1684
|
+
* this very instance. Attempt-and-tolerate: DO SQLite restricts PRAGMA, so there
|
|
1685
|
+
* is no column probe, and a duplicate is the steady state after the first cold
|
|
1686
|
+
* start (same argument as ControlPlaneDO.addColumn).
|
|
1687
|
+
*/
|
|
1688
|
+
applySpineColumnAdditions() {
|
|
1689
|
+
for (const alter of [
|
|
1690
|
+
'ALTER TABLE _substrat_tuples ADD COLUMN revoked_at TEXT',
|
|
1691
|
+
// Executor retry state (#100). The defaults read as "terminal", which is
|
|
1692
|
+
// right for every row already there: each is a completed delivery or a
|
|
1693
|
+
// consumer dead-letter.
|
|
1694
|
+
'ALTER TABLE _substrat_deliveries ADD COLUMN attempts INTEGER NOT NULL DEFAULT 0',
|
|
1695
|
+
'ALTER TABLE _substrat_deliveries ADD COLUMN next_attempt_at TEXT',
|
|
1696
|
+
// K-34: the authorization column on a scope DO created before it existed. Nullable,
|
|
1697
|
+
// so legacy outbox rows read as "unrecorded". (_substrat_denials is a new table,
|
|
1698
|
+
// covered by KERNEL_DDL's IF NOT EXISTS with no ALTER.)
|
|
1699
|
+
'ALTER TABLE _substrat_outbox ADD COLUMN authorization TEXT',
|
|
1700
|
+
// #841: refusal attribution on a scope DO that predates it. Nullable, so an
|
|
1701
|
+
// intent settled before this column reads as "nobody classified this" rather
|
|
1702
|
+
// than claiming an origin the drain never decided.
|
|
1703
|
+
'ALTER TABLE _substrat_platform_requests ADD COLUMN last_failure TEXT',
|
|
1704
|
+
// K-42: the two-actor stamp on the three spine tables that record who did
|
|
1705
|
+
// what, for a scope DO created before impersonation existed. Nullable
|
|
1706
|
+
// everywhere, and the null means "nobody was impersonating" rather than
|
|
1707
|
+
// "unrecorded" — every one of those rows predates the possibility.
|
|
1708
|
+
'ALTER TABLE _substrat_outbox ADD COLUMN impersonation TEXT',
|
|
1709
|
+
'ALTER TABLE _substrat_platform_requests ADD COLUMN impersonation TEXT',
|
|
1710
|
+
'ALTER TABLE _substrat_denials ADD COLUMN impersonation TEXT',
|
|
1711
|
+
// #1231: the emitting operation, on a scope DO created before the column.
|
|
1712
|
+
// Nullable so every legacy row reads as unrecorded rather than named.
|
|
1713
|
+
'ALTER TABLE _substrat_outbox ADD COLUMN operation TEXT',
|
|
1714
|
+
// #1242: the signals `version` dimension on the outbox, for a scope DO
|
|
1715
|
+
// created before the column. NULL stays honest — unstamped.
|
|
1716
|
+
'ALTER TABLE _substrat_outbox ADD COLUMN version TEXT',
|
|
1717
|
+
]) {
|
|
1718
|
+
try {
|
|
1719
|
+
this.sql.exec(alter);
|
|
1720
|
+
}
|
|
1721
|
+
catch (err) {
|
|
1722
|
+
if (!/duplicate column name/i.test(err.message))
|
|
1723
|
+
throw err;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1649
1727
|
async importDump(tables, destScopeId) {
|
|
1650
1728
|
// The WHOLE drop-then-replay runs under deferred foreign keys, in one transaction.
|
|
1651
1729
|
//
|
|
@@ -1712,6 +1790,12 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
1712
1790
|
// exist so the checker can read it.
|
|
1713
1791
|
for (const stmt of splitSqlStatements(KERNEL_DDL))
|
|
1714
1792
|
this.sql.exec(stmt);
|
|
1793
|
+
// …and the additive columns, for the same reason KERNEL_DDL is re-asserted: a
|
|
1794
|
+
// dump captured before a column existed replays DDL WITHOUT it, and IF NOT
|
|
1795
|
+
// EXISTS cannot widen a table the dump did bring. Without this, the next emit
|
|
1796
|
+
// in this instance fails with `no such column` until a cold start re-runs the
|
|
1797
|
+
// constructor's pass.
|
|
1798
|
+
this.applySpineColumnAdditions();
|
|
1715
1799
|
// Rebuild the derived search indexes over the rows just loaded (#827). Drop-then-
|
|
1716
1800
|
// create, so it also repairs an index a dump left stale, and the triggers it
|
|
1717
1801
|
// recreates are what keep the restored scope in step from here. Skipped for a plan
|
|
@@ -1879,6 +1963,8 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
1879
1963
|
// was impersonating, because `DomainEvent.impersonation` is optional — the
|
|
1880
1964
|
// shape module code never sees is also the shape it cannot branch on.
|
|
1881
1965
|
...(row.impersonation ? { impersonation: JSON.parse(row.impersonation) } : {}),
|
|
1966
|
+
// #1231: absent rather than null, the same shape rule as the stamp above.
|
|
1967
|
+
...(row.operation ? { operation: row.operation } : {}),
|
|
1882
1968
|
payload: row.payload === null ? undefined : JSON.parse(row.payload),
|
|
1883
1969
|
});
|
|
1884
1970
|
}
|
|
@@ -1893,7 +1979,13 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
1893
1979
|
* when support is looking" is the one behaviour this feature must make
|
|
1894
1980
|
* impossible.
|
|
1895
1981
|
*/
|
|
1896
|
-
impersonation
|
|
1982
|
+
impersonation,
|
|
1983
|
+
/**
|
|
1984
|
+
* #1231: the exact `invoke()` string this context runs on behalf of — stamped
|
|
1985
|
+
* onto every event it emits. Absent for consumer dispatch: a consumer runs on
|
|
1986
|
+
* behalf of no operation, and the emitted row's NULL says so.
|
|
1987
|
+
*/
|
|
1988
|
+
operation) {
|
|
1897
1989
|
const checker = this.checker;
|
|
1898
1990
|
const relations = this.relations;
|
|
1899
1991
|
const searchPlans = this.searchPlans;
|
|
@@ -2001,12 +2093,19 @@ export function defineScopeDO(modules, bareOps) {
|
|
|
2001
2093
|
actor: systemActor ?? derivedActor,
|
|
2002
2094
|
...(passed.length ? { authorization: passed.map((p) => ({ ...p })) } : {}),
|
|
2003
2095
|
...(stamp ? { impersonation: stamp } : {}),
|
|
2096
|
+
// #1231: kernel-stamped like the two above — `domainEventInput.parse`
|
|
2097
|
+
// already stripped anything module code tried to smuggle under this key.
|
|
2098
|
+
...(operation ? { operation } : {}),
|
|
2004
2099
|
});
|
|
2005
2100
|
sql.exec(`INSERT INTO _substrat_outbox
|
|
2006
2101
|
(id, type, schema_version, occurred_at, tenant_id, scope_id, actor,
|
|
2007
2102
|
entity_type, entity_id, pii_class, subject_id, authorization,
|
|
2008
|
-
impersonation, payload)
|
|
2009
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 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.
|
|
2103
|
+
impersonation, operation, version, payload)
|
|
2104
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 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,
|
|
2105
|
+
// #1242: script configuration, not envelope data — the version is a fact
|
|
2106
|
+
// about the deploy, so it never rides `DomainEvent` for module code to
|
|
2107
|
+
// branch on; it exists for the observability joins the column serves.
|
|
2108
|
+
this.env.SUBSTRAT_VERSION_ID ?? null, full.payload === undefined ? null : JSON.stringify(full.payload));
|
|
2010
2109
|
},
|
|
2011
2110
|
requestPlatform: (request) => {
|
|
2012
2111
|
assertImpersonationWrites(impersonation, 'ctx.requestPlatform');
|