@substrat-run/adapter-cloudflare 0.124.0 → 0.125.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.
@@ -39,8 +39,12 @@ export declare const LIVE_SCOPE_HEADER = "x-substrat-live-scope";
39
39
  * is how somebody reports "live updates are broken" two months later and nobody can
40
40
  * establish whether they ever worked on that hostname. One header makes the fallback a
41
41
  * fact the client can log, display, and report.
42
+ *
43
+ * Defined in the kernel (#1859), because the pure host's `501` names it too and a
44
+ * vertical's dev server should not import this adapter to spell it. Re-exported here so
45
+ * `host.ts` and `scope-do.ts` keep reading every wire name from this one file.
42
46
  */
43
- export declare const LIVE_MODE_HEADER = "x-substrat-live";
47
+ export { LIVE_MODE_HEADER, type LiveRefusal } from '@substrat-run/kernel';
44
48
  /**
45
49
  * Cloudflare's own per-request marker for orange-to-orange routing.
46
50
  *
@@ -58,12 +62,6 @@ export declare const LIVE_MODE_HEADER = "x-substrat-live";
58
62
  * cheaper than a matrix and correct on the request after the tenant changes their DNS.
59
63
  */
60
64
  export declare const O2O_HEADER = "cf-connecting-o2o";
61
- /** Why a subscription was refused — the value of `LIVE_MODE_HEADER` on a refusal. */
62
- export type LiveRefusal =
63
- /** This connection cannot carry a WebSocket; the client should keep polling. */
64
- 'poll'
65
- /** The request was not an upgrade at all — a programming error at the caller. */
66
- | 'not-an-upgrade';
67
65
  /**
68
66
  * What a subscribed socket remembers about itself across a hibernation.
69
67
  *
@@ -103,8 +101,8 @@ export declare function readSubscription(attachment: unknown): LiveSubscription
103
101
  * reaching it means something unusual happened rather than something normal being cut off.
104
102
  */
105
103
  export declare const LIVE_FANOUT_LIMIT = 200;
106
- /** Is this request asking to be upgraded to a WebSocket? */
107
- export declare function isUpgradeRequest(request: Request): boolean;
104
+ /** Is this request asking to be upgraded to a WebSocket? Defined in the kernel (#1859). */
105
+ export { isUpgradeRequest } from '@substrat-run/kernel';
108
106
  /**
109
107
  * Is this request arriving over an orange-to-orange hop?
110
108
  *
@@ -1 +1 @@
1
- {"version":3,"file":"live-reads.d.ts","sourceRoot":"","sources":["../src/live-reads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAE9E;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,8BAA8B,CAAC;AACjE,eAAO,MAAM,kBAAkB,2BAA2B,CAAC;AAC3D,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AAEzD;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAElD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,UAAU,sBAAsB,CAAC;AAE9C,qFAAqF;AACrF,MAAM,MAAM,WAAW;AACrB,gFAAgF;AAC9E,MAAM;AACR,iFAAiF;GAC/E,gBAAgB,CAAC;AAErB;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,yFAAyF;IACzF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAa7E;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,4DAA4D;AAC5D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAI1D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE1D"}
1
+ {"version":3,"file":"live-reads.d.ts","sourceRoot":"","sources":["../src/live-reads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAE9E;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,8BAA8B,CAAC;AACjE,eAAO,MAAM,kBAAkB,2BAA2B,CAAC;AAC3D,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AAEzD;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE1E;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,UAAU,sBAAsB,CAAC;AAE9C;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,yFAAyF;IACzF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAa7E;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,2FAA2F;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE1D"}
@@ -27,8 +27,12 @@ export const LIVE_SCOPE_HEADER = 'x-substrat-live-scope';
27
27
  * is how somebody reports "live updates are broken" two months later and nobody can
28
28
  * establish whether they ever worked on that hostname. One header makes the fallback a
29
29
  * fact the client can log, display, and report.
30
+ *
31
+ * Defined in the kernel (#1859), because the pure host's `501` names it too and a
32
+ * vertical's dev server should not import this adapter to spell it. Re-exported here so
33
+ * `host.ts` and `scope-do.ts` keep reading every wire name from this one file.
30
34
  */
31
- export const LIVE_MODE_HEADER = 'x-substrat-live';
35
+ export { LIVE_MODE_HEADER } from '@substrat-run/kernel';
32
36
  /**
33
37
  * Cloudflare's own per-request marker for orange-to-orange routing.
34
38
  *
@@ -87,12 +91,8 @@ export function readSubscription(attachment) {
87
91
  * reaching it means something unusual happened rather than something normal being cut off.
88
92
  */
89
93
  export const LIVE_FANOUT_LIMIT = 200;
90
- /** Is this request asking to be upgraded to a WebSocket? */
91
- export function isUpgradeRequest(request) {
92
- // Case-insensitive: the header is `Upgrade: websocket` by the RFC, but the token is
93
- // compared case-insensitively there too, and browsers are not the only clients.
94
- return (request.headers.get('Upgrade') ?? '').toLowerCase() === 'websocket';
95
- }
94
+ /** Is this request asking to be upgraded to a WebSocket? Defined in the kernel (#1859). */
95
+ export { isUpgradeRequest } from '@substrat-run/kernel';
96
96
  /**
97
97
  * Is this request arriving over an orange-to-orange hop?
98
98
  *
@@ -1 +1 @@
1
- {"version":3,"file":"live-reads.js","sourceRoot":"","sources":["../src/live-reads.ts"],"names":[],"mappings":"AAaA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAErD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAG,wBAAwB,CAAC;AAC3D,MAAM,CAAC,MAAM,iBAAiB,GAAG,uBAAuB,CAAC;AAEzD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAElD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,mBAAmB,CAAC;AA0B9C;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAmB;IAClD,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACvE,MAAM,CAAC,GAAG,UAA8D,CAAC;IACzE,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACrE,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACnE,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO;QACL,SAAS,EAAE,CAAC,CAAC,SAAwB;QACrC,QAAQ,EAAE,CAAC,CAAC,QAAoB;QAChC,OAAO,EAAE,CAAC,CAAC,OAAkB;QAC7B,KAAK,EAAE,CAAC,CAAC,KAAK;KACf,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC,4DAA4D;AAC5D,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,oFAAoF;IACpF,gFAAgF;IAChF,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,WAAW,CAAC;AAC9E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"live-reads.js","sourceRoot":"","sources":["../src/live-reads.ts"],"names":[],"mappings":"AAaA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAErD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,2BAA2B,CAAC;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAG,wBAAwB,CAAC;AAC3D,MAAM,CAAC,MAAM,iBAAiB,GAAG,uBAAuB,CAAC;AAEzD;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,gBAAgB,EAAoB,MAAM,sBAAsB,CAAC;AAE1E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,mBAAmB,CAAC;AAmB9C;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAmB;IAClD,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACvE,MAAM,CAAC,GAAG,UAA8D,CAAC;IACzE,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,SAAS,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACrE,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACnE,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO;QACL,SAAS,EAAE,CAAC,CAAC,SAAwB;QACrC,QAAQ,EAAE,CAAC,CAAC,QAAoB;QAChC,OAAO,EAAE,CAAC,CAAC,OAAkB;QAC7B,KAAK,EAAE,CAAC,CAAC,KAAK;KACf,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC,2FAA2F;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC;AACjD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"scope-do.d.ts","sourceRoot":"","sources":["../src/scope-do.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAWL,KAAK,WAAW,EAqCjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAmCL,KAAK,SAAS,EAmBd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EAoEtB,MAAM,sBAAsB,CAAC;AAiF9B;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAwaD;;;GAGG;AACH,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAgDxG;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,CAOxD;AAED,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,CAo5JjE"}
1
+ {"version":3,"file":"scope-do.d.ts","sourceRoot":"","sources":["../src/scope-do.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAYL,KAAK,WAAW,EAqCjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAmCL,KAAK,SAAS,EAmBd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EAoEtB,MAAM,sBAAsB,CAAC;AAiF9B;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAwaD;;;GAGG;AACH,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAgDxG;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,CAOxD;AAED,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,CA86JjE"}
package/dist/scope-do.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { REWIND_REFUSED } from './rewind-refusal.js';
2
2
  import { DurableObject } from 'cloudflare:workers';
3
- import { ATTACHMENT_ADDED, ATTACHMENT_REMOVED, attachmentRecord, domainEvent, domainEventInput, eventId, instant, objectRef, toWireFailure, grantRefFromProof, principalId, scopeId as scopeIdOf, tenantId as tenantIdOf, platformRequestInput, platformRequestId, MAX_PENDING_PLATFORM_REQUESTS, MAX_PENDING_SWEEP_RUNS, SWEEP_RUNS_KIND, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, listLimitOf, requestFingerprint, substratError, assertReplayableDump, REDRAIN_BATCH, } from '@substrat-run/contracts';
3
+ import { ATTACHMENT_ADDED, ATTACHMENT_REMOVED, attachmentRecord, domainEvent, domainEventInput, eventId, instant, objectRef, entityObjectRef, toWireFailure, grantRefFromProof, principalId, scopeId as scopeIdOf, tenantId as tenantIdOf, platformRequestInput, platformRequestId, MAX_PENDING_PLATFORM_REQUESTS, MAX_PENDING_SWEEP_RUNS, SWEEP_RUNS_KIND, SCOPE_TABLE_PAGE_MAX, SCOPE_QUERY_ROW_MAX, listLimitOf, requestFingerprint, substratError, assertReplayableDump, REDRAIN_BATCH, } from '@substrat-run/contracts';
4
4
  import { ulid, DO_SQL_LIMITS, createUlid, assertAllowed, ConnectionSealingKeyUnavailableError, noSealingKeyMessage, sealTo, assertReadOnlyQuery, entitlementDenial, platformRequestHistoryQuery, platformRequestOf, PLATFORM_REQUEST_COLUMNS, PLATFORM_REQUEST_REDACTION_SQL, platformRequestRedactionParams, platformRequestRedactionQuery, intentPayloadCarriesSubject, redactSubjectJobRuns, JOB_RUN_PATCH_SQL, JOB_STEP_RECORD_SQL, DELIVERY_ERROR_REDACTION_SQL, REDACTED_DELIVERY_NOTE, seatScopeTuple, effectiveRoleGrantQuery, switchRecordedOff, switchSystemSchedules, peerGrantsStatus, systemGrantsStatus, systemScheduleState, systemSwitchedOff, denialListQuery, denialSummaryQuery, denialTotalsQuery, DENIAL_WINDOW_QUERY, mapDenialRow, mapDenialSummaryBuckets, PermissionDenied, assertImpersonationWrites, assertModuleEnqueueableKind, impersonationStampOf, createAtomic, NotSearchable, isSearchIndexTable, searchIndexDdl, searchIndexPlans, NotListable, listIndexPlans, moduleMigrations, listQuery, cursorOf, searchLimit, searchMatchExpression, searchQuery, IDEMPOTENCY_DDL, assertIdempotencyKey, assertPermissionKey, idempotencyLookupQuery, idempotencyPruneStatement, idempotencyRecordStatement, idempotencyOptedOutMessage, replayFor, entityVersionQuery, entityVersionOf, assertIfMatch, OUTBOX_ENTITY_INDEX, SCHEDULE_STATE_DDL, SCHEDULE_STATE_REBUILD, scheduleStateHasKind, VERTICAL_EVENTS_DDL, EXPORT_HOPS_SQL, IMPORT_CURSORS_SQL, IMPORT_CURSOR_OF_SQL, OUTBOX_MARK_SQL, exportedSinceQuery, emittedSinceQuery, emittedReportOf, EMITTED_REPORT_CAP, IMPORT_CURSOR_ADVANCE_SQL, IMPORT_RECORD_SQL, moveImportCursor, CrossVerticalRegistry, exportReadPlan, exportReadQuery, planExportBatch, withheldNote, JOB_RUN_DDL, jobRunListLimit, } from '@substrat-run/kernel';
5
5
  import { isUpgradeRequest, readSubscription, LIVE_FANOUT_LIMIT, LIVE_MODE_HEADER, LIVE_PRINCIPAL_HEADER, LIVE_SCOPE_HEADER, LIVE_SUBSCRIBE_PATH, LIVE_TENANT_HEADER, } from './live-reads.js';
6
6
  import { OperationQueue } from './serialization.js';
@@ -556,8 +556,20 @@ export function defineScopeDO(modules, bareOps) {
556
556
  lastFailure = null;
557
557
  /** Passes of `applyPendingMigrations` that had work to do — see `migrationAttemptsOnInstance`. */
558
558
  migrationRuns = 0;
559
+ /**
560
+ * TEST-ONLY (#1860): no production code reads this. An exact `'ping'` is answered
561
+ * by `setWebSocketAutoResponse` without reaching `webSocketMessage` at all, and
562
+ * this count is how a test proves that from outside the object — read through
563
+ * `runInDurableObject` — rather than assuming the runtime's documented behaviour.
564
+ */
565
+ webSocketMessagesHandled = 0;
559
566
  constructor(ctx, env) {
560
567
  super(ctx, env);
568
+ // #1860: the client's 45s keep-alive (demos/ticket0/app/src/feed.ts) answered at
569
+ // the runtime level, before this object wakes for it. Without this, every idle
570
+ // subscriber's ping still reaches `webSocketMessage` and pins the DO in memory —
571
+ // exactly the hibernation cost `acceptWebSocket` (below) exists to avoid.
572
+ ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair('ping', 'pong'));
561
573
  this.sql = ctx.storage.sql;
562
574
  for (const stmt of splitSqlStatements(KERNEL_DDL)) {
563
575
  this.sql.exec(stmt);
@@ -1827,8 +1839,14 @@ export function defineScopeDO(modules, bareOps) {
1827
1839
  * Deliberately NOT a place to let a client narrow or widen what it receives: a
1828
1840
  * filter the client chooses is a filter the client can choose wrongly, and the
1829
1841
  * only filter that matters here is the one it does not control.
1842
+ *
1843
+ * #1860: an exact `'ping'` is now answered by `setWebSocketAutoResponse` in the
1844
+ * constructor, at the runtime level, without waking this object — so this branch no
1845
+ * longer runs for it. Kept as the documented fallback for a client on an older
1846
+ * build, or a hop where the runtime's auto-response is unavailable.
1830
1847
  */
1831
1848
  webSocketMessage(ws, message) {
1849
+ this.webSocketMessagesHandled++;
1832
1850
  if (message === 'ping')
1833
1851
  ws.send('pong');
1834
1852
  }
@@ -4070,6 +4088,7 @@ export function defineScopeDO(modules, bareOps) {
4070
4088
  */
4071
4089
  grant: async (principal, permission, entity) => {
4072
4090
  assertImpersonationWrites(impersonation, 'ctx.grant');
4091
+ entityObjectRef(entity, 'ctx.grant'); // #1856: a tuple the walk can read back
4073
4092
  const held = await runCheck(permission, entity);
4074
4093
  if (!held.allowed) {
4075
4094
  throw new PermissionDenied(`cannot grant '${permission}' on ${entity.entityType}:${entity.entityId} — ` +
@@ -4077,6 +4096,11 @@ export function defineScopeDO(modules, bareOps) {
4077
4096
  }
4078
4097
  sql.exec(`INSERT OR IGNORE INTO _substrat_tuples (subject, relation, object) VALUES (?, ?, ?)`, `principal:${principal}`, `granted:${permission}`, `${entity.entityType}:${entity.entityId}`);
4079
4098
  },
4099
+ /**
4100
+ * Deliberately NOT the #1856 grammar check `grant` and `link` make: a revoke writes
4101
+ * nothing the walk must read back, and a grant stored before that check existed must
4102
+ * stay removable.
4103
+ */
4080
4104
  revoke: async (principal, permission, entity) => {
4081
4105
  assertImpersonationWrites(impersonation, 'ctx.revoke');
4082
4106
  const held = await runCheck(permission, entity);
@@ -4105,6 +4129,8 @@ export function defineScopeDO(modules, bareOps) {
4105
4129
  }),
4106
4130
  link: (child, parent) => {
4107
4131
  assertImpersonationWrites(impersonation, 'ctx.link');
4132
+ entityObjectRef(child, 'ctx.link'); // #1856: an edge the walk can read back
4133
+ entityObjectRef(parent, 'ctx.link');
4108
4134
  const allowed = relations.get(child.entityType);
4109
4135
  if (!allowed?.has(parent.entityType)) {
4110
4136
  throw new Error(`undeclared entity relation: ${child.entityType} → ${parent.entityType} ` +