@cat-factory/gatekeeper-worker 0.5.0 → 0.6.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.
Files changed (73) hide show
  1. package/README.md +43 -2
  2. package/dist/arguments.d.ts +18 -0
  3. package/dist/arguments.d.ts.map +1 -0
  4. package/dist/arguments.js +75 -0
  5. package/dist/arguments.js.map +1 -0
  6. package/dist/capability.d.ts +10 -0
  7. package/dist/capability.d.ts.map +1 -1
  8. package/dist/capability.js +39 -1
  9. package/dist/capability.js.map +1 -1
  10. package/dist/env.d.ts +10 -0
  11. package/dist/env.d.ts.map +1 -1
  12. package/dist/env.js +12 -0
  13. package/dist/env.js.map +1 -1
  14. package/dist/errors.d.ts +20 -1
  15. package/dist/errors.d.ts.map +1 -1
  16. package/dist/errors.js +11 -0
  17. package/dist/errors.js.map +1 -1
  18. package/dist/gatekeeper.d.ts +31 -1
  19. package/dist/gatekeeper.d.ts.map +1 -1
  20. package/dist/gatekeeper.js +38 -8
  21. package/dist/gatekeeper.js.map +1 -1
  22. package/dist/index.d.ts +8 -4
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +5 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/os/discoverability.d.ts +13 -1
  27. package/dist/os/discoverability.d.ts.map +1 -1
  28. package/dist/os/discoverability.js +23 -6
  29. package/dist/os/discoverability.js.map +1 -1
  30. package/dist/os/exports.d.ts +27 -0
  31. package/dist/os/exports.d.ts.map +1 -1
  32. package/dist/os/exports.js +25 -0
  33. package/dist/os/exports.js.map +1 -1
  34. package/dist/os/hook-controller.d.ts +14 -0
  35. package/dist/os/hook-controller.d.ts.map +1 -0
  36. package/dist/os/hook-controller.js +53 -0
  37. package/dist/os/hook-controller.js.map +1 -0
  38. package/dist/os/hooks.d.ts +200 -0
  39. package/dist/os/hooks.d.ts.map +1 -0
  40. package/dist/os/hooks.js +253 -0
  41. package/dist/os/hooks.js.map +1 -0
  42. package/dist/os/protocol.d.ts +43 -0
  43. package/dist/os/protocol.d.ts.map +1 -1
  44. package/dist/os/queue.d.ts +9 -0
  45. package/dist/os/queue.d.ts.map +1 -1
  46. package/dist/os/queue.js +44 -1
  47. package/dist/os/queue.js.map +1 -1
  48. package/dist/os/resource-core.d.ts +25 -10
  49. package/dist/os/resource-core.d.ts.map +1 -1
  50. package/dist/os/resource-core.js +31 -14
  51. package/dist/os/resource-core.js.map +1 -1
  52. package/dist/os/resource.d.ts.map +1 -1
  53. package/dist/os/resource.js +7 -1
  54. package/dist/os/resource.js.map +1 -1
  55. package/dist/os/session-types.d.ts.map +1 -1
  56. package/dist/os/session-types.js +28 -0
  57. package/dist/os/session-types.js.map +1 -1
  58. package/dist/os/sharing.d.ts +35 -0
  59. package/dist/os/sharing.d.ts.map +1 -0
  60. package/dist/os/sharing.js +135 -0
  61. package/dist/os/sharing.js.map +1 -0
  62. package/dist/os/vendor.d.ts +1 -1
  63. package/dist/os/vendor.d.ts.map +1 -1
  64. package/dist/os/vendor.js +8 -1
  65. package/dist/os/vendor.js.map +1 -1
  66. package/dist/state.d.ts +80 -1
  67. package/dist/state.d.ts.map +1 -1
  68. package/dist/state.js +234 -6
  69. package/dist/state.js.map +1 -1
  70. package/dist/worker.d.ts.map +1 -1
  71. package/dist/worker.js +4 -1
  72. package/dist/worker.js.map +1 -1
  73. package/package.json +3 -3
@@ -29,7 +29,13 @@ export function createGatekeeperResource(options) {
29
29
  // ONE core for the object's lifetime, because the action ledger inside it is what a later
30
30
  // `applyAction` settles: a core rebuilt per call would have forgotten the action the decision
31
31
  // is about. Field initializers run after the base constructor, so `ctx` and `env` are set.
32
- #core = new ResourceCore(this.env, policy, this.ctx.props);
32
+ //
33
+ // The exports bag is handed down because this shell is the only thing that has one: a hook's
34
+ // controller is one of the Worker's own named exports, resolved against the DEPLOYMENT's entry
35
+ // module, and the core is deliberately drivable without a Durable Object at all.
36
+ #core = new ResourceCore(this.env, policy, this.ctx.props, {
37
+ exports: this.ctx.exports,
38
+ });
33
39
  async describe() {
34
40
  return this.#core.describe();
35
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resource.js","sourceRoot":"","sources":["../../src/os/resource.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,4BAA4B;AAC5B,EAAE;AACF,kGAAkG;AAClG,8FAA8F;AAC9F,kGAAkG;AAClG,oGAAoG;AACpG,iGAAiG;AACjG,+FAA+F;AAC/F,wEAAwE;AACxE,EAAE;AACF,kGAAkG;AAClG,iGAAiG;AACjG,6FAA6F;AAC7F,gFAAgF;AAEhF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIlD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,YAAY,EAAsB,MAAM,oBAAoB,CAAA;AAIrE;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAExC;IACC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAE1B,OAAO,MAAM,kBAAmB,SAAQ,aAA2C;QACjF,0FAA0F;QAC1F,8FAA8F;QAC9F,2FAA2F;QAClF,KAAK,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAEnE,KAAK,CAAC,QAAQ;YACZ,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAA;QAC9B,CAAC;QAED,KAAK,CAAC,kBAAkB;YACtB,OAAO,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAA;QACxC,CAAC;QAED,6FAA6F;QAC7F,wFAAwF;QACxF,KAAK,CAAC,YAAY,CAAC,aAA4B;YAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAA;QAC1D,CAAC;QAED,KAAK,CAAC,wBAAwB;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,wBAAwB,EAAE,CAAA;QAC9C,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,MAAc;YAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QACvC,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,MAAc;YAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QACxC,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,MAAc;YAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QACxC,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,IAAa;YACzC,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,CAAA;QACzC,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,EAAU;YAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,CAAA;QACtC,CAAC;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"resource.js","sourceRoot":"","sources":["../../src/os/resource.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,4BAA4B;AAC5B,EAAE;AACF,kGAAkG;AAClG,8FAA8F;AAC9F,kGAAkG;AAClG,oGAAoG;AACpG,iGAAiG;AACjG,+FAA+F;AAC/F,wEAAwE;AACxE,EAAE;AACF,kGAAkG;AAClG,iGAAiG;AACjG,6FAA6F;AAC7F,gFAAgF;AAEhF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIlD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,YAAY,EAAsB,MAAM,oBAAoB,CAAA;AAIrE;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAExC;IACC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAE1B,OAAO,MAAM,kBAAmB,SAAQ,aAA2C;QACjF,0FAA0F;QAC1F,8FAA8F;QAC9F,2FAA2F;QAC3F,EAAE;QACF,6FAA6F;QAC7F,+FAA+F;QAC/F,iFAAiF;QACxE,KAAK,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE;YAClE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO;SAC1B,CAAC,CAAA;QAEF,KAAK,CAAC,QAAQ;YACZ,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAA;QAC9B,CAAC;QAED,KAAK,CAAC,kBAAkB;YACtB,OAAO,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAA;QACxC,CAAC;QAED,6FAA6F;QAC7F,wFAAwF;QACxF,KAAK,CAAC,YAAY,CAAC,aAA4B;YAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAA;QAC1D,CAAC;QAED,KAAK,CAAC,wBAAwB;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,wBAAwB,EAAE,CAAA;QAC9C,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,MAAc;YAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QACvC,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,MAAc;YAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QACxC,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,MAAc;YAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QACxC,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,IAAa;YACzC,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,CAAA;QACzC,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,EAAU;YAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,CAAA;QACtC,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"session-types.d.ts","sourceRoot":"","sources":["../../src/os/session-types.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAExD,gGAAgG;AAChG,eAAO,MAAM,sBAAsB,wBAAwB,CAAA;AA2D3D,wDAAwD;AACxD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAUjE"}
1
+ {"version":3,"file":"session-types.d.ts","sourceRoot":"","sources":["../../src/os/session-types.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAExD,gGAAgG;AAChG,eAAO,MAAM,sBAAsB,wBAAwB,CAAA;AAuF3D,wDAAwD;AACxD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAUjE"}
@@ -66,6 +66,34 @@ const RESERVED_MEMBERS = [
66
66
  `,
67
67
  ` /** Every run this Gatekeeper has been pushed lifecycle events for. */
68
68
  runs_watched(): Promise<unknown[]>
69
+ `,
70
+ ` /**
71
+ * Be pushed each approval card as it is raised or settled, instead of polling
72
+ * \`approvals_list()\`. The callback needs one method:
73
+ * \`onApprovalCard(card): Promise<void>\`.
74
+ *
75
+ * The workspace holds the registration and may ask a person before enabling it, so binding is
76
+ * not receiving: nothing arrives until it is enabled, and \`hooks_bound()\` is where that shows.
77
+ */
78
+ approvals_subscribe(callback: unknown): Promise<void>
79
+ `,
80
+ ` /**
81
+ * Be pushed each run lifecycle transition, instead of polling \`runs_watched()\`. The callback
82
+ * needs one method: \`onRunEvent(state): Promise<void>\`.
83
+ */
84
+ runs_subscribe(callback: unknown): Promise<void>
85
+ `,
86
+ ` /**
87
+ * Every hook this account has enabled, with what each has been pushed (\`deliveries\`), what it
88
+ * could not be pushed (\`missed\`), what the workspace's own side refused (\`failures\`) and
89
+ * whether it is still \`live\`.
90
+ *
91
+ * A hook goes quiet when this Gatekeeper's durable object is evicted between deliveries, since
92
+ * the workspace's callback source cannot be stored. That reads as \`live: false\` with a rising
93
+ * \`missed\`, and the remedy is to bind again; what was missed is still in
94
+ * \`approvals_list()\` and \`runs_watched()\`, which is why they remain the truth.
95
+ */
96
+ hooks_bound(): Promise<unknown[]>
69
97
  `,
70
98
  ];
71
99
  /** Render the session `.d.ts` for one compiled tier. */
@@ -1 +1 @@
1
- {"version":3,"file":"session-types.js","sourceRoot":"","sources":["../../src/os/session-types.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,EAAE;AACF,mFAAmF;AACnF,gGAAgG;AAChG,iGAAiG;AACjG,iGAAiG;AACjG,iGAAiG;AACjG,oCAAoC;AACpC,EAAE;AACF,mGAAmG;AACnG,6EAA6E;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAGrE,gGAAgG;AAChG,MAAM,CAAC,MAAM,sBAAsB,GAAG,qBAAqB,CAAA;AAE3D;;;;;;;GAOG;AACH,MAAM,gBAAgB,GAAsB;IAC1C;;CAED;IACC;;;;;;;;;;;;;;;;CAgBD;IACC;;;;;;CAMD;IACC;;CAED;IACC;;;;;CAKD;IACC;;;;;;CAMD;IACC;;CAED;CACA,CAAA;AAED,wDAAwD;AACxD,MAAM,UAAU,sBAAsB,CAAC,IAAkB;IACvD,OAAO,kBAAkB,CAAC;QACxB,aAAa,EAAE,sBAAsB;QACrC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;QACrD,YAAY,EAAE,gBAAgB;QAC9B,QAAQ,EACN,oCAAoC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,WAAW,IAAI;YACxE,2DAA2D,IAAI,CAAC,QAAQ,wBAAwB;YAChG,yDAAyD;KAC5D,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"session-types.js","sourceRoot":"","sources":["../../src/os/session-types.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,EAAE;AACF,mFAAmF;AACnF,gGAAgG;AAChG,iGAAiG;AACjG,iGAAiG;AACjG,iGAAiG;AACjG,oCAAoC;AACpC,EAAE;AACF,mGAAmG;AACnG,6EAA6E;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAGrE,gGAAgG;AAChG,MAAM,CAAC,MAAM,sBAAsB,GAAG,qBAAqB,CAAA;AAE3D;;;;;;;GAOG;AACH,MAAM,gBAAgB,GAAsB;IAC1C;;CAED;IACC;;;;;;;;;;;;;;;;CAgBD;IACC;;;;;;CAMD;IACC;;CAED;IACC;;;;;CAKD;IACC;;;;;;CAMD;IACC;;CAED;IACC;;;;;;;;;CASD;IACC;;;;;CAKD;IACC;;;;;;;;;;;CAWD;CACA,CAAA;AAED,wDAAwD;AACxD,MAAM,UAAU,sBAAsB,CAAC,IAAkB;IACvD,OAAO,kBAAkB,CAAC;QACxB,aAAa,EAAE,sBAAsB;QACrC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;QACrD,YAAY,EAAE,gBAAgB;QAC9B,QAAQ,EACN,oCAAoC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,WAAW,IAAI;YACxE,2DAA2D,IAAI,CAAC,QAAQ,wBAAwB;YAChG,yDAAyD;KAC5D,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { CompiledTier } from '../policy/compile.js';
2
+ /** What the caller supplies so a claimed account can be checked against the ones this Gatekeeper made. */
3
+ export interface ObserverRecognition {
4
+ /** Whether this deployment minted the named account. Never a guess: an unknown id is refused. */
5
+ recognize: (accountId: string) => Promise<boolean>;
6
+ }
7
+ /**
8
+ * Resolve the account behind an observer's verifier, or refuse.
9
+ *
10
+ * The verifier is the contract's own identity token: another vendor's gatekeeper is handed one,
11
+ * and the only thing it carries is who its holder is. Ours answers `{ accountId }`, so a stub that
12
+ * answers anything else did not come from this vendor and cannot be resolved against this
13
+ * deployment's policy. Answering the right SHAPE is not the same as being one of ours, which is
14
+ * why the id is then checked against the accounts this deployment actually minted.
15
+ *
16
+ * What that does NOT establish is worth stating, because the rule above is only as strong as this:
17
+ * a verifier that names an account of ours is taken at its word. The stub is reached over RPC from
18
+ * whoever the workspace handed us, so proving the holder IS that account would take a challenge
19
+ * the published contract does not carry. What is closed here is the case that needs no
20
+ * impersonation at all, which was every honest viewer from every other vendor.
21
+ */
22
+ export declare function identifyObserver(user: unknown, deps: ObserverRecognition): Promise<string>;
23
+ /**
24
+ * Refuse the share unless the observer could have read all of it themselves.
25
+ *
26
+ * Takes the two COMPILED tiers rather than resolving them, so the rule is testable without a
27
+ * policy, a Worker or an account: what it decides is a comparison, and everything else is lookup.
28
+ */
29
+ export declare function assertObserverMaySee(deps: {
30
+ observerId: string;
31
+ observerAccount: string;
32
+ owner: CompiledTier;
33
+ observer: CompiledTier;
34
+ }): void;
35
+ //# sourceMappingURL=sharing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sharing.d.ts","sourceRoot":"","sources":["../../src/os/sharing.ts"],"names":[],"mappings":"AAgDA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAUxD,0GAA0G;AAC1G,MAAM,WAAW,mBAAmB;IAClC,iGAAiG;IACjG,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACnD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsChG;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;IACvB,KAAK,EAAE,YAAY,CAAA;IACnB,QAAQ,EAAE,YAAY,CAAA;CACvB,GAAG,IAAI,CAqCP"}
@@ -0,0 +1,135 @@
1
+ // Whether one account may be shown what another has already read through this resource.
2
+ //
3
+ // The contract's requirement is exact and it is not "is this person trusted": `addObserver` must
4
+ // verify that the new viewer could DIRECTLY READ everything historically observed through the
5
+ // resource, and throw to block the share otherwise. Slice 8 shipped the blanket refusal, which was
6
+ // the right default while there was no rule worth writing down. This is the rule.
7
+ //
8
+ // It rests on one fact about this Gatekeeper that makes the question answerable at all: a resource
9
+ // object is bound FOR ONE ACCOUNT (`ResourceProps.accountId`), so every observation ever made
10
+ // through it was made at that account's tier. There is no observation log and there does not need
11
+ // to be one, because the tier is an upper bound on what any of those reads could have returned.
12
+ // So the test is a comparison of two compiled tiers:
13
+ //
14
+ // 1. The observer's tier GRANTS every operation the owner's tier grants. An operation the
15
+ // observer does not hold is one they could not have made themselves, whatever they are shown.
16
+ // 2. The observer's MASK hides nothing the owner's does not. Masking is redaction on the way
17
+ // out, so a field the observer's tier blanks is a field they cannot read directly, even
18
+ // through an operation they hold.
19
+ // 3. The owner's tier reaches NO TELEMETRY SINK. Those reads serve captured agent text and are
20
+ // described with `prohibitAllSharing`, which says the data may not be shared onward with any
21
+ // observer whatever their own access is. Since nothing here records which reads were actually
22
+ // made, a tier that COULD have made one is a tier whose observations may include it.
23
+ //
24
+ // Every failure is a refusal that names which of the three it is, because they need different
25
+ // answers: a grant an operator can widen, a mask they can align, and one they must not "fix" at
26
+ // all. And an observer this Gatekeeper cannot identify is refused separately again, because
27
+ // "unknown" and "not permitted" are the same outcome and opposite facts.
28
+ //
29
+ // WHICH IS WHY IDENTIFYING ONE IS TWO QUESTIONS, not one. Reading an account id off the verifier
30
+ // answers what the observer CLAIMS; it does not answer whether this deployment has ever heard of
31
+ // them, and the tier lookup behind the comparison cannot tell the difference on its own. It
32
+ // resolves an unknown id to the auto-provisioned tier, which is the tier nearly every account
33
+ // here holds, so a viewer from an entirely different vendor (whose verifier honestly names an
34
+ // account of THEIRS) resolved to the same tier as the owner and passed all three tests without
35
+ // holding a single one of the operations they were being shown. The comparison was answering
36
+ // "would an account like this be able to read it", which is not a question anyone asked.
37
+ //
38
+ // So an observer must be an account this Gatekeeper MINTED. That is a fact only the deployment's
39
+ // own state knows, so the caller supplies the recogniser and this module supplies the refusal:
40
+ // the message it already carried ("Observers must hold an account on this same Gatekeeper") is
41
+ // now enforced rather than asserted.
42
+ //
43
+ // The rule is deliberately conservative in the one direction that costs nothing but a share:
44
+ // every accepted observer can read everything the owner's tier can, so no observation ever needs
45
+ // excluding afterwards and `ObservationDescription.excludeObservers` stays unused rather than
46
+ // being a second, weaker gate on the same question.
47
+ import { describeError, GatekeeperError } from '../errors.js';
48
+ /** How many withheld operations a refusal names before it stops listing them. */
49
+ const NAMED_OPERATIONS = 5;
50
+ /**
51
+ * Resolve the account behind an observer's verifier, or refuse.
52
+ *
53
+ * The verifier is the contract's own identity token: another vendor's gatekeeper is handed one,
54
+ * and the only thing it carries is who its holder is. Ours answers `{ accountId }`, so a stub that
55
+ * answers anything else did not come from this vendor and cannot be resolved against this
56
+ * deployment's policy. Answering the right SHAPE is not the same as being one of ours, which is
57
+ * why the id is then checked against the accounts this deployment actually minted.
58
+ *
59
+ * What that does NOT establish is worth stating, because the rule above is only as strong as this:
60
+ * a verifier that names an account of ours is taken at its word. The stub is reached over RPC from
61
+ * whoever the workspace handed us, so proving the holder IS that account would take a challenge
62
+ * the published contract does not carry. What is closed here is the case that needs no
63
+ * impersonation at all, which was every honest viewer from every other vendor.
64
+ */
65
+ export async function identifyObserver(user, deps) {
66
+ const verifier = user;
67
+ if (typeof verifier?.describe !== 'function') {
68
+ throw new GatekeeperError('sharing_unverifiable', 'The observer offered no verifier this Gatekeeper can question, so there is no account to ' +
69
+ 'resolve a tier for. A share is refused rather than granted to an identity nobody stated.');
70
+ }
71
+ let identity;
72
+ try {
73
+ identity = await verifier.describe();
74
+ }
75
+ catch (error) {
76
+ throw new GatekeeperError('sharing_unverifiable', `The observer's verifier could not be questioned (${describeError(error)}), so the share ` +
77
+ 'is refused: an identity this Gatekeeper failed to read is not an identity it may assume.');
78
+ }
79
+ if (typeof identity?.accountId !== 'string' || identity.accountId.length === 0) {
80
+ throw new GatekeeperError('sharing_unverifiable', "The observer's verifier named no account, so this deployment's policy has nothing to " +
81
+ 'resolve a tier against. Observers must hold an account on this same Gatekeeper.');
82
+ }
83
+ if (!(await deps.recognize(identity.accountId))) {
84
+ throw new GatekeeperError('sharing_unverifiable', `This Gatekeeper did not mint account '${identity.accountId}', so it holds no tier for it ` +
85
+ 'and cannot judge what its holder could read for themselves. A viewer connected to some ' +
86
+ 'other vendor is refused here rather than measured against the tier an account of ours ' +
87
+ 'would have had: that comparison would pass for a viewer holding none of these ' +
88
+ 'operations. Connect this deployment as a vendor from that workspace and share with the ' +
89
+ 'account it mints.');
90
+ }
91
+ return identity.accountId;
92
+ }
93
+ /**
94
+ * Refuse the share unless the observer could have read all of it themselves.
95
+ *
96
+ * Takes the two COMPILED tiers rather than resolving them, so the rule is testable without a
97
+ * policy, a Worker or an account: what it decides is a comparison, and everything else is lookup.
98
+ */
99
+ export function assertObserverMaySee(deps) {
100
+ const { owner, observer } = deps;
101
+ const captured = owner.granted.filter((binding) => binding.telemetrySink !== undefined);
102
+ if (captured.length > 0) {
103
+ throw new GatekeeperError('sharing_refused', `This resource is bound at tier '${owner.name}', which can read captured agent text ` +
104
+ `(${names(captured.map((binding) => binding.name))}). Those reads are marked as not ` +
105
+ 'shareable onward whatever the viewer holds, and nothing here records which of them were ' +
106
+ `actually made, so the share to '${deps.observerId}' is refused. A tier without the ` +
107
+ 'telemetry operations can be shared.');
108
+ }
109
+ const held = new Set(observer.granted.map((binding) => binding.name));
110
+ const unreachable = owner.granted.filter((binding) => !held.has(binding.name));
111
+ if (unreachable.length > 0) {
112
+ throw new GatekeeperError('sharing_refused', `Account '${deps.observerAccount}' holds tier '${observer.name}', which does not grant ` +
113
+ `${names(unreachable.map((binding) => binding.name))}. This resource is bound at tier ` +
114
+ `'${owner.name}', so it may already have read those, and the share would show them what ` +
115
+ 'they cannot read for themselves.');
116
+ }
117
+ const ownerMask = new Set(owner.mask);
118
+ const extraMask = observer.mask.filter((path) => !ownerMask.has(path));
119
+ if (extraMask.length > 0) {
120
+ throw new GatekeeperError('sharing_refused', `Tier '${observer.name}' masks ${names(extraMask)}, which tier '${owner.name}' does not. A ` +
121
+ 'masked field is one this observer cannot read directly even through an operation they ' +
122
+ 'hold, so the share is refused rather than served with the redaction undone.');
123
+ }
124
+ }
125
+ /** A bounded list: enough to act on, never the whole surface pasted into a refusal. */
126
+ function names(all) {
127
+ const shown = all
128
+ .slice(0, NAMED_OPERATIONS)
129
+ .map((name) => `'${name}'`)
130
+ .join(', ');
131
+ return all.length > NAMED_OPERATIONS
132
+ ? `${shown} and ${all.length - NAMED_OPERATIONS} more`
133
+ : shown;
134
+ }
135
+ //# sourceMappingURL=sharing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sharing.js","sourceRoot":"","sources":["../../src/os/sharing.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { GatekeeperEnv } from '../env.js';
1
+ import { type GatekeeperEnv } from '../env.js';
2
2
  import type { GatekeeperPolicy } from '../policy/compile.js';
3
3
  import type { VendorEntrypoint } from './protocol.js';
4
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"vendor.d.ts","sourceRoot":"","sources":["../../src/os/vendor.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAE9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAG5D,OAAO,KAAK,EAAwC,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAe3F;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE;IAC9C,MAAM,EAAE,gBAAgB,CAAA;CACzB,GAAG,KAAK,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,aAAa,KAAK,gBAAgB,CA4DtE"}
1
+ {"version":3,"file":"vendor.d.ts","sourceRoot":"","sources":["../../src/os/vendor.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,WAAW,CAAA;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAG5D,OAAO,KAAK,EAAwC,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAe3F;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE;IAC9C,MAAM,EAAE,gBAAgB,CAAA;CACzB,GAAG,KAAK,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,aAAa,KAAK,gBAAgB,CAkEtE"}
package/dist/os/vendor.js CHANGED
@@ -16,6 +16,7 @@
16
16
  // against it. Nothing here is transcribed, so a surface change cannot leave the description of it
17
17
  // behind.
18
18
  import { WorkerEntrypoint } from 'cloudflare:workers';
19
+ import { stateFor } from '../env.js';
19
20
  import { Gatekeeper } from '../gatekeeper.js';
20
21
  import { loopbackExport } from './exports.js';
21
22
  import { supportedResourceFor } from './resources.js';
@@ -88,7 +89,13 @@ export function createGatekeeperVendor(options) {
88
89
  const props = { accountId: mintAccountId() };
89
90
  // Resolved before the account is handed back, so a deployment missing the export is refused
90
91
  // at the call that would have created the account rather than at the first use of it.
91
- return loopbackExport(this.ctx.exports, 'account', props);
92
+ const account = loopbackExport(this.ctx.exports, 'account', props);
93
+ // The one thing recorded about an account, and it is recorded HERE because this is the only
94
+ // moment the fact exists: minting is what makes an id one of ours, and `addObserver` has no
95
+ // other way to tell a viewer holding one from a viewer naming one. It lands before the stub
96
+ // does, so an account the workspace can use is never one this Gatekeeper would disown.
97
+ await stateFor(this.env).recordAccount(props.accountId, Date.now());
98
+ return account;
92
99
  }
93
100
  };
94
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vendor.js","sourceRoot":"","sources":["../../src/os/vendor.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,mGAAmG;AACnG,kGAAkG;AAClG,yFAAyF;AACzF,EAAE;AACF,iGAAiG;AACjG,8FAA8F;AAC9F,mGAAmG;AACnG,uFAAuF;AACvF,gGAAgG;AAChG,6FAA6F;AAC7F,+FAA+F;AAC/F,EAAE;AACF,iGAAiG;AACjG,kGAAkG;AAClG,UAAU;AAEV,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAG7C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAE3D;;;;;;GAMG;AACH,SAAS,aAAa;IACpB,OAAO,QAAQ,MAAM,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAA;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAEtC;IACC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAE1B,OAAO,MAAM,gBAAiB,SAAQ,gBAA+B;QACnE,WAAW;YACT,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QAC5C,CAAC;QAED,KAAK,CAAC,QAAQ;YACZ,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAA;YAChD,OAAO;gBACL,WAAW,EAAE,aAAa;gBAC1B,GAAG,EAAE,UAAU;gBACf,OAAO,EAAE,oCAAoC;gBAC7C,WAAW,EACT,yFAAyF;oBACzF,uFAAuF;oBACvF,kCAAkC;gBACpC,wFAAwF;gBACxF,oEAAoE;gBACpE,YAAY,EAAE,KAAK;gBACnB,oFAAoF;gBACpF,2EAA2E;gBAC3E,qBAAqB,EAAE,IAAI;aAC5B,CAAA;QACH,CAAC;QAED,KAAK,CAAC,qBAAqB,CAAC,QAA8B;YACxD,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;QAC9D,CAAC;QAED;;;;;;;;;;WAUG;QACH,KAAK,CAAC,kBAAkB;YACtB,OAAO,sBAAsB,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAA;QACvE,CAAC;QAED;;;;;;WAMG;QACH,KAAK,CAAC,aAAa;YACjB,MAAM,KAAK,GAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,CAAA;YAC1D,4FAA4F;YAC5F,sFAAsF;YACtF,OAAO,cAAc,CAAU,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;QACpE,CAAC;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"vendor.js","sourceRoot":"","sources":["../../src/os/vendor.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,mGAAmG;AACnG,kGAAkG;AAClG,yFAAyF;AACzF,EAAE;AACF,iGAAiG;AACjG,8FAA8F;AAC9F,mGAAmG;AACnG,uFAAuF;AACvF,gGAAgG;AAChG,6FAA6F;AAC7F,+FAA+F;AAC/F,EAAE;AACF,iGAAiG;AACjG,kGAAkG;AAClG,UAAU;AAEV,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,QAAQ,EAAsB,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAG7C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAE3D;;;;;;GAMG;AACH,SAAS,aAAa;IACpB,OAAO,QAAQ,MAAM,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAA;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAEtC;IACC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAE1B,OAAO,MAAM,gBAAiB,SAAQ,gBAA+B;QACnE,WAAW;YACT,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QAC5C,CAAC;QAED,KAAK,CAAC,QAAQ;YACZ,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAA;YAChD,OAAO;gBACL,WAAW,EAAE,aAAa;gBAC1B,GAAG,EAAE,UAAU;gBACf,OAAO,EAAE,oCAAoC;gBAC7C,WAAW,EACT,yFAAyF;oBACzF,uFAAuF;oBACvF,kCAAkC;gBACpC,wFAAwF;gBACxF,oEAAoE;gBACpE,YAAY,EAAE,KAAK;gBACnB,oFAAoF;gBACpF,2EAA2E;gBAC3E,qBAAqB,EAAE,IAAI;aAC5B,CAAA;QACH,CAAC;QAED,KAAK,CAAC,qBAAqB,CAAC,QAA8B;YACxD,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;QAC9D,CAAC;QAED;;;;;;;;;;WAUG;QACH,KAAK,CAAC,kBAAkB;YACtB,OAAO,sBAAsB,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAA;QACvE,CAAC;QAED;;;;;;WAMG;QACH,KAAK,CAAC,aAAa;YACjB,MAAM,KAAK,GAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,CAAA;YAC1D,4FAA4F;YAC5F,sFAAsF;YACtF,MAAM,OAAO,GAAG,cAAc,CAAU,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;YAC3E,4FAA4F;YAC5F,4FAA4F;YAC5F,4FAA4F;YAC5F,uFAAuF;YACvF,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;YACnE,OAAO,OAAO,CAAA;QAChB,CAAC;KACF,CAAA;AACH,CAAC"}
package/dist/state.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { DurableObject } from 'cloudflare:workers';
2
+ import { type HookDispatchReport, type HookPushTarget, type HookRecord, type HookRegistration } from './os/hooks.js';
3
+ import type { HookInitiator } from './os/protocol.js';
2
4
  /** A parked cat-factory decision, as the OS shows it. */
3
5
  export interface ApprovalCard {
4
6
  /** The delivery that raised it. Also the id the OS answers on. */
@@ -83,6 +85,18 @@ export type DeliveryApplication = {
83
85
  } | {
84
86
  applied: true;
85
87
  effect: 'opened' | 'superseded' | 'run-event' | 'none';
88
+ /**
89
+ * What this delivery CHANGED, for the hooks registered on each topic to be pushed about.
90
+ *
91
+ * Named here rather than re-derived by the dispatcher, for two reasons that are really one.
92
+ * A terminal run event both records the transition and settles the run's open cards, so it
93
+ * changes two topics' worth of state, and a dispatcher handed only the effect could see just
94
+ * the first: a card-subscribed gadget went on rendering settled cards as answerable, which
95
+ * is exactly what `HOOK_TOPICS.approval_card` tells an approver it will not do. And what the
96
+ * write actually changed is knowable only here, so a supersede that met a card already
97
+ * settled pushes nothing rather than re-announcing a transition that did not happen.
98
+ */
99
+ pushes: HookPushTarget[];
86
100
  };
87
101
  export declare class GatekeeperState extends DurableObject {
88
102
  #private;
@@ -95,13 +109,78 @@ export declare class GatekeeperState extends DurableObject {
95
109
  * re-deriving that on every replay would spend the platform's retry budget on it.
96
110
  */
97
111
  applyDelivery(deliveryId: string, effect: DeliveryEffect, now: number): Promise<DeliveryApplication>;
98
- /** Settle a card this Gatekeeper answered. A card already settled keeps its first resolution. */
112
+ /**
113
+ * Settle a card this Gatekeeper answered. A card already settled keeps its first resolution.
114
+ *
115
+ * The settlement is pushed before this returns, which is the opposite disposition from the
116
+ * webhook receiver's and for the reason that separates the two callers. There the fan-out is
117
+ * deferred because the platform's retry is waiting on the acknowledgement and would re-drive a
118
+ * delivery this Gatekeeper has already committed; here the caller is the session that just
119
+ * answered the card, nothing is retrying it, and a push bounded by its own per-hook deadline
120
+ * costs that one call rather than the record.
121
+ */
99
122
  resolveCard(cardId: string, resolution: string, now: number): Promise<ApprovalCard | null>;
100
123
  getCard(cardId: string): Promise<ApprovalCard | null>;
101
124
  /** Every card, newest first. Resolved ones are included: the OS decides what it renders. */
102
125
  listCards(): Promise<ApprovalCard[]>;
103
126
  /** The latest lifecycle state of every run a delivery has mentioned, newest first. */
104
127
  listRunStates(): Promise<RunState[]>;
128
+ /**
129
+ * The workspace enabled a hook: store the registration, and hold the live half beside it.
130
+ *
131
+ * The counters of an existing registration SURVIVE a re-enable. A workspace re-arms a hook
132
+ * exactly when it noticed one had gone quiet, and the number that told it so is `missed`;
133
+ * zeroing it on the way back up would delete the evidence at the one moment it is being acted
134
+ * on. What is replaced is the initiator, which the contract asks for by name.
135
+ */
136
+ enableHook(record: HookRecord, initiator: HookInitiator): Promise<void>;
137
+ /**
138
+ * The workspace withdrew a hook: both halves go, which is the permanent clean-up asked for.
139
+ *
140
+ * Found BY HOOK ID rather than addressed by it, because the row is keyed on the target while the
141
+ * controller a workspace persisted names the id it was bound under. A controller whose row was
142
+ * replaced by a later bind from the same gadget finds nothing and settles for releasing its own
143
+ * half, which is the right answer: it is being asked to withdraw a registration that a re-arm
144
+ * has already superseded, and deleting the successor's row would silence a live hook.
145
+ */
146
+ disableHook(hookId: string): Promise<void>;
147
+ /**
148
+ * The hooks enabled for one account, newest first, each with whether its live half survives.
149
+ *
150
+ * Scoped to the account rather than answering the object's whole set: a session reads this, and
151
+ * one account's gadget bindings are not another's to enumerate.
152
+ */
153
+ listHooks(accountId: string): Promise<HookRegistration[]>;
154
+ /**
155
+ * Push what a change produced to every hook registered for each payload's topic.
156
+ *
157
+ * Called AFTER the write it reports has committed, never as part of it: the card is the durable
158
+ * truth and the push is the accelerator over it, so an outbound call that hangs or refuses costs
159
+ * a notification and never the record of what the deployment reported. Each registration is
160
+ * pushed independently and concurrently, and every outcome lands on that registration's own
161
+ * counters, because one workspace's broken gadget must not decide whether another's hook fires
162
+ * or how long it waits to.
163
+ */
164
+ dispatchHooks(pushes: readonly HookPushTarget[], now: number): Promise<HookDispatchReport>;
165
+ /**
166
+ * Remember that this deployment minted an account.
167
+ *
168
+ * The one thing an account id has to be checkable for, and the reason it is the only account
169
+ * state here: `addObserver` is handed an id by a verifier and has to decide whether this
170
+ * Gatekeeper has ever heard of it. Nothing else consults this, because nothing else needs to. A
171
+ * session's account id rides `ctx.props` on a stub the workspace holds, so it was minted here by
172
+ * construction, and re-deriving that on every call would be a lookup with no question behind it.
173
+ */
174
+ recordAccount(accountId: string, now: number): Promise<void>;
175
+ /**
176
+ * Whether this deployment minted the named account.
177
+ *
178
+ * Never pruned, and that is deliberate rather than an omission: the workspace holds the account
179
+ * stub for as long as it likes, and an id whose row expired would read as an account this
180
+ * Gatekeeper never made. Revoking an account's keys does not remove it either, because what was
181
+ * revoked is a credential and what is asked here is a fact about who exists.
182
+ */
183
+ hasAccount(accountId: string): Promise<boolean>;
105
184
  /**
106
185
  * Either the actor's existing key, or an exclusive claim on minting one.
107
186
  *
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}