@substrat-run/adapter-cloudflare 0.143.0 → 0.144.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"scope-do.d.ts","sourceRoot":"","sources":["../src/scope-do.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA4DnD,OAAO,EA2DL,KAAK,SAAS,EAsBd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EAqGtB,MAAM,sBAAsB,CAAC;AAmD9B,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAkBL,kBAAkB,EAsEnB,MAAM,sBAAsB,CAAC;AA0B9B;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAoFD,oFAAoF;AACpF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AAsjB5E;;;GAGG;AACH,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;AAmBtD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,CAOxD;AAED,gHAAgH;AAChH,OAAO,EAAE,kBAAkB,EAAE,CAAC;AA2B9B,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,CAAC,UAAU,CAAC,CA46N7E"}
1
+ {"version":3,"file":"scope-do.d.ts","sourceRoot":"","sources":["../src/scope-do.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA4DnD,OAAO,EA2DL,KAAK,SAAS,EAsBd,KAAK,kBAAkB,EAEvB,KAAK,gBAAgB,EAqGtB,MAAM,sBAAsB,CAAC;AAoD9B,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAkBL,kBAAkB,EAyEnB,MAAM,sBAAsB,CAAC;AA0B9B;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAoFD,oFAAoF;AACpF,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AA+jB5E;;;GAGG;AACH,MAAM,MAAM,yBAAyB,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;AAmBtD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,CAOxD;AAED,gHAAgH;AAChH,OAAO,EAAE,kBAAkB,EAAE,CAAC;AA2B9B,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,CAAC,UAAU,CAAC,CA0gO7E"}
package/dist/scope-do.js CHANGED
@@ -7,7 +7,7 @@ import { isUpgradeRequest, readSubscription, LIVE_DECIDE_ATTEMPTS, LIVE_FANOUT_L
7
7
  import { replyOf } from './do-reply.js';
8
8
  import { OperationQueue } from './serialization.js';
9
9
  import { assertNoWorkerdStorage, doScopedSql, doBuiltColumnsOf, doRedactionSql, doSpineSql } from './sql.js';
10
- import { actorOf, admitPeer, collectPeers, switchPeer, peerSubjectRef, subjectGrantState, assertNoSecret, CAPABILITY_DDL, COPY_ORIGIN_DDL, ENTITY_STATE_MOVES_DDL, SWITCH_FENCES_DDL, TABLE_OWNERS_DDL, assertMigrationLeavesLedgerAlone, recordOwnershipSteps, runMigrationStatements, splitSqlStatements, executableSqlStatements, blankSqlComments, CAPABILITY_EXCHANGE_OPERATION, CAPABILITY_BECOME_MINT_OPERATION, becomeMintCheck, mintBecomeCapabilityAsPrincipal, revokeBecomeCapabilityAsPrincipal, readBecomeLinkStates, capabilityAttachmentWriteRefused, createCapabilityVerbs, createEntityEdgeVerbs, createEntityStateVerbs, createTrashedReads, CONTINUATION_POSITION_CAP, visibleContinuation, searchStateWhere, uncheckedView, addStatePlans, assertNoCallerPurge, isUnreachableParent, PURGE_BATCH, heldPurgePass, lifecycleRefusal, purgeHeldBy, purgeDueOf, purgeOnlyKeysOf, runPurgePass, refuseTrashedTarget, assertBoundOperations, registerTrashTargets, withheldKeysFor, statefulTablesOf, afterMigration, afterRuntimeDdl, derivesAnything, repairDerivedObjects, StateColumnLost, exchangeCapability, guardSecrets, mintBecomeCapability, redactSecrets, redactSecretText, moduleLog, asyncInvocationId, asyncLinePass, resolveCapabilitySession, revokeCapabilityAsPlatform, readCapabilityPage, domainEventOf, facetEvents, readDeadLetters, readLifecycleFlow, readOperationSeries, readHistory, readInvocation, readUndrainedOutbox, walkEventCause, walkEventEffects, } from '@substrat-run/kernel';
10
+ import { actorOf, admitPeer, collectPeers, switchPeer, peerSubjectRef, subjectGrantState, assertNoSecret, CAPABILITY_DDL, COPY_ORIGIN_DDL, ENTITY_STATE_MOVES_DDL, SWITCH_FENCES_DDL, TABLE_OWNERS_DDL, assertMigrationLeavesLedgerAlone, recordOwnershipSteps, runMigrationStatements, splitSqlStatements, executableSqlStatements, blankSqlComments, CAPABILITY_EXCHANGE_OPERATION, CAPABILITY_BECOME_MINT_OPERATION, becomeMintCheck, mintBecomeCapabilityAsPrincipal, revokeBecomeCapabilityAsPrincipal, readBecomeLinkStates, ENTITY_UNBIND_OPERATION, allowedFor, unbindEntityHolderBounded, capabilityAttachmentWriteRefused, createCapabilityVerbs, createEntityEdgeVerbs, createEntityStateVerbs, createTrashedReads, CONTINUATION_POSITION_CAP, visibleContinuation, searchStateWhere, uncheckedView, addStatePlans, assertNoCallerPurge, isUnreachableParent, PURGE_BATCH, heldPurgePass, lifecycleRefusal, purgeHeldBy, purgeDueOf, purgeOnlyKeysOf, runPurgePass, refuseTrashedTarget, assertBoundOperations, registerTrashTargets, withheldKeysFor, statefulTablesOf, afterMigration, afterRuntimeDdl, derivesAnything, repairDerivedObjects, StateColumnLost, exchangeCapability, guardSecrets, mintBecomeCapability, redactSecrets, redactSecretText, moduleLog, asyncInvocationId, asyncLinePass, resolveCapabilitySession, revokeCapabilityAsPlatform, readCapabilityPage, domainEventOf, facetEvents, readDeadLetters, readLifecycleFlow, readOperationSeries, readHistory, readInvocation, readUndrainedOutbox, walkEventCause, walkEventEffects, } from '@substrat-run/kernel';
11
11
  import { COPY_RESTORE_FENCE_LAPSED, copyRestoreFenceLapsed } from '@substrat-run/kernel';
12
12
  import { createDoTupleChecker, createLocalControlPlaneReader, scopeTupleReader } from './checker.js';
13
13
  import { settlePlatformRequestIn } from '@substrat-run/kernel';
@@ -561,6 +561,15 @@ function toRpcError(err) {
561
561
  }
562
562
  return new Error(String(err));
563
563
  }
564
+ /** A throwing RPC kept for a coordinator from before #113: `fn`'s refusal, as `toRpcError` rebuilds it. */
565
+ async function thrownForRpc(fn) {
566
+ try {
567
+ return await fn();
568
+ }
569
+ catch (err) {
570
+ throw toRpcError(err);
571
+ }
572
+ }
564
573
  /** The check subject for an attachment gate (#476): the connection when the connector door
565
574
  * set one, else the principal — mirrors the invoke path's subject selection. */
566
575
  /**
@@ -2758,8 +2767,42 @@ export function defineScopeDO(modules, bareOps) {
2758
2767
  .toArray()[0];
2759
2768
  return row ? attachmentRecordOfRow(row) : null;
2760
2769
  }
2770
+ // The attachment verbs (#473) each have two doors onto one private body. The `…Reply`
2771
+ // door answers a refusal as DATA, so its code and extensions survive the hop (#113); the
2772
+ // plain one throws it as the bare `Error` `toRpcError` rebuilds, and is kept for a
2773
+ // coordinator from before #113.
2774
+ attachmentAdd(...args) {
2775
+ return thrownForRpc(() => this.addAttachment(...args));
2776
+ }
2777
+ attachmentAddReply(...args) {
2778
+ return replyOf(() => this.addAttachment(...args));
2779
+ }
2780
+ attachmentList(...args) {
2781
+ return thrownForRpc(() => this.listAttachments(...args));
2782
+ }
2783
+ attachmentListReply(...args) {
2784
+ return replyOf(() => this.listAttachments(...args));
2785
+ }
2786
+ attachmentAuthorize(...args) {
2787
+ return thrownForRpc(() => this.authorizeAttachment(...args));
2788
+ }
2789
+ attachmentAuthorizeReply(...args) {
2790
+ return replyOf(() => this.authorizeAttachment(...args));
2791
+ }
2792
+ systemAttachmentAuthorize(...args) {
2793
+ return thrownForRpc(() => this.authorizeSystemAttachment(...args));
2794
+ }
2795
+ systemAttachmentAuthorizeReply(...args) {
2796
+ return replyOf(() => this.authorizeSystemAttachment(...args));
2797
+ }
2798
+ attachmentRemove(...args) {
2799
+ return thrownForRpc(() => this.removeAttachment(...args));
2800
+ }
2801
+ attachmentRemoveReply(...args) {
2802
+ return replyOf(() => this.removeAttachment(...args));
2803
+ }
2761
2804
  /** Record an uploaded attachment: write gate + row + `attachment.added`, one txn. */
2762
- async attachmentAdd(record, principal, tenantId, scopeId,
2805
+ async addAttachment(record, principal, tenantId, scopeId,
2763
2806
  // #476: when set, the gate is checked as this CONNECTION (not `principal`, which is
2764
2807
  // then only the ctx.principal placeholder the connector door passes).
2765
2808
  connectionId) {
@@ -2795,7 +2838,7 @@ export function defineScopeDO(modules, bareOps) {
2795
2838
  if (err instanceof PermissionDenied) {
2796
2839
  this.recordDenial(attachSubject(principal, connectionId), tenantId, 'attachments.upload', err, null);
2797
2840
  }
2798
- throw toRpcError(err);
2841
+ throw err;
2799
2842
  }
2800
2843
  // #1525: null. An attachment RPC carries no invocation, exactly as its denial
2801
2844
  // records none — so the deliveries it drains name no call rather than a wrong one.
@@ -2804,7 +2847,7 @@ export function defineScopeDO(modules, bareOps) {
2804
2847
  });
2805
2848
  }
2806
2849
  /** Records for one entity, newest first — read gate first. */
2807
- async attachmentList(entity, principal, tenantId, scopeId, connectionId) {
2850
+ async listAttachments(entity, principal, tenantId, scopeId, connectionId) {
2808
2851
  await this.ensureMigrations();
2809
2852
  const gate = this.attachmentGate(entity.entityType);
2810
2853
  try {
@@ -2815,7 +2858,7 @@ export function defineScopeDO(modules, bareOps) {
2815
2858
  if (err instanceof PermissionDenied) {
2816
2859
  this.recordDenial(attachSubject(principal, connectionId), tenantId, 'attachments.list', err, null);
2817
2860
  }
2818
- throw toRpcError(err);
2861
+ throw err;
2819
2862
  }
2820
2863
  const rows = this.sql
2821
2864
  .exec(`SELECT id FROM _substrat_attachments WHERE entity_type = ? AND entity_id = ?
@@ -2827,7 +2870,7 @@ export function defineScopeDO(modules, bareOps) {
2827
2870
  * Authorize one attachment (`read` before bytes are served, `write` before a remove is
2828
2871
  * attempted elsewhere) and return its record — null for an id this scope does not know.
2829
2872
  */
2830
- async attachmentAuthorize(attachmentId, mode, principal, tenantId, scopeId, connectionId, forEventId) {
2873
+ async authorizeAttachment(attachmentId, mode, principal, tenantId, scopeId, connectionId, forEventId) {
2831
2874
  await this.ensureMigrations();
2832
2875
  const record = this.attachmentRow(attachmentId);
2833
2876
  if (!record)
@@ -2846,7 +2889,7 @@ export function defineScopeDO(modules, bareOps) {
2846
2889
  if (err instanceof PermissionDenied) {
2847
2890
  this.recordDenial(attachSubject(principal, connectionId), tenantId, 'attachments.open', err, null);
2848
2891
  }
2849
- throw toRpcError(err);
2892
+ throw err;
2850
2893
  }
2851
2894
  return record;
2852
2895
  }
@@ -2859,12 +2902,12 @@ export function defineScopeDO(modules, bareOps) {
2859
2902
  if (err instanceof PermissionDenied) {
2860
2903
  this.recordDenial(attachSubject(principal, connectionId), tenantId, 'attachments.open', err, null);
2861
2904
  }
2862
- throw toRpcError(err);
2905
+ throw err;
2863
2906
  }
2864
2907
  return record;
2865
2908
  }
2866
2909
  /** Authorize a job's read as its registered module, never as a person. */
2867
- async systemAttachmentAuthorize(attachmentId, moduleId, tenantId, scopeId,
2910
+ async authorizeSystemAttachment(attachmentId, moduleId, tenantId, scopeId,
2868
2911
  /** #1834: the instance the system door's gate read; see `invoke`. */
2869
2912
  systemDoorInstance) {
2870
2913
  await this.ensureMigrations();
@@ -2875,10 +2918,10 @@ export function defineScopeDO(modules, bareOps) {
2875
2918
  catch (err) {
2876
2919
  if (err instanceof SystemDoorMovedError)
2877
2920
  return SYSTEM_DOOR_MOVED;
2878
- throw toRpcError(err);
2921
+ throw err;
2879
2922
  }
2880
2923
  if (!this.modules.has(moduleId)) {
2881
- throw toRpcError(substratError('not_found', `module not registered in this scope: ${moduleId}`));
2924
+ throw substratError('not_found', `module not registered in this scope: ${moduleId}`);
2882
2925
  }
2883
2926
  const record = this.attachmentRow(attachmentId);
2884
2927
  if (!record)
@@ -2892,12 +2935,12 @@ export function defineScopeDO(modules, bareOps) {
2892
2935
  if (err instanceof PermissionDenied) {
2893
2936
  this.recordDenial({ kind: 'system', id: moduleId }, tenantId, 'attachments.open', err, null);
2894
2937
  }
2895
- throw toRpcError(err);
2938
+ throw err;
2896
2939
  }
2897
2940
  return record;
2898
2941
  }
2899
2942
  /** Remove an attachment's metadata fact: write gate + delete + `attachment.removed`. */
2900
- async attachmentRemove(attachmentId, principal, tenantId, scopeId, connectionId) {
2943
+ async removeAttachment(attachmentId, principal, tenantId, scopeId, connectionId) {
2901
2944
  await this.ensureMigrations();
2902
2945
  return this.queue.enqueue(async () => {
2903
2946
  const record = this.attachmentRow(attachmentId);
@@ -2924,7 +2967,7 @@ export function defineScopeDO(modules, bareOps) {
2924
2967
  if (err instanceof PermissionDenied) {
2925
2968
  this.recordDenial(attachSubject(principal, connectionId), tenantId, 'attachments.remove', err, null);
2926
2969
  }
2927
- throw toRpcError(err);
2970
+ throw err;
2928
2971
  }
2929
2972
  // #1525: null. An attachment RPC carries no invocation, exactly as its denial
2930
2973
  // records none — so the deliveries it drains name no call rather than a wrong one.
@@ -3530,6 +3573,7 @@ export function defineScopeDO(modules, bareOps) {
3530
3573
  holdings: this.checker.holdings
3531
3574
  ? (principal) => this.checker.holdings({ kind: 'principal', id: principal }, { tenantId, scopeId })
3532
3575
  : undefined,
3576
+ allowed: (principal, permission, entity) => allowedFor(this.checker, principal, { tenantId, scopeId })(permission, entity),
3533
3577
  }, secret, mode);
3534
3578
  });
3535
3579
  if (outcome)
@@ -3548,7 +3592,7 @@ export function defineScopeDO(modules, bareOps) {
3548
3592
  const minter = principalId.parse(caller);
3549
3593
  const parsed = principalBecomeCapabilityInput.parse(input);
3550
3594
  return await this.queue.enqueue(async () => {
3551
- const checked = await becomeMintCheck({ sql: doSpineSql(this.sql), checker: this.checker }, minter, parsed.principal, { tenantId, scopeId });
3595
+ const checked = await becomeMintCheck({ sql: doSpineSql(this.sql), checker: this.checker }, minter, parsed.principal, { tenantId, scopeId }, parsed.bind);
3552
3596
  if (!checked.ok)
3553
3597
  return checked;
3554
3598
  const liveSince = this.liveHighWaterMark();
@@ -3565,6 +3609,32 @@ export function defineScopeDO(modules, bareOps) {
3565
3609
  return { ok: true, minted: minted };
3566
3610
  });
3567
3611
  }
3612
+ /**
3613
+ * Unbind a binding shape's record (#1686) — its column cleared and the shape its person held
3614
+ * there through it taken back — as `caller`, who must hold the binding's key on the record. The
3615
+ * kernel's bound and the write in ONE queued body and one storage transaction; a refusal writes
3616
+ * nothing. On the spine as `entity.holder-unbound`, the caller its actor.
3617
+ */
3618
+ async unbindEntityHolderBoundedFor(tenantId, scopeId, caller, entity) {
3619
+ await this.ensureMigrations();
3620
+ const by = principalId.parse(caller);
3621
+ return await this.queue.enqueue(async () => {
3622
+ const liveSince = this.liveHighWaterMark();
3623
+ const now = instant.parse(new Date().toISOString());
3624
+ let answer = { ok: true, unbound: [] };
3625
+ await this.revision.transaction(async () => {
3626
+ answer = await unbindEntityHolderBounded({
3627
+ sql: doSpineSql(this.sql),
3628
+ now,
3629
+ allowed: allowedFor(this.checker, by, { tenantId, scopeId }),
3630
+ emit: (event) => kernelEmit(this.operationContext(by, tenantId, scopeId, undefined, undefined, undefined, undefined, undefined, ENTITY_UNBIND_OPERATION, undefined, [], now), event),
3631
+ }, entity);
3632
+ });
3633
+ if (answer.ok && answer.unbound.length > 0)
3634
+ await this.settleCommitted(tenantId, scopeId, liveSince, null);
3635
+ return answer;
3636
+ });
3637
+ }
3568
3638
  /** Where each named `become` link stands (#1686) — a pending-invite list's read. */
3569
3639
  async becomeLinkStatesFor(tenantId, scopeId, ids) {
3570
3640
  await this.ensureMigrations();
@@ -4510,12 +4580,7 @@ export function defineScopeDO(modules, bareOps) {
4510
4580
  /** A bounded page of one table. Unknown table names throw — never queried blind. Kept for a
4511
4581
  * coordinator from before #113; the current one calls `introspectTableReply`. */
4512
4582
  introspectTable(table, limit, offset) {
4513
- try {
4514
- return this.tablePage(table, limit, offset);
4515
- }
4516
- catch (e) {
4517
- throw toRpcError(e);
4518
- }
4583
+ return thrownForRpc(async () => this.tablePage(table, limit, offset));
4519
4584
  }
4520
4585
  /** `introspectTable`, its refusal answered as DATA so its code survives the hop (#113). */
4521
4586
  introspectTableReply(table, limit, offset) {
@@ -4547,13 +4612,8 @@ export function defineScopeDO(modules, bareOps) {
4547
4612
  * at SCOPE_QUERY_ROW_MAX with `truncated` set, never an error.
4548
4613
  * Kept for a coordinator from before #113; the current one calls `introspectQueryReply`.
4549
4614
  */
4550
- async introspectQuery(sql) {
4551
- try {
4552
- return await this.readOnlyQuery(sql);
4553
- }
4554
- catch (e) {
4555
- throw toRpcError(e);
4556
- }
4615
+ introspectQuery(sql) {
4616
+ return thrownForRpc(() => this.readOnlyQuery(sql));
4557
4617
  }
4558
4618
  /** `introspectQuery`, its refusal answered as DATA so its code survives the hop (#113). */
4559
4619
  introspectQueryReply(sql) {
@@ -4711,6 +4771,8 @@ export function defineScopeDO(modules, bareOps) {
4711
4771
  'ALTER TABLE _substrat_capabilities ADD COLUMN target_digest TEXT',
4712
4772
  // #1686: why the kernel revoked a link itself; NULL for every revoke already there.
4713
4773
  'ALTER TABLE _substrat_capabilities ADD COLUMN revoked_reason TEXT',
4774
+ // #1686: the action a become carries; NULL on every row already there — none carries one.
4775
+ 'ALTER TABLE _substrat_capabilities ADD COLUMN action TEXT',
4714
4776
  // #1632: legacy runs retain an unknown subject; no content-based backfill.
4715
4777
  'ALTER TABLE _substrat_job_runs ADD COLUMN subject_id TEXT',
4716
4778
  // #2034: the lease. NULL = nobody holds the run, which is right for every row already there.