iterate 0.3.0 → 0.4.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 (241) hide show
  1. package/README.md +164 -82
  2. package/dist/{next/api.d.ts → api.d.ts} +197 -33
  3. package/dist/{next/app-server.d.ts → app-server.d.ts} +7 -0
  4. package/dist/{next/app-server.mjs → app-server.mjs} +17 -15
  5. package/dist/app-server.mjs.map +1 -0
  6. package/dist/{next/app-session.mjs → app-session.mjs} +12 -15
  7. package/dist/app-session.mjs.map +1 -0
  8. package/dist/{next/app.mjs → app.mjs} +53 -14
  9. package/dist/app.mjs.map +1 -0
  10. package/dist/{next/client → client}/live-state.d.ts +11 -11
  11. package/dist/client/oauth.d.ts +17 -0
  12. package/dist/{next/client → client}/react.d.ts +10 -42
  13. package/dist/{next/client → client}/socket.d.ts +1 -0
  14. package/dist/client.mjs +156 -4
  15. package/dist/client.mjs.map +1 -0
  16. package/dist/{next/expression.d.ts → expression.d.ts} +11 -69
  17. package/dist/{next/expression.mjs → expression.mjs} +11 -109
  18. package/dist/expression.mjs.map +1 -0
  19. package/dist/lib-BWr-5mFO.mjs +36 -0
  20. package/dist/lib-BWr-5mFO.mjs.map +1 -0
  21. package/dist/{next/lib.d.ts → lib.d.ts} +16 -2
  22. package/dist/{next/lib.mjs → lib.mjs} +32 -3
  23. package/dist/lib.mjs.map +1 -0
  24. package/dist/node.d.ts +15 -3
  25. package/dist/node.mjs +36 -174
  26. package/dist/node.mjs.map +1 -1
  27. package/dist/{next/oauth-scopes.mjs → oauth-scopes.mjs} +1 -1
  28. package/dist/oauth-scopes.mjs.map +1 -0
  29. package/dist/{next/oauth.mjs → oauth.mjs} +14 -2
  30. package/dist/oauth.mjs.map +1 -0
  31. package/dist/principal.d.ts +8 -0
  32. package/dist/principal.mjs +8 -0
  33. package/dist/principal.mjs.map +1 -0
  34. package/dist/project-ingress.d.ts +58 -0
  35. package/dist/project-ingress.mjs +104 -0
  36. package/dist/project-ingress.mjs.map +1 -0
  37. package/dist/{next/react.mjs → react.mjs} +12 -12
  38. package/dist/react.mjs.map +1 -0
  39. package/dist/sdk/auth.d.ts +25 -0
  40. package/dist/sdk/index.d.ts +155 -0
  41. package/dist/sdk/record-pipelined-steps.d.ts +19 -0
  42. package/dist/sdk.mjs +245 -2
  43. package/dist/sdk.mjs.map +1 -0
  44. package/dist/{next/stream → stream}/processor.d.ts +28 -23
  45. package/dist/{next/stream → stream}/processor.mjs +48 -25
  46. package/dist/stream/processor.mjs.map +1 -0
  47. package/dist/{next/stream → stream}/run.d.ts +9 -6
  48. package/dist/{next/stream → stream}/run.mjs +13 -8
  49. package/dist/stream/run.mjs.map +1 -0
  50. package/dist/stream/test-support.d.ts +45 -0
  51. package/dist/stream/test-support.mjs +196 -0
  52. package/dist/stream/test-support.mjs.map +1 -0
  53. package/package.json +65 -219
  54. package/THIRD_PARTY_NOTICES.md +0 -55
  55. package/bin/iterate.js +0 -94
  56. package/dist/api-url-B6404M82.mjs +0 -17
  57. package/dist/api-url-B6404M82.mjs.map +0 -1
  58. package/dist/app-ref-BipL0feU.mjs +0 -35
  59. package/dist/app-ref-BipL0feU.mjs.map +0 -1
  60. package/dist/app-ref-C1CrgXqX.mjs +0 -7
  61. package/dist/app-ref-C1CrgXqX.mjs.map +0 -1
  62. package/dist/app-ref-DYai_om1.mjs +0 -7
  63. package/dist/app-ref-DYai_om1.mjs.map +0 -1
  64. package/dist/cli-D0c-pDL_.mjs +0 -1010
  65. package/dist/cli-D0c-pDL_.mjs.map +0 -1
  66. package/dist/client.d.ts +0 -3
  67. package/dist/cloudflare-BTm90gQ4.mjs +0 -951
  68. package/dist/cloudflare-BTm90gQ4.mjs.map +0 -1
  69. package/dist/contract-s4FW4eES.mjs +0 -309
  70. package/dist/contract-s4FW4eES.mjs.map +0 -1
  71. package/dist/document-review/index.d.ts +0 -5
  72. package/dist/document-review/types.d.ts +0 -107
  73. package/dist/document-review.mjs +0 -7015
  74. package/dist/document-review.mjs.map +0 -1
  75. package/dist/durable-object-processor-durability-CNsTjAJS.mjs +0 -205
  76. package/dist/durable-object-processor-durability-CNsTjAJS.mjs.map +0 -1
  77. package/dist/idempotency-DleloJNt.mjs +0 -28
  78. package/dist/idempotency-DleloJNt.mjs.map +0 -1
  79. package/dist/index.d.mts +0 -5
  80. package/dist/index.mjs +0 -8
  81. package/dist/index.mjs.map +0 -1
  82. package/dist/itx/api-url.d.ts +0 -6
  83. package/dist/itx/itx-node-client.d.ts +0 -65
  84. package/dist/itx/itx-session.d.ts +0 -215
  85. package/dist/itx/owned-rpc-session.d.ts +0 -14
  86. package/dist/itx/query-client.d.ts +0 -10
  87. package/dist/itx-api.generated.d.ts +0 -6195
  88. package/dist/itx-session-sjud8GiT.mjs +0 -534
  89. package/dist/itx-session-sjud8GiT.mjs.map +0 -1
  90. package/dist/live-state-BJNqOwFw.mjs +0 -299
  91. package/dist/live-state-BJNqOwFw.mjs.map +0 -1
  92. package/dist/next/app-server.mjs.map +0 -1
  93. package/dist/next/app-session.mjs.map +0 -1
  94. package/dist/next/app.mjs.map +0 -1
  95. package/dist/next/client/oauth.d.ts +0 -12
  96. package/dist/next/client.mjs +0 -156
  97. package/dist/next/client.mjs.map +0 -1
  98. package/dist/next/expression.mjs.map +0 -1
  99. package/dist/next/lib.mjs.map +0 -1
  100. package/dist/next/oauth-scopes.mjs.map +0 -1
  101. package/dist/next/oauth.mjs.map +0 -1
  102. package/dist/next/principal.d.ts +0 -64
  103. package/dist/next/principal.mjs +0 -98
  104. package/dist/next/principal.mjs.map +0 -1
  105. package/dist/next/project-ingress.d.ts +0 -37
  106. package/dist/next/project-ingress.mjs +0 -75
  107. package/dist/next/project-ingress.mjs.map +0 -1
  108. package/dist/next/react.mjs.map +0 -1
  109. package/dist/next/sdk/auth.d.ts +0 -5
  110. package/dist/next/sdk/index.d.ts +0 -112
  111. package/dist/next/sdk.mjs +0 -139
  112. package/dist/next/sdk.mjs.map +0 -1
  113. package/dist/next/stream/processor.mjs.map +0 -1
  114. package/dist/next/stream/run.mjs.map +0 -1
  115. package/dist/next-node.d.ts +0 -15
  116. package/dist/next-node.mjs +0 -51
  117. package/dist/next-node.mjs.map +0 -1
  118. package/dist/processor-host-capabilities-BMFH3KTM.mjs +0 -56
  119. package/dist/processor-host-capabilities-BMFH3KTM.mjs.map +0 -1
  120. package/dist/processors/cloudflare.d.ts +0 -3
  121. package/dist/processors/durable-object-processor-durability.d.ts +0 -79
  122. package/dist/processors/event-consumption-metrics.d.ts +0 -82
  123. package/dist/processors/idempotency.d.ts +0 -13
  124. package/dist/processors/index.d.ts +0 -12
  125. package/dist/processors/processor-contracts.d.ts +0 -342
  126. package/dist/processors/processor-facet.d.ts +0 -186
  127. package/dist/processors/processor-host-capabilities.d.ts +0 -60
  128. package/dist/processors/prompt-sections.d.ts +0 -17
  129. package/dist/processors/rpc-types.d.ts +0 -515
  130. package/dist/processors/schemas.d.ts +0 -102
  131. package/dist/processors/stream-handle.d.ts +0 -45
  132. package/dist/processors/stream-processor-keepalive.d.ts +0 -95
  133. package/dist/processors/stream-processor-registry.d.ts +0 -233
  134. package/dist/processors/stream-processor-runner.d.ts +0 -289
  135. package/dist/processors/stream-processor.d.ts +0 -339
  136. package/dist/processors/stream-runtime-metrics.d.ts +0 -107
  137. package/dist/processors/testing.d.ts +0 -302
  138. package/dist/processors-BoNyeBfQ.mjs +0 -10
  139. package/dist/processors-BoNyeBfQ.mjs.map +0 -1
  140. package/dist/processors-cloudflare.mjs +0 -3
  141. package/dist/processors-testing.mjs +0 -435
  142. package/dist/processors-testing.mjs.map +0 -1
  143. package/dist/processors.mjs +0 -52
  144. package/dist/processors.mjs.map +0 -1
  145. package/dist/protocol-DnK_f2m6.mjs +0 -251
  146. package/dist/protocol-DnK_f2m6.mjs.map +0 -1
  147. package/dist/sdk/capnweb/index.d.ts +0 -2
  148. package/dist/sdk/capnweb/live-state/compact.d.ts +0 -5
  149. package/dist/sdk/capnweb/live-state/diff.d.ts +0 -41
  150. package/dist/sdk/capnweb/live-state/engine.d.ts +0 -44
  151. package/dist/sdk/capnweb/live-state/index.d.ts +0 -41
  152. package/dist/sdk/capnweb/live-state/protocol.d.ts +0 -87
  153. package/dist/sdk/capnweb/live-state/retain.d.ts +0 -23
  154. package/dist/sdk/capnweb/live-state/store.d.ts +0 -20
  155. package/dist/sdk/capnweb/live-state/types.d.ts +0 -11
  156. package/dist/sdk/capnweb/react.d.ts +0 -45
  157. package/dist/sdk/capnweb/react.mjs +0 -316
  158. package/dist/sdk/capnweb/react.mjs.map +0 -1
  159. package/dist/sdk/capnweb.mjs +0 -4
  160. package/dist/sdk/itx/react.d.ts +0 -191
  161. package/dist/sdk/itx/react.mjs +0 -383
  162. package/dist/sdk/itx/react.mjs.map +0 -1
  163. package/dist/sdk-DMB-IM11.mjs +0 -933
  164. package/dist/sdk-DMB-IM11.mjs.map +0 -1
  165. package/dist/sdk.d.ts +0 -339
  166. package/dist/serve-itx.d.ts +0 -46
  167. package/dist/starter-apps/flake-dashboard/app-ref.d.ts +0 -31
  168. package/dist/starter-apps/flake-dashboard/configured-worker.mjs +0 -1055
  169. package/dist/starter-apps/flake-dashboard/configured-worker.mjs.map +0 -1
  170. package/dist/starter-apps/flake-dashboard/contract.d.ts +0 -4839
  171. package/dist/starter-apps/flake-dashboard/contract.mjs +0 -2
  172. package/dist/starter-apps/flake-dashboard/index.d.ts +0 -17
  173. package/dist/starter-apps/flake-dashboard/index.mjs +0 -56
  174. package/dist/starter-apps/flake-dashboard/index.mjs.map +0 -1
  175. package/dist/starter-apps/flake-dashboard/worker.d.ts +0 -4607
  176. package/dist/starter-apps/github-ai-linter/ai-linter.d.ts +0 -8914
  177. package/dist/starter-apps/github-ai-linter/configured-worker.mjs +0 -17987
  178. package/dist/starter-apps/github-ai-linter/configured-worker.mjs.map +0 -1
  179. package/dist/starter-apps/github-ai-linter/contract.d.ts +0 -9193
  180. package/dist/starter-apps/github-ai-linter/index.d.ts +0 -10
  181. package/dist/starter-apps/github-ai-linter/index.mjs +0 -36
  182. package/dist/starter-apps/github-ai-linter/index.mjs.map +0 -1
  183. package/dist/starter-apps/github-ai-linter/prompt.d.ts +0 -13
  184. package/dist/starter-apps/github-ai-linter/review-bot.d.ts +0 -808
  185. package/dist/starter-apps/github-ai-linter/rules.d.ts +0 -34
  186. package/dist/starter-apps/github-ai-linter/worker-ref.d.ts +0 -19
  187. package/dist/starter-apps/github-ai-linter/worker.d.ts +0 -19
  188. package/dist/starter-apps/github-ai-linter/worker.mjs +0 -947
  189. package/dist/starter-apps/github-ai-linter/worker.mjs.map +0 -1
  190. package/dist/starter-apps/guestbook/app-ref.d.ts +0 -27
  191. package/dist/starter-apps/guestbook/client.d.ts +0 -7
  192. package/dist/starter-apps/guestbook/client.mjs +0 -59
  193. package/dist/starter-apps/guestbook/configured-worker.mjs +0 -205
  194. package/dist/starter-apps/guestbook/configured-worker.mjs.map +0 -1
  195. package/dist/starter-apps/guestbook/index.d.ts +0 -9
  196. package/dist/starter-apps/guestbook/index.mjs +0 -31
  197. package/dist/starter-apps/guestbook/index.mjs.map +0 -1
  198. package/dist/starter-apps/guestbook/processor.d.ts +0 -2267
  199. package/dist/starter-apps/guestbook/worker.d.ts +0 -26
  200. package/dist/starter-apps/guestbook/worker.mjs +0 -191
  201. package/dist/starter-apps/guestbook/worker.mjs.map +0 -1
  202. package/dist/starter-apps/media/configured-worker.mjs +0 -577
  203. package/dist/starter-apps/media/configured-worker.mjs.map +0 -1
  204. package/dist/starter-apps/media/index.mjs +0 -36
  205. package/dist/starter-apps/media/index.mjs.map +0 -1
  206. package/dist/starter-apps/media/ref.mjs +0 -20
  207. package/dist/starter-apps/media/ref.mjs.map +0 -1
  208. package/dist/starter-apps/media/worker.mjs +0 -579
  209. package/dist/starter-apps/media/worker.mjs.map +0 -1
  210. package/dist/starter-apps/notes/configured-worker.mjs +0 -6134
  211. package/dist/starter-apps/notes/configured-worker.mjs.map +0 -1
  212. package/dist/starter-apps/notes/index.mjs +0 -23
  213. package/dist/starter-apps/notes/index.mjs.map +0 -1
  214. package/dist/starter-apps/notes/ref.mjs +0 -21
  215. package/dist/starter-apps/notes/ref.mjs.map +0 -1
  216. package/dist/starter-apps/notes/worker.mjs +0 -427
  217. package/dist/starter-apps/notes/worker.mjs.map +0 -1
  218. package/dist/starter-apps/todo/client.mjs +0 -59
  219. package/dist/starter-apps/todo/configured-worker.mjs +0 -2864
  220. package/dist/starter-apps/todo/configured-worker.mjs.map +0 -1
  221. package/dist/starter-apps/todo/index.d.ts +0 -8
  222. package/dist/starter-apps/todo/index.mjs +0 -29
  223. package/dist/starter-apps/todo/index.mjs.map +0 -1
  224. package/dist/stream-processor-keepalive-DAQTP6m3.mjs +0 -2082
  225. package/dist/stream-processor-keepalive-DAQTP6m3.mjs.map +0 -1
  226. package/dist/usingCtx-mZx5nsAW.mjs +0 -11800
  227. package/dist/usingCtx-mZx5nsAW.mjs.map +0 -1
  228. package/dist/worker-ref-DZxPDmb_.mjs +0 -390
  229. package/dist/worker-ref-DZxPDmb_.mjs.map +0 -1
  230. package/dist/worker.d.mts +0 -33
  231. package/dist/worker.mjs +0 -18
  232. package/dist/worker.mjs.map +0 -1
  233. package/menubar/Iterate.entitlements +0 -12
  234. package/menubar/Iterate.swift +0 -914
  235. package/menubar/IterateIcon.swift +0 -145
  236. package/menubar/README.md +0 -28
  237. package/menubar/build-menubar-app.sh +0 -59
  238. /package/dist/{next/api.mjs → api.mjs} +0 -0
  239. /package/dist/{next/app-session.d.ts → app-session.d.ts} +0 -0
  240. /package/dist/{next/app.d.ts → app.d.ts} +0 -0
  241. /package/dist/{next/oauth-scopes.d.ts → oauth-scopes.d.ts} +0 -0
@@ -1,5 +1,6 @@
1
1
  export declare function openSocketWithRetry(url: string | URL, options?: {
2
2
  delaysMs?: readonly number[];
3
+ handshakeTimeoutMs?: number;
3
4
  /** the constructor to use — a test's fake, `WebSocket` otherwise */
4
5
  WebSocket?: typeof WebSocket;
5
6
  sleep?: (ms: number) => Promise<void>;
package/dist/client.mjs CHANGED
@@ -1,4 +1,156 @@
1
- import { a as disconnectIterateSession, c as reconnectIterateSession, d as retryFailedIterateSession, l as releaseItxConnection, m as watchItxConnection, n as connectIterateSession, o as isItxTransportError, r as connectItx, t as configureIterateSession, u as reportTransportSuspicion } from "./itx-session-sjud8GiT.mjs";
2
- import "./live-state-BJNqOwFw.mjs";
3
- import { o as applyPatch, r as createLiveStateStore, s as diff } from "./protocol-DnK_f2m6.mjs";
4
- export { applyPatch, configureIterateSession, connectIterateSession, connectItx, createLiveStateStore, diff, disconnectIterateSession, isItxTransportError, reconnectIterateSession, releaseItxConnection, reportTransportSuspicion, retryFailedIterateSession, watchItxConnection };
1
+ import { applyPatch } from "./lib.mjs";
2
+ import { z } from "zod";
3
+ //#region src/client/live-state.ts
4
+ /** The delta as it arrives over the wire — PARSED, never cast: `from`/`to` MUST be real numbers (a
5
+ * non-numeric rev would poison the held revision and silently wedge every later frame), and each
6
+ * patch op is a known RFC-6902-subset shape. A frame that fails this heals by re-reading the seed
7
+ * rather than being applied — the same recovery the store already runs on a revision gap. */
8
+ const LiveStateDeltaMessage = z.object({
9
+ key: z.string(),
10
+ from: z.number(),
11
+ to: z.number(),
12
+ patch: z.array(z.union([
13
+ z.object({
14
+ op: z.literal("add"),
15
+ path: z.string(),
16
+ value: z.unknown()
17
+ }),
18
+ z.object({
19
+ op: z.literal("replace"),
20
+ path: z.string(),
21
+ value: z.unknown()
22
+ }),
23
+ z.object({
24
+ op: z.literal("remove"),
25
+ path: z.string()
26
+ })
27
+ ])).nullable()
28
+ });
29
+ function createLiveStateStore() {
30
+ let held = {
31
+ rev: null,
32
+ state: void 0
33
+ };
34
+ const listeners = /* @__PURE__ */ new Set();
35
+ const notify = () => listeners.forEach((l) => l());
36
+ return {
37
+ get: () => held.state,
38
+ rev: () => held.rev,
39
+ subscribe: (listener) => {
40
+ listeners.add(listener);
41
+ return () => void listeners.delete(listener);
42
+ },
43
+ seed: (seed) => {
44
+ if (held.rev !== null && seed.rev < held.rev) return;
45
+ held = {
46
+ rev: seed.rev,
47
+ state: seed.state
48
+ };
49
+ notify();
50
+ },
51
+ apply: (delta, resync) => {
52
+ if (held.rev !== null && delta.to <= held.rev) return;
53
+ if (delta.from !== held.rev || !delta.patch) {
54
+ resync();
55
+ return;
56
+ }
57
+ held = {
58
+ rev: delta.to,
59
+ state: applyPatch(held.state, delta.patch)
60
+ };
61
+ notify();
62
+ }
63
+ };
64
+ }
65
+ /** Subscribe to a producer's live state and reduce it into a store. `readSeed` reads the seed
66
+ * (`itx.invoke("itx.facets.get('slug').liveSnapshot()")` for a processor, or a mini-app's
67
+ * own `state()` method). Subscribe happens BEFORE the first seed, so a delta racing the seed just
68
+ * triggers one seed re-read — never a lost update. Gap heals are SINGLE-FLIGHT (a burst of gapped
69
+ * frames triggers one seed read, not one per frame); a failed heal is reported through `onResync`
70
+ * and retried by the next delivered delta (its `from` still mismatches, so it re-triggers). */
71
+ async function connectLiveState(itx, opts) {
72
+ const store = createLiveStateStore();
73
+ let healing = false;
74
+ let healWantedAgain = false;
75
+ let disposed = false;
76
+ const reseed = () => {
77
+ if (disposed) return;
78
+ if (healing) {
79
+ healWantedAgain = true;
80
+ return;
81
+ }
82
+ healing = true;
83
+ const settled = () => {
84
+ healing = false;
85
+ if (disposed || !healWantedAgain) return;
86
+ healWantedAgain = false;
87
+ reseed();
88
+ };
89
+ opts.readSeed().then((s) => {
90
+ if (!disposed) {
91
+ store.seed(s);
92
+ opts.onResync?.("healed");
93
+ }
94
+ settled();
95
+ }, (e) => {
96
+ if (!disposed) opts.onResync?.(e instanceof Error ? e : new Error(String(e)));
97
+ settled();
98
+ });
99
+ };
100
+ const subscription = await itx.subscribe({
101
+ name: opts.name,
102
+ consumes: ["events.iterate.com/itx/live-state-changed"],
103
+ target: (events) => {
104
+ if (disposed) return;
105
+ for (const e of events) {
106
+ let parsed;
107
+ try {
108
+ parsed = LiveStateDeltaMessage.safeParse(JSON.parse(JSON.stringify(e.payload)));
109
+ } catch {
110
+ parsed = void 0;
111
+ }
112
+ if (!parsed?.success) {
113
+ reseed();
114
+ continue;
115
+ }
116
+ if (parsed.data.key !== opts.key) continue;
117
+ try {
118
+ store.apply(parsed.data, reseed);
119
+ } catch {
120
+ reseed();
121
+ }
122
+ }
123
+ }
124
+ });
125
+ try {
126
+ const seed = opts.readSeed();
127
+ const { signal } = opts;
128
+ const aborted = signal && new Promise((_, reject) => {
129
+ const abort = () => reject(signal.reason ?? /* @__PURE__ */ new Error("connectLiveState: aborted while the first seed was pending"));
130
+ if (signal.aborted) abort();
131
+ else signal.addEventListener("abort", abort, { once: true });
132
+ });
133
+ if (aborted) seed.catch(() => void 0);
134
+ store.seed(await (aborted ? Promise.race([seed, aborted]) : seed));
135
+ } catch (error) {
136
+ disposed = true;
137
+ try {
138
+ subscription[Symbol.dispose]();
139
+ } catch {}
140
+ throw error;
141
+ }
142
+ return {
143
+ store,
144
+ async dispose() {
145
+ if (disposed) return;
146
+ disposed = true;
147
+ try {
148
+ subscription[Symbol.dispose]();
149
+ } catch {}
150
+ }
151
+ };
152
+ }
153
+ //#endregion
154
+ export { connectLiveState, createLiveStateStore };
155
+
156
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../src/client/live-state.ts"],"sourcesContent":["// client/live-state.ts — THE CLIENT HALF of live state (`iterate/client`), framework-free, for\n// browsers and node test clients. Two concepts:\n// live state store — `createLiveStateStore`: the pure reduce — seed from the producer, apply each delta, heal on a gap\n// live state client — `connectLiveState`: wire an itx session's `subscribe` + a seed read to the store\n\nimport { z } from \"zod\";\nimport { applyPatch, type PatchOp } from \"../lib.ts\";\n\n// ── live state store ── THE CLIENT HALF of live state, for browsers and node test clients.\n// A deliberately small store for the platform's live-state wire:\n//\n// • SEED from the producer — `{rev, state}` read via an RPC method (a processor's\n// `liveSnapshot()`, a mini-app's `state()`).\n// • APPLY each `{key, from, to, patch}` delta the subscription delivers: a patch lands only when\n// its `from` matches the held rev; a mismatch means a missed delta (or a reborn producer's fresh\n// epoch) — resync by re-reading the seed.\n//\n// The patch format is lib.ts (an RFC-6902 subset), so this store shares ONE applyPatch with\n// the producer — no second diff implementation. No capnweb import: a caller wires the transport and\n// hands deltas in, so the same store backs a node test client and the React hook (client/react.tsx).\n\n/** One live-state delta off the wire — the payload of an `events.iterate.com/itx/live-state-changed`\n * ephemeral event, delivered raw to the subscriber. `patch: null` = the change was too large to\n * send — the rev moved, re-read the seed. */\nexport type LiveStateDelta = { key: string; from: number; to: number; patch: PatchOp[] | null };\n\n/** The delta as it arrives over the wire — PARSED, never cast: `from`/`to` MUST be real numbers (a\n * non-numeric rev would poison the held revision and silently wedge every later frame), and each\n * patch op is a known RFC-6902-subset shape. A frame that fails this heals by re-reading the seed\n * rather than being applied — the same recovery the store already runs on a revision gap. */\nconst LiveStateDeltaMessage: z.ZodType<LiveStateDelta> = z.object({\n key: z.string(),\n from: z.number(),\n to: z.number(),\n patch: z\n .array(\n z.union([\n z.object({ op: z.literal(\"add\"), path: z.string(), value: z.unknown() }),\n z.object({ op: z.literal(\"replace\"), path: z.string(), value: z.unknown() }),\n z.object({ op: z.literal(\"remove\"), path: z.string() }),\n ]),\n )\n .nullable(),\n});\n\n/** What the producer's seed read returns: the current revision paired with the current value. */\nexport type LiveStateSeed<S> = { rev: number; state: S };\n\nexport type LiveStateStore<S> = {\n /** The current value, or undefined until the first seed lands. */\n get(): S | undefined;\n /** The held revision, or null before the first seed. */\n rev(): number | null;\n /** Subscribe to changes (for React's useSyncExternalStore, or a test's await-loop). */\n subscribe(listener: () => void): () => void;\n /** Seed (or re-seed) from a seed read — the first paint, and the heal after a gap. */\n seed(seed: LiveStateSeed<S>): void;\n /** Reduce one delta in; on a revision gap call `resync` and hold the value until a fresh seed. */\n apply(delta: LiveStateDelta, resync: () => void): void;\n};\n\nexport function createLiveStateStore<S>(): LiveStateStore<S> {\n // `rev: null` until the first seed lands.\n let held: { rev: number | null; state: S | undefined } = { rev: null, state: undefined };\n const listeners = new Set<() => void>();\n const notify = () => listeners.forEach((l) => l());\n return {\n get: () => held.state,\n rev: () => held.rev,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => void listeners.delete(listener);\n },\n seed: (seed) => {\n // MONOTONIC: a late-resolving OLDER seed read must never move the store backwards past state\n // deltas have already advanced (a delta-triggered resync can race the initial seed). Revisions\n // are time-seeded epochs plus increments, so \"newer\" is numeric.\n if (held.rev !== null && seed.rev < held.rev) return;\n held = { rev: seed.rev, state: seed.state };\n notify();\n },\n apply: (delta, resync) => {\n // A delta at-or-behind the held rev is a duplicate/out-of-order frame — drop it silently.\n // (Epochs are minted from the clock, so a reborn producer's fresh chain sits numerically above\n // every rev an old chain handed out; a frame wholly behind us is genuinely old. The one\n // exception is a clock that regressed across a producer rebirth — accepted: the next applied\n // or gapped frame resyncs from a fresh seed anyway.)\n if (held.rev !== null && delta.to <= held.rev) return;\n // A gap (its `from` is not the held rev — including \"no seed yet\") means a missed delta or a\n // reborn epoch, and a `null` patch means the change was too large to send — either way re-read\n // the seed instead of applying onto a diverged base.\n if (delta.from !== held.rev || !delta.patch) {\n resync();\n return;\n }\n held = { rev: delta.to, state: applyPatch(held.state as S, delta.patch) };\n notify();\n },\n };\n}\n\n// ── live state client ── wire an itx session's `subscribe` + a seed read to a LiveStateStore.\n// This is the whole cleanroom client: a subscription that consumes the one live-state event type\n// (`itx.subscribe({ target, consumes: [\"events.iterate.com/itx/live-state-changed\"] })`) delivers every\n// key's deltas in batches; this filters the watched `key` and reduces each delta into the store; a\n// `readSeed` thunk reads `{rev, state}` for the first paint and every gap heal. Transport lives here so\n// the store above and the React hook stay pure.\n\n/** The slice of an itx session this needs — a capnweb `IterateContextRpcTarget` proxy satisfies it structurally:\n * `subscribe` hands back a DISPOSABLE handle (disposing it removes the subscription server-side). */\nexport type LiveStateItx = {\n subscribe(input: {\n name?: string;\n consumes?: string[];\n target: (events: unknown[], range: unknown) => void;\n }): Promise<{ [Symbol.dispose](): void }>;\n};\n\n/** A connected live-state subscription: the store rendering it, and the dispose that removes the\n * server-side subscription (the handle's disposer; the session's end does the same). */\nexport type LiveStateConnection<S> = {\n store: LiveStateStore<S>;\n /** Unsubscribe on the server and stop reducing deltas. Safe to call more than once. */\n dispose(): Promise<void>;\n};\n\n/** Subscribe to a producer's live state and reduce it into a store. `readSeed` reads the seed\n * (`itx.invoke(\"itx.facets.get('slug').liveSnapshot()\")` for a processor, or a mini-app's\n * own `state()` method). Subscribe happens BEFORE the first seed, so a delta racing the seed just\n * triggers one seed re-read — never a lost update. Gap heals are SINGLE-FLIGHT (a burst of gapped\n * frames triggers one seed read, not one per frame); a failed heal is reported through `onResync`\n * and retried by the next delivered delta (its `from` still mismatches, so it re-triggers). */\nexport async function connectLiveState<S>(\n itx: LiveStateItx,\n opts: {\n key: string;\n name?: string;\n readSeed: () => Promise<LiveStateSeed<S>>;\n /** Called after each gap heal attempt: \"healed\" on a fresh seed, the error when the seed read\n * failed (the store keeps its last value; the next delta retries). */\n onResync?: (result: \"healed\" | Error) => void;\n /** Abort while the FIRST seed is still pending (a component unmounting): the row just configured\n * is recalled and the connect rejects — a seed read that never answers leaves nothing lent. */\n signal?: AbortSignal;\n },\n): Promise<LiveStateConnection<S>> {\n const store = createLiveStateStore<S>();\n let healing = false;\n let healWantedAgain = false; // a gap seen WHILE a heal was in flight: the seed may predate it\n let disposed = false;\n const reseed = () => {\n if (disposed) return;\n if (healing) {\n healWantedAgain = true;\n return;\n }\n healing = true;\n const settled = () => {\n healing = false;\n if (disposed || !healWantedAgain) return;\n healWantedAgain = false;\n reseed();\n };\n void opts.readSeed().then(\n (s) => {\n if (!disposed) {\n store.seed(s);\n opts.onResync?.(\"healed\");\n }\n settled();\n },\n (e: unknown) => {\n if (!disposed) opts.onResync?.(e instanceof Error ? e : new Error(String(e)));\n settled();\n },\n );\n };\n const subscription = await itx.subscribe({\n name: opts.name,\n consumes: [\"events.iterate.com/itx/live-state-changed\"],\n // A batch of live-state deltas (every key's); keep the watched key's.\n target: (events: unknown[]) => {\n if (disposed) return;\n for (const e of events) {\n // capnweb hands each event as a live proxy value — deep-copy to a plain object, then PARSE\n // the frame (never cast network data). The whole decode is guarded: an ABSENT payload makes\n // `JSON.parse(JSON.stringify(undefined))` throw before validation, and any throw here would\n // skip every later delta in the batch. A malformed OR undecodable frame heals from a fresh seed\n // instead of poisoning the held rev or escaping this callback.\n let parsed: ReturnType<(typeof LiveStateDeltaMessage)[\"safeParse\"]> | undefined;\n try {\n parsed = LiveStateDeltaMessage.safeParse(\n JSON.parse(JSON.stringify((e as { payload: unknown }).payload)),\n );\n } catch {\n parsed = undefined;\n }\n if (!parsed?.success) {\n reseed();\n continue;\n }\n if (parsed.data.key !== opts.key) continue;\n // `store.apply` runs `applyPatch`, which THROWS on a patch it refuses (a `/__proto__` path a\n // legitimate state with an own `__proto__` key produces, say). Contain it per frame: heal from\n // a fresh seed — which re-seeds the state DIRECTLY, no patch to reject — instead of escaping this\n // callback and skipping every later frame.\n try {\n store.apply(parsed.data, reseed);\n } catch {\n reseed();\n }\n }\n },\n });\n try {\n const seed = opts.readSeed();\n const { signal } = opts;\n const aborted =\n signal &&\n new Promise<never>((_, reject) => {\n const abort = () =>\n reject(\n signal.reason ??\n new Error(\"connectLiveState: aborted while the first seed was pending\"),\n );\n if (signal.aborted) abort();\n else signal.addEventListener(\"abort\", abort, { once: true });\n });\n if (aborted) seed.catch(() => undefined); // the seed read may still settle after the abort — quietly\n store.seed(await (aborted ? Promise.race([seed, aborted]) : seed));\n } catch (error) {\n // The seed failed after the row was configured: recall it, or the server keeps delivering to a\n // callback no one holds (and the session's other rows wait behind it).\n disposed = true;\n try {\n subscription[Symbol.dispose]();\n } catch {\n // a dead session has already removed it\n }\n throw error;\n }\n return {\n store,\n async dispose() {\n if (disposed) return;\n disposed = true;\n try {\n subscription[Symbol.dispose](); // the server removes the row and recalls the lent callback\n } catch {\n // a dead session has already removed it — the socket close disposed every handle\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -4,13 +4,13 @@ import { RpcTarget } from "capnweb";
4
4
  * is `["itx","builtins","rpcStubs",["get","cam"],["",1,2]]` — what a `provide(stub)` rule spells when
5
5
  * the lent stub is called with args. */
6
6
  export type ItxExpressionStep = string | [method: string, ...args: unknown[]];
7
- /** An itx expression as data: the scope root (`itx`) then get/call steps. THE parsed form every door
8
- * works on. */
7
+ /** An itx expression as data: the scope root (`itx`) then get/call steps. THE parsed form every
8
+ * dispatching method works on. */
9
9
  export type ItxExpression = ItxExpressionStep[];
10
10
  /** THE dispatch target, in EITHER codec half — a dotted string that starts with the scope root
11
11
  * (`"itx.facets.get('core')"`) OR the parsed structured form (`["itx","facets",["get","core"]]`).
12
12
  * Both carry call args (the string via `.method(args)`), and `normalizedItxExpression` normalizes
13
- * either to the structured form — so either works wherever one works, at every door that dispatches. */
13
+ * either to the structured form — so either works wherever one works, in every method that dispatches. */
14
14
  export type ItxExpressionInput = string | ItxExpression;
15
15
  /** An itx-expression PREFIX — a rewrite rule's `match`: dotted names, any of which may be a call step
16
16
  * PINNING literal args — `itx.ai.run` or `itx.ai.run('gpt-5')` or `itx.repo.get('main').files`. A
@@ -20,7 +20,7 @@ export type ItxExpressionInput = string | ItxExpression;
20
20
  export type ItxExpressionPrefix = ItxExpression;
21
21
  /** The name a step carries: the property itself, or a call step's method. */
22
22
  export declare const itxExpressionStepName: (step: ItxExpressionStep | undefined) => string | undefined;
23
- /** The merge entry's key — `...@` — read by rule 7. */
23
+ /** The merge entry's key — `...@` — read by apps/os `fillItxExpressionHoles`. */
24
24
  export declare const ITX_EXPRESSION_MERGE_KEY = "...@";
25
25
  /** Is `value` the marker literal `{ "@": true }`? */
26
26
  export declare const isItxExpressionHole: (value: unknown) => boolean;
@@ -32,8 +32,8 @@ export declare function containsItxExpressionHole(value: unknown): boolean;
32
32
  export declare function parse(source: string, options?: {
33
33
  holes?: boolean;
34
34
  }): ItxExpression;
35
- /** THE ONE NORMALIZING DOOR: either half, normalized to the array half and checked — a string is
36
- * parsed (short by rule), an array is shape-checked in place. Every door that takes an
35
+ /** THE ONE NORMALIZER: either half, normalized to the array half and checked — a string is
36
+ * parsed (short by rule), an array is shape-checked in place. Every function that takes an
37
37
  * `ItxExpressionInput` (the edge `invoke`, the resolver, the event builders, the prefix parser
38
38
  * below) enters through it. */
39
39
  export declare function normalizedItxExpression(input: ItxExpressionInput, options?: {
@@ -60,33 +60,6 @@ export declare function parseItxExpressionPrefix(source: ItxExpressionInput): It
60
60
  * stub is keyed by through `provide`'s sugar: parsed, then printed (dotted names; pinned args as JSON5
61
61
  * literals). */
62
62
  export declare function canonicalItxExpressionPrefix(source: ItxExpressionInput): string;
63
- /** Register a pipelinable promise brand (the workerd entrypoint's two calls at boot). */
64
- export declare function registerPipelinedRpcBrand(brand: abstract new (...args: never[]) => unknown): void;
65
- /**
66
- * THE step walk: property steps `Reflect.get` with the receiver carried; call steps `Reflect.apply`
67
- * ON that receiver (detaching a method from a Workers-RPC receiver breaks it); an ordinary promise
68
- * is awaited between steps, a branded one (PIPELINED_RPC_BRANDS, above) is not.
69
- *
70
- * ⚠️ DataCloneError LEARNING:
71
- * invoke facet/RPC-stub methods with `Reflect.apply(fn, receiver, args)`, NEVER `stub[m].apply(stub,
72
- * args)`. Reading `.apply` off an RPC stub's method proxy is a capnweb PIPELINED REMOTE PATH;
73
- * calling it passes the stub as an argument, so workerd serializes it — and a Worker-Loader facet
74
- * stub may never be serialized (`requireAllowsTransfer()` throws unconditionally) → `DataCloneError:
75
- * Durable Object Facet stubs cannot be transferred between Workers`. Do not "simplify" this away.
76
- */
77
- export declare function walkSteps(start: {
78
- value: unknown;
79
- receiver: unknown;
80
- }, steps: ItxExpression): Promise<{
81
- value: unknown;
82
- receiver: unknown;
83
- }>;
84
- /** Apply `args` to a resolved value on its carried receiver, or a LOUD error if it is not callable
85
- * (never the silent arg-drop apps/os shipped). An `InvokeHandle` is NOT a JS function (a real
86
- * RpcTarget so dotted access pipelines — the invoke handle section), so ROOT-calling it dispatches
87
- * those args at its EMPTY path: `handle(events,range)` ⇒ the bare callback the handle fronts. The one
88
- * bridge between "callable capability" and "pipelinable RpcTarget". */
89
- export declare function callOn(value: unknown, receiver: unknown, args: unknown[]): Promise<unknown>;
90
63
  /** Install the hop drawn in the header on a class's PROTOTYPE CHAIN, with the scope `root` (`["itx"]`
91
64
  * for the edge context, `[]` for a handle). Call ONCE per class. Constructor inheritance is
92
65
  * untouched — only `Class.prototype`'s parent link changes, and the hop forwards everything it does
@@ -100,47 +73,16 @@ export declare function installPrototypeInvokeFallback<T extends abstract new (.
100
73
  export declare class InvokeHandle extends RpcTarget {
101
74
  #private;
102
75
  constructor(dispatchItxExpressionSteps: (itxExpressionSteps: ItxExpression) => unknown);
103
- /** THE reduce door the prototype hop dispatches onto; the expression is RELATIVE to this handle. */
76
+ /** THE dispatch method the prototype hop reduces onto; the expression is RELATIVE to this handle. */
104
77
  invoke(itxExpressionSteps: ItxExpression): unknown;
105
- /** Call the bare capability this handle fronts — the ANONYMOUS call step (`callOn` in the dispatch section
106
- * uses it when a rewritten call's target IS a handle: `handle(events, range)`). */
78
+ /** Call the bare capability this handle fronts — the ANONYMOUS call step (how a rewritten call
79
+ * whose target IS a handle calls it: `handle(events, range)`). */
107
80
  applyRoot(args: unknown[]): unknown;
108
81
  }
109
82
  /** Walk itx-expression steps off a capnweb stub; the ANONYMOUS call step (`""`) calls the value
110
83
  * itself (a bare function lent as a capability). NO await inside the loop: on a capnweb stub every
111
84
  * step is a PIPELINED path, so an n-step chain costs ONE round trip, flushed by the caller's single
112
85
  * await. A DIRECT call on the stub, never `.apply`: reading `.apply` off a capnweb stub's method is
113
- * itself a pipelined remote path (`walkSteps`'s DataCloneError learning). Exported for the library
114
- * tier, which may import this module only. */
86
+ * itself a pipelined remote path, and calling it sends the stub as an argument, which a facet stub
87
+ * refuses with a DataCloneError. What a connector over a lent stub or a remote capnweb API walks. */
115
88
  export declare function walkStepsOnRpcStub(stub: unknown, steps: ItxExpression): unknown;
116
- /** `itx.facets.get(name)` / `itx.facets.get(name, { source, className })` — a facet of this context. */
117
- export declare class FacetHandle extends InvokeHandle {
118
- }
119
- /** A HANDLE ON THE WIRE IS THE EXPRESSION THAT NAMES IT. An `InvokeHandle` a context mints (`repos.get(path)`,
120
- * `workspaces.get(path)`, `facets.get(name)`, `cd(path)`, `workers.get(spec)`) holds no state — it is a
121
- * dispatch closure over a path — yet as a Workers-RPC result it would cross a hop as a LIVE stub whose
122
- * session keeps the context's actor resident for as long as the holder keeps it (prd 2026-09-22: ~125
123
- * such sessions parked around the clock). So the context's RPC door answers with THIS instead: the
124
- * caller's own expression, which from the caller's root denotes the same handle; the caller mints its
125
- * own handle over it (`materializeItxHandleReference`), and every later verb is one whole call the
126
- * context resolves from scratch. Nothing outlives a call. A lent client stub (`RpcStubHandle`) is the
127
- * one handle that IS live and crosses as itself. */
128
- export declare const ITX_HANDLE_REFERENCE_KEY = "$itxHandleExpression";
129
- export type ItxHandleReference = {
130
- [ITX_HANDLE_REFERENCE_KEY]: ItxExpression;
131
- };
132
- export declare const isItxHandleReference: (value: unknown) => value is ItxHandleReference;
133
- /** What a context's RPC door hands back for `expression`'s result: a reference when the result is a
134
- * path-shaped handle (an `InvokeHandle` that is not a lent stub) or a reference from a hop below —
135
- * re-rooted, since `expression` is how THIS caller reached it — else nothing (the result crosses as it
136
- * is). Runtime args that fold into a terminal NAME fold here exactly as the resolver folds them, so the
137
- * reference is the call the resolver ran; args left over apply to the value and never name a handle. */
138
- export declare function itxHandleReferenceOf(result: unknown, expression: ItxExpression, args?: unknown[]): ItxHandleReference | undefined;
139
- /** The holder's side: a reference becomes a handle of the HOLDER's own whose every dotted call is one
140
- * whole expression through `invoke` — the reference's expression plus the steps. Anything else passes
141
- * through untouched. The proxy hands relative steps; a caller's own `.invoke("itx.whoami()")` is a
142
- * whole call, spelled from the root. */
143
- export declare function materializeItxHandleReference(result: unknown, invoke: (expression: ItxExpression) => unknown): unknown;
144
- /** `itx.rpcStubs.get(key)` — a live stub lent to the registry. */
145
- export declare class RpcStubHandle extends InvokeHandle {
146
- }
@@ -1,11 +1,11 @@
1
1
  import { codedError, jsonEqual } from "./lib.mjs";
2
2
  import { RpcTarget } from "capnweb";
3
3
  import JSON5 from "json5";
4
- //#region src/next/expression.ts
4
+ //#region src/expression.ts
5
5
  /** A STRING expression is for what a person types: short. Anything bigger — a worker's source, a large
6
6
  * literal — rides the PARSED form (`["itx","workers",["get",{ source }]]`), which is plain data and never
7
7
  * meets json5. The cap is O(1), before any parsing (stock json5 allocates per character and a
8
- * multi-megabyte literal kills a 128 MiB isolate — the 2026-09-07 wave-0 plan, issue 2). */
8
+ * multi-megabyte literal kills a 128 MiB isolate). */
9
9
  const ITX_EXPRESSION_STRING_MAX_CHARS = 2048;
10
10
  /** The name a step carries: the property itself, or a call step's method. */
11
11
  const itxExpressionStepName = (step) => Array.isArray(step) ? step[0] : step;
@@ -17,7 +17,7 @@ const RESERVED = new Set([
17
17
  ]);
18
18
  /** The marker's array-half spelling, the one reserved literal. */
19
19
  const ITX_EXPRESSION_HOLE = { "@": true };
20
- /** The merge entry's key — `...@` — read by rule 7. */
20
+ /** The merge entry's key — `...@` — read by apps/os `fillItxExpressionHoles`. */
21
21
  const ITX_EXPRESSION_MERGE_KEY = "...@";
22
22
  /** A single- or double-quoted string literal (escapes honored) or a JSON5 comment (block or line):
23
23
  * THE one pattern every walk that must skip what is inside them is built from — the marker lex, the
@@ -131,8 +131,8 @@ function assertItxExpressionShape(expression) {
131
131
  if (i === 0) fail("a call on the root itself");
132
132
  });
133
133
  }
134
- /** THE ONE NORMALIZING DOOR: either half, normalized to the array half and checked — a string is
135
- * parsed (short by rule), an array is shape-checked in place. Every door that takes an
134
+ /** THE ONE NORMALIZER: either half, normalized to the array half and checked — a string is
135
+ * parsed (short by rule), an array is shape-checked in place. Every function that takes an
136
136
  * `ItxExpressionInput` (the edge `invoke`, the resolver, the event builders, the prefix parser
137
137
  * below) enters through it. */
138
138
  function normalizedItxExpression(input, options) {
@@ -178,68 +178,6 @@ function parseItxExpressionPrefix(source) {
178
178
  function canonicalItxExpressionPrefix(source) {
179
179
  return print(parseItxExpressionPrefix(source));
180
180
  }
181
- const PIPELINED_RPC_BRANDS = [];
182
- /** Register a pipelinable promise brand (the workerd entrypoint's two calls at boot). */
183
- function registerPipelinedRpcBrand(brand) {
184
- PIPELINED_RPC_BRANDS.push(brand);
185
- }
186
- const pipelined = (v) => PIPELINED_RPC_BRANDS.some((b) => v instanceof b);
187
- /** Resolve one step's property. `__proto__` / `constructor` / `prototype` never resolve — `constructor`
188
- * would hand out the class itself (trusted clients or not, that is not a step anyone means). */
189
- function stepGet(value, key) {
190
- if (key === "__proto__" || key === "constructor" || key === "prototype") return void 0;
191
- return Reflect.get(value, key);
192
- }
193
- /**
194
- * THE step walk: property steps `Reflect.get` with the receiver carried; call steps `Reflect.apply`
195
- * ON that receiver (detaching a method from a Workers-RPC receiver breaks it); an ordinary promise
196
- * is awaited between steps, a branded one (PIPELINED_RPC_BRANDS, above) is not.
197
- *
198
- * ⚠️ DataCloneError LEARNING:
199
- * invoke facet/RPC-stub methods with `Reflect.apply(fn, receiver, args)`, NEVER `stub[m].apply(stub,
200
- * args)`. Reading `.apply` off an RPC stub's method proxy is a capnweb PIPELINED REMOTE PATH;
201
- * calling it passes the stub as an argument, so workerd serializes it — and a Worker-Loader facet
202
- * stub may never be serialized (`requireAllowsTransfer()` throws unconditionally) → `DataCloneError:
203
- * Durable Object Facet stubs cannot be transferred between Workers`. Do not "simplify" this away.
204
- */
205
- async function walkSteps(start, steps) {
206
- let { value, receiver } = start;
207
- for (const [stepIndex, step] of steps.entries()) {
208
- if (!pipelined(value)) value = await value;
209
- if (value == null) throw new Error(`hit ${String(value)} at step ${stepIndex + 1} of ${print(steps)} (${JSON.stringify(step)})`);
210
- if (typeof step === "string") {
211
- receiver = value;
212
- value = stepGet(value, step);
213
- } else {
214
- const [method, ...args] = step;
215
- if (method === "") {
216
- value = callOn(value, receiver, args);
217
- receiver = void 0;
218
- if (!pipelined(value)) value = await value;
219
- continue;
220
- }
221
- const fn = stepGet(value, method);
222
- if (typeof fn !== "function") throw codedError("NOT_A_METHOD", `${JSON.stringify(method)} is not a method at step ${stepIndex + 1} of ${print(steps)}`);
223
- receiver = void 0;
224
- value = Reflect.apply(fn, value, args);
225
- if (!pipelined(value)) value = await value;
226
- }
227
- }
228
- return {
229
- value: pipelined(value) ? value : await value,
230
- receiver
231
- };
232
- }
233
- /** Apply `args` to a resolved value on its carried receiver, or a LOUD error if it is not callable
234
- * (never the silent arg-drop apps/os shipped). An `InvokeHandle` is NOT a JS function (a real
235
- * RpcTarget so dotted access pipelines — the invoke handle section), so ROOT-calling it dispatches
236
- * those args at its EMPTY path: `handle(events,range)` ⇒ the bare callback the handle fronts. The one
237
- * bridge between "callable capability" and "pipelinable RpcTarget". */
238
- async function callOn(value, receiver, args) {
239
- if (typeof value === "function") return Reflect.apply(value, receiver, args);
240
- if (value instanceof InvokeHandle) return value.applyRoot(args);
241
- throw codedError("NOT_A_METHOD", `target is not callable but ${args.length} arg(s) were passed`);
242
- }
243
181
  /** Names that must NEVER become dynamic capability segments — a dispatcher answering them would turn
244
182
  * a plain property probe into a live capability call. Enforced at the prototype-chain hop and at
245
183
  * every depth of the path proxies it hands out. Two kinds, one set: */
@@ -331,12 +269,12 @@ var InvokeHandle = class extends RpcTarget {
331
269
  super();
332
270
  this.#dispatchItxExpressionSteps = dispatchItxExpressionSteps;
333
271
  }
334
- /** THE reduce door the prototype hop dispatches onto; the expression is RELATIVE to this handle. */
272
+ /** THE dispatch method the prototype hop reduces onto; the expression is RELATIVE to this handle. */
335
273
  invoke(itxExpressionSteps) {
336
274
  return this.#dispatchItxExpressionSteps(itxExpressionSteps);
337
275
  }
338
- /** Call the bare capability this handle fronts — the ANONYMOUS call step (`callOn` in the dispatch section
339
- * uses it when a rewritten call's target IS a handle: `handle(events, range)`). */
276
+ /** Call the bare capability this handle fronts — the ANONYMOUS call step (how a rewritten call
277
+ * whose target IS a handle calls it: `handle(events, range)`). */
340
278
  applyRoot(args) {
341
279
  return this.#dispatchItxExpressionSteps([["", ...args]]);
342
280
  }
@@ -346,8 +284,8 @@ installPrototypeInvokeFallback(InvokeHandle, []);
346
284
  * itself (a bare function lent as a capability). NO await inside the loop: on a capnweb stub every
347
285
  * step is a PIPELINED path, so an n-step chain costs ONE round trip, flushed by the caller's single
348
286
  * await. A DIRECT call on the stub, never `.apply`: reading `.apply` off a capnweb stub's method is
349
- * itself a pipelined remote path (`walkSteps`'s DataCloneError learning). Exported for the library
350
- * tier, which may import this module only. */
287
+ * itself a pipelined remote path, and calling it sends the stub as an argument, which a facet stub
288
+ * refuses with a DataCloneError. What a connector over a lent stub or a remote capnweb API walks. */
351
289
  function walkStepsOnRpcStub(stub, steps) {
352
290
  let value = stub;
353
291
  for (const step of steps) if (typeof step === "string") value = value[step];
@@ -357,43 +295,7 @@ function walkStepsOnRpcStub(stub, steps) {
357
295
  }
358
296
  return value;
359
297
  }
360
- /** `itx.facets.get(name)` / `itx.facets.get(name, { source, className })` — a facet of this context. */
361
- var FacetHandle = class extends InvokeHandle {};
362
- /** A HANDLE ON THE WIRE IS THE EXPRESSION THAT NAMES IT. An `InvokeHandle` a context mints (`repos.get(path)`,
363
- * `workspaces.get(path)`, `facets.get(name)`, `cd(path)`, `workers.get(spec)`) holds no state — it is a
364
- * dispatch closure over a path — yet as a Workers-RPC result it would cross a hop as a LIVE stub whose
365
- * session keeps the context's actor resident for as long as the holder keeps it (prd 2026-09-22: ~125
366
- * such sessions parked around the clock). So the context's RPC door answers with THIS instead: the
367
- * caller's own expression, which from the caller's root denotes the same handle; the caller mints its
368
- * own handle over it (`materializeItxHandleReference`), and every later verb is one whole call the
369
- * context resolves from scratch. Nothing outlives a call. A lent client stub (`RpcStubHandle`) is the
370
- * one handle that IS live and crosses as itself. */
371
- const ITX_HANDLE_REFERENCE_KEY = "$itxHandleExpression";
372
- const isItxHandleReference = (value) => Boolean(value) && typeof value === "object" && Array.isArray(value["$itxHandleExpression"]);
373
- /** What a context's RPC door hands back for `expression`'s result: a reference when the result is a
374
- * path-shaped handle (an `InvokeHandle` that is not a lent stub) or a reference from a hop below —
375
- * re-rooted, since `expression` is how THIS caller reached it — else nothing (the result crosses as it
376
- * is). Runtime args that fold into a terminal NAME fold here exactly as the resolver folds them, so the
377
- * reference is the call the resolver ran; args left over apply to the value and never name a handle. */
378
- function itxHandleReferenceOf(result, expression, args = []) {
379
- const last = expression.at(-1);
380
- const folded = args.length > 0 && typeof last === "string" && expression.length > 1 ? [...expression.slice(0, -1), [last, ...args]] : args.length > 0 ? void 0 : expression;
381
- if (!folded) return void 0;
382
- if (!(result instanceof InvokeHandle && !(result instanceof RpcStubHandle)) && !isItxHandleReference(result)) return void 0;
383
- return { [ITX_HANDLE_REFERENCE_KEY]: folded };
384
- }
385
- /** The holder's side: a reference becomes a handle of the HOLDER's own whose every dotted call is one
386
- * whole expression through `invoke` — the reference's expression plus the steps. Anything else passes
387
- * through untouched. The proxy hands relative steps; a caller's own `.invoke("itx.whoami()")` is a
388
- * whole call, spelled from the root. */
389
- function materializeItxHandleReference(result, invoke) {
390
- if (!isItxHandleReference(result)) return result;
391
- const expression = result[ITX_HANDLE_REFERENCE_KEY];
392
- return new InvokeHandle((steps) => invoke(typeof steps === "string" || steps[0] === "itx" ? normalizedItxExpression(steps) : [...expression, ...steps]));
393
- }
394
- /** `itx.rpcStubs.get(key)` — a live stub lent to the registry. */
395
- var RpcStubHandle = class extends InvokeHandle {};
396
298
  //#endregion
397
- export { FacetHandle, ITX_EXPRESSION_MERGE_KEY, ITX_HANDLE_REFERENCE_KEY, InvokeHandle, RpcStubHandle, callOn, canonicalItxExpressionPrefix, containsItxExpressionHole, installPrototypeInvokeFallback, isItxExpressionHole, isItxHandleReference, itxExpressionStepName, itxHandleReferenceOf, keySortedForPrint, materializeItxHandleReference, normalizedItxExpression, parse, parseItxExpressionPrefix, print, registerPipelinedRpcBrand, walkSteps, walkStepsOnRpcStub };
299
+ export { ITX_EXPRESSION_MERGE_KEY, InvokeHandle, canonicalItxExpressionPrefix, containsItxExpressionHole, installPrototypeInvokeFallback, isItxExpressionHole, itxExpressionStepName, keySortedForPrint, normalizedItxExpression, parse, parseItxExpressionPrefix, print, walkStepsOnRpcStub };
398
300
 
399
301
  //# sourceMappingURL=expression.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expression.mjs","names":["#dispatchItxExpressionSteps"],"sources":["../src/expression.ts"],"sourcesContent":["// expression.ts — THE expression codec: the STRING half (itx.facets.get(\"core\")) ⇄ the\n// STRUCTURED half ([\"itx\", \"facets\", [\"get\", \"core\"]]). Args are ONE JSON5 grammar, comments included\n// (no hand-rolled number/object parser; __proto__-safe); expressions are persisted NAMES, so deleting\n// one IS revocation. One more concept rides with the codec, because user code speaks it too:\n// invoke handle — `InvokeHandle` + the prototype hop: the DOTTED SURFACE, every unknown chain one\n// `invoke(expression)` — what `itx.facets.get(name)` and every other mid-chain\n// capability of api.ts is, and what a connector builds over a remote API\n// What the PLATFORM does with an expression is apps/os: the rewrite rules (match, rank, rewrite) are\n// src/context/itx-expression-rewriting.ts, and executing a rewritten call against a live object graph\n// (`walkSteps`, the answer a context hands back) is src/context/dispatch.ts.\nimport JSON5 from \"json5\";\nimport { RpcTarget } from \"capnweb\";\nimport { codedError, jsonEqual } from \"./lib.ts\";\n\n/** A STRING expression is for what a person types: short. Anything bigger — a worker's source, a large\n * literal — rides the PARSED form (`[\"itx\",\"workers\",[\"get\",{ source }]]`), which is plain data and never\n * meets json5. The cap is O(1), before any parsing (stock json5 allocates per character and a\n * multi-megabyte literal kills a 128 MiB isolate). */\nconst ITX_EXPRESSION_STRING_MAX_CHARS = 2048;\n\n/** One step: a property read (string) or a call (`[method, ...args]`). Args are plain JSON. The\n * method `\"\"` is the ANONYMOUS call — call the value itself: `itx.builtins.rpcStubs.get('cam')(1, 2)`\n * is `[\"itx\",\"builtins\",\"rpcStubs\",[\"get\",\"cam\"],[\"\",1,2]]` — what a `provide(stub)` rule spells when\n * the lent stub is called with args. */\nexport type ItxExpressionStep = string | [method: string, ...args: unknown[]];\n/** An itx expression as data: the scope root (`itx`) then get/call steps. THE parsed form every\n * dispatching method works on. */\nexport type ItxExpression = ItxExpressionStep[];\n/** THE dispatch target, in EITHER codec half — a dotted string that starts with the scope root\n * (`\"itx.facets.get('core')\"`) OR the parsed structured form (`[\"itx\",\"facets\",[\"get\",\"core\"]]`).\n * Both carry call args (the string via `.method(args)`), and `normalizedItxExpression` normalizes\n * either to the structured form — so either works wherever one works, in every method that dispatches. */\nexport type ItxExpressionInput = string | ItxExpression;\n/** An itx-expression PREFIX — a rewrite rule's `match`: dotted names, any of which may be a call step\n * PINNING literal args — `itx.ai.run` or `itx.ai.run('gpt-5')` or `itx.repo.get('main').files`. A\n * pinned arg must equal the call's arg at that position for the rule to match, and is CONSUMED by\n * the match (partial application): `itx.ai.run('gpt-5') ⇒ itx.openai.chat` makes\n * `itx.ai.run('gpt-5', inputs)` into `itx.openai.chat(inputs)`. */\nexport type ItxExpressionPrefix = ItxExpression;\n/** The name a step carries: the property itself, or a call step's method. */\nexport const itxExpressionStepName = (step: ItxExpressionStep | undefined): string | undefined =>\n Array.isArray(step) ? step[0] : step;\n\nconst IDENT = /^[A-Za-z_$][A-Za-z0-9_$-]*/;\nconst RESERVED = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n// ── `@`, THE CALLER'S INPUT — a rewrite rule's target may hold it, nothing else may ──\n// In the string half a bare `@` outside a string literal is the marker (`'@cf/…'` inside quotes is a\n// string like any other); `...@` as an object-literal entry is the merge form. In the array half the\n// marker is ONE reserved literal, `{ \"@\": true }`, and the merge entry the key `\"...@\"` with the value\n// `true` — so the stored form is plain JSON, and those two spellings are unspellable as literals in a\n// target (the codec's one reservation). What `@` MEANS is `fillItxExpressionHoles` in\n// apps/os/src/context/itx-expression-rewriting.ts; here it is only lexed (parse, targets only) and\n// printed back (print, targets only).\n/** The marker's array-half spelling, the one reserved literal. */\nconst ITX_EXPRESSION_HOLE = { \"@\": true } as const;\n/** The merge entry's key — `...@` — read by apps/os `fillItxExpressionHoles`. */\nexport const ITX_EXPRESSION_MERGE_KEY = \"...@\";\n\n/** A single- or double-quoted string literal (escapes honored) or a JSON5 comment (block or line):\n * THE one pattern every walk that must skip what is inside them is built from — the marker lex, the\n * marker print, the paren matcher. In an alternation a span is consumed whole, so nothing inside one\n * (a quote in a comment, an `@` in a string) is ever seen by the other alternatives. */\nconst STRING_OR_COMMENT = String.raw`\"(?:[^\"\\\\]|\\\\[\\s\\S])*\"|'(?:[^'\\\\]|\\\\[\\s\\S])*'|/\\*[\\s\\S]*?\\*/|//[^\\n]*`;\nconst isStringOrComment = (match: string): boolean =>\n match[0] === '\"' || match[0] === \"'\" || match[0] === \"/\";\n/** In call args: a literal (kept verbatim) or a marker — `...@` before `@`, so the merge form wins. */\nconst MARKERS_IN_ARGS = new RegExp(`${STRING_OR_COMMENT}|\\\\.\\\\.\\\\.@|@`, \"g\");\n/** In JSON5's printed output: the marker literal `{'@':true}` and the merge entry `'...@':true` are\n * spelled with a single-quoted key and matched on those exact boundaries — listed BEFORE the literal\n * alternative so the entry's `'...@'` is read as the entry, not as a string. A user's string that\n * merely contains those characters is emitted by JSON5 as a longer (double-quoted) literal and is\n * consumed whole. */\nconst MARKERS_IN_PRINT = new RegExp(`\\\\{'@':true\\\\}|'\\\\.\\\\.\\\\.@':true|${STRING_OR_COMMENT}`, \"g\");\n/** A bracket outside a literal. */\nconst BRACKETS = new RegExp(`${STRING_OR_COMMENT}|[()[\\\\]{}]`, \"g\");\n\n/** Is `value` the marker literal `{ \"@\": true }`? */\nexport const isItxExpressionHole = (value: unknown): boolean =>\n jsonEqual(value, ITX_EXPRESSION_HOLE);\n\n/** Does `value` (a step, an arg tree, a whole expression) hold the marker or a merge entry anywhere? */\nexport function containsItxExpressionHole(value: unknown): boolean {\n if (isItxExpressionHole(value)) return true;\n if (Array.isArray(value)) return value.some(containsItxExpressionHole);\n // oxlint-disable-next-line iterate/simple-truthiness-check -- `value` is `unknown`; the typeof separates real objects from primitives (a bare truthiness check would recurse into strings/numbers)\n if (value !== null && typeof value === \"object\")\n return (\n (value as Record<string, unknown>)[ITX_EXPRESSION_MERGE_KEY] === true ||\n Object.values(value).some(containsItxExpressionHole)\n );\n return false;\n}\n\n/** Index of the `)` closing the `(` at `open`; tracks bracket depth, skipping quoted string args. */\nfunction matchingParen(source: string, open: number): number {\n let depth = 0;\n BRACKETS.lastIndex = open;\n for (let bracket = BRACKETS.exec(source); bracket; bracket = BRACKETS.exec(source)) {\n if (isStringOrComment(bracket[0])) continue;\n if (\"([{\".includes(bracket[0])) depth++;\n else if (--depth === 0) return bracket.index;\n }\n throw new Error(`expression: unbalanced \"(\" in ${JSON.stringify(source)}`);\n}\n\n/** Parse the STRING half: dotted names + `.method(args)` calls (args JSON5-parsed); rejects reserved\n * names + bare scope calls. `holes: true` — a rewrite rule's TARGET only — lexes `@` / `...@` into\n * the marker literals; anywhere else a bare `@` is refused. */\nexport function parse(source: string, options?: { holes?: boolean }): ItxExpression {\n if (source.length > ITX_EXPRESSION_STRING_MAX_CHARS)\n throw codedError(\n \"EXPRESSION_TOO_LONG\",\n `itx expression: ${source.length} chars is over the ${ITX_EXPRESSION_STRING_MAX_CHARS}-char limit for the string form — a string expression is for what a person types; pass the parsed form instead: [\"itx\",\"workers\",[\"get\",{ source: … }]]`,\n );\n const s = source.trim();\n const steps: ItxExpression = [];\n let i = 0;\n function fail(m: string): never {\n throw new Error(`expression: ${m} in ${JSON.stringify(source)}`); // decl, not arrow: TS never-narrows\n }\n const readName = (): string => {\n const m = IDENT.exec(s.slice(i));\n if (!m) fail(`name expected at ${i}`);\n if (RESERVED.has(m[0])) fail(`reserved name \"${m[0]}\"`);\n i += m[0].length;\n return m[0];\n };\n steps.push(readName()); // the scope root (itx)\n while (i < s.length) {\n const c = s[i];\n if (/\\s/.test(c)) i++;\n else if (c === \".\") steps.push((i++, readName()));\n else if (c === \"(\") {\n const end = matchingParen(s, i);\n const raw = s.slice(i + 1, end).trim();\n // `@` outside a string literal: the marker (targets only), a refusal everywhere else.\n const inner = raw.replace(MARKERS_IN_ARGS, (match) => {\n if (isStringOrComment(match)) return match;\n if (!options?.holes)\n fail(\"`@` (the caller's input) is legal only in a rewrite rule's target\");\n return match === \"@\"\n ? JSON.stringify(ITX_EXPRESSION_HOLE)\n : `${JSON.stringify(ITX_EXPRESSION_MERGE_KEY)}:true`;\n });\n let args: unknown[] = [];\n try {\n if (inner !== \"\") args = JSON5.parse(`[${inner}]`) as unknown[];\n } catch (e) {\n fail(`call args are not JSON5 (${(e as Error).message})`);\n }\n const previous = steps.at(-1);\n if (Array.isArray(previous))\n steps.push([\"\", ...args]); // `f(x)(y)`: call the result itself\n else {\n const name = steps.pop();\n if (typeof name !== \"string\") fail(\"a call must follow a name\");\n if (steps.length === 0) fail(\"cannot call the scope symbol itself\");\n steps.push([name, ...args]);\n }\n i = end + 1;\n } else fail(`unexpected ${JSON.stringify(c)} at ${i}`);\n }\n return steps;\n}\n\n/** The array half, checked the way the parser checks the string half — every name step an identifier\n * that is not reserved, every call step `[method, ...args]` with an identifier method (or `\"\"`, the\n * anonymous call, only right after a call) — WITHOUT printing and re-parsing: a stored target carries a worker's whole source as\n * data, and that data must never meet the string codec (the 2 KiB cap, json5). Throws in the\n * parser's words. */\nfunction assertItxExpressionShape(expression: ItxExpression): void {\n const fail = (m: string): never => {\n throw new Error(`expression: ${m} in ${JSON.stringify(expression).slice(0, 200)}`);\n };\n // oxlint-disable-next-line iterate/simple-truthiness-check -- runtime shape validation of wire/stored data; the ItxExpression array type is a claim here, not a guarantee\n if (!Array.isArray(expression) || expression.length === 0)\n fail(\"an expression is a non-empty array\");\n const name = (step: string, what: string) => {\n if (!IDENT.test(step) || IDENT.exec(step)![0] !== step)\n fail(`${what} ${JSON.stringify(step)} is not an identifier`);\n if (RESERVED.has(step)) fail(`reserved name \"${step}\"`);\n };\n expression.forEach((step, i) => {\n if (typeof step === \"string\") {\n name(step, i === 0 ? \"the root\" : \"a name step\");\n return;\n }\n // oxlint-disable-next-line iterate/simple-truthiness-check -- runtime shape validation of wire/stored data; the step's static array type is a claim here, not a guarantee\n if (!Array.isArray(step) || typeof step[0] !== \"string\")\n fail(`step ${i} is neither a name nor [method, ...args]`);\n const [method] = step;\n if (method === \"\") {\n if (i === 0 || !Array.isArray(expression[i - 1]))\n fail(\"the anonymous call `f(x)(y)` follows a call\");\n } else name(method, \"a method\");\n if (i === 0) fail(\"a call on the root itself\");\n });\n // No hole check on the array half: `{ \"@\": true }` carried as DATA is data (edge#6) — only the\n // STRING form lexes a bare `@` into the marker, and only for a rule's target.\n}\n\n/** THE ONE NORMALIZER: either half, normalized to the array half and checked — a string is\n * parsed (short by rule), an array is shape-checked in place. Every function that takes an\n * `ItxExpressionInput` (the edge `invoke`, the resolver, the event builders, the prefix parser\n * below) enters through it. */\nexport function normalizedItxExpression(\n input: ItxExpressionInput,\n options?: { holes?: boolean },\n): ItxExpression {\n if (typeof input === \"string\") return parse(input, options);\n assertItxExpressionShape(input);\n return input;\n}\n\n/** Object args print with their keys SORTED, so two spellings of one object are one canonical string\n * — one rewrite-rule row, one facet memo, one library connection memo (library.ts) — the way\n * `jsonEqual` already matches them. A `JSON.stringify` / `JSON5.stringify` replacer. */\nexport const keySortedForPrint = (_key: string, value: unknown): unknown =>\n // oxlint-disable-next-line iterate/simple-truthiness-check -- `value` is `unknown` (a JSON replacer arg); the typeof discriminates real objects from primitive values\n value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? Object.fromEntries(\n Object.keys(value as Record<string, unknown>)\n .sort()\n .map((k) => [k, (value as Record<string, unknown>)[k]]),\n )\n : value;\n\n/** Canonical stored form: dotted path + `.method(args)` calls (args `JSON5.stringify`d, object keys\n * sorted). `holes: true` — a rewrite rule's TARGET only — spells the marker literals back as `@` /\n * `...@`, and `parse(print(e, { holes: true }), { holes: true })` round-trips; without it the\n * reserved literals print as the plain JSON5 they are, so a CALL that happens to carry `{ \"@\": true }`\n * as data round-trips through `parse` (no holes) unchanged — the resolve/invoke law holds for it. */\nexport function print(expr: ItxExpression, options?: { holes?: boolean }): string {\n return expr\n .map((step, i) => {\n const dot = i ? \".\" : \"\";\n if (typeof step === \"string\") return dot + step;\n const json = JSON5.stringify(step.slice(1), keySortedForPrint).slice(1, -1);\n const args = options?.holes\n ? json.replace(MARKERS_IN_PRINT, (match) =>\n match === \"{'@':true}\" ? \"@\" : match === \"'...@':true\" ? \"...@\" : match,\n )\n : json;\n return step[0] === \"\" ? `(${args})` : `${dot}${step[0]}(${args})`;\n })\n .join(\"\");\n}\n\n/** Parse an itx-expression prefix (either codec half) — `normalizedItxExpression` (so every step is\n * an identifier that is not reserved, in either half) plus the two refusals only a PREFIX has: the\n * anonymous call step (`f(x)(y)` — a prefix cannot call a result), and a call step with NO args,\n * which pins nothing and is the same prefix as the plain name: spell `itx.ai.run`. */\nexport function parseItxExpressionPrefix(source: ItxExpressionInput): ItxExpressionPrefix {\n const expr = normalizedItxExpression(source);\n const spelled = typeof source === \"string\" ? source : print(expr);\n for (const step of expr) {\n if (!Array.isArray(step)) continue;\n if (step[0] === \"\")\n throw new Error(`an itx-expression prefix cannot call a result — ${JSON.stringify(spelled)}`);\n if (step.length === 1)\n throw new Error(\n `an itx-expression prefix pins literal args with a call step — ${JSON.stringify(spelled)} has \"${step[0]}()\" with none; spell \"${step[0]}\"`,\n );\n }\n return expr;\n}\n\n/** THE ONE canonical spelling of an itx-expression prefix — the rewrite-rule table's key, what a lent\n * stub is keyed by through `provide`'s sugar: parsed, then printed (dotted names; pinned args as JSON5\n * literals). */\nexport function canonicalItxExpressionPrefix(source: ItxExpressionInput): string {\n return print(parseItxExpressionPrefix(source));\n}\n\n// ── invoke handle ── THE DOTTED SURFACE: how a surface that declares only fixed methods is\n// spoken as deep dotted access (`itx.slack.chat.postMessage({...})`), every unknown segment\n// accumulating into ONE `invoke(expression)` dispatch, `[...root, ...prefix, [method, ...args]]`.\n// Declared members always win. The pieces: the reserved names, the function-backed PATH PROXY, the\n// PROTOTYPE HOP that installs the fallback on a class, `InvokeHandle` (the genuine RpcTarget a\n// mid-chain capability is handed back as), and `walkStepsOnRpcStub`.\n//\n// WHY A PROTOTYPE HOP AND NOT A PROXY AROUND THE INSTANCE (tried FIRST and reverted): workerd RPC\n// classifies a call's RESULT for promise pipelining with native brand checks a JS Proxy can never\n// pass (`serializeJsValueWithPipeline` in worker-rpc.c++ → `NonPipelinable`; cloudflare/workerd#6873).\n// So a surface returned FROM A METHOD must hand back a REAL, unproxied instance or every pipelined\n// call on it dies with \"The RPC receiver does not implement the method ...\". A mid-chain call\n// returns its handle ACROSS an RPC boundary (`itx.facets.get('b').hello()` is two dispatches), and\n// capnweb's RpcTarget IS the native `cloudflare:workers` RpcTarget on workerd, so a real RpcTarget\n// passes on both hops. The hop squares that with dynamic dispatch by inserting one proxied link\n// BETWEEN `Class.prototype` and its parent:\n//\n// instance ──proto──▶ Class.prototype ──proto──▶ Proxy(hop) ──proto──▶ parent\n//\n// - The instance is a genuine, natively-branded RpcTarget → the pipeline classifier accepts it.\n// - Declared members resolve BEFORE the hop — built-ins win, so a dynamic capability can never\n// shadow a declared name (the deliberate trade-off).\n// - Unknown string keys reach the hop's `get` trap and become path proxies, dispatched via the\n// receiver's own `invoke`; the receiver IS the invoker, so it wires with zero glue.\n// - Instances stay clean of own properties, so Workers RPC's instance-property protection needs\n// no `getOwnPropertyDescriptor` help.\n//\n// KNOWN QUIRKS: no `has` trap on the hop, so `\"x\" in instance`\n// reflects DECLARED members only while `instance.x` conjures a dispatcher — feature-detect with\n// access, not `in`; a typo'd built-in (`itx.strems`) is a syntactically valid dynamic dispatch that\n// fails at the capability table, not a crisp missing-method error.\n//\n// apps/os's library tier (src/library.ts) builds its connectors on this section and the codec, as a\n// userspace worker would (library.test.ts pins that).\n\n/** The dispatch method every dotted miss collapses onto. `IterateContextRpcTarget` implements it directly (root\n * `itx`); a mid-chain `InvokeHandle` implements it relative to itself (empty root). */\ntype InvokeTarget = {\n invoke(itxExpression: ItxExpression): unknown;\n};\n\n/** Names that must NEVER become dynamic capability segments — a dispatcher answering them would turn\n * a plain property probe into a live capability call. Enforced at the prototype-chain hop and at\n * every depth of the path proxies it hands out. Two kinds, one set: */\nconst RESERVED_SEGMENT_NAMES: ReadonlySet<string> = new Set([\n // JS/RPC protocol machinery a framework or capnweb probes on any object (`then` above all: an\n // instance must never look thenable, or every `await` of it would resolve a capability).\n \"__defineGetter__\",\n \"__defineSetter__\",\n \"__lookupGetter__\",\n \"__lookupSetter__\",\n \"__proto__\",\n \"apply\",\n \"bind\",\n \"call\",\n \"catch\",\n \"constructor\",\n \"dup\",\n \"finally\",\n \"hasOwnProperty\",\n \"isPrototypeOf\",\n \"map\",\n \"onRpcBroken\",\n \"propertyIsEnumerable\",\n \"prototype\",\n \"then\",\n \"toLocaleString\",\n \"toString\",\n \"valueOf\",\n // Names common protocols LOOK UP on arbitrary objects and CALL if callable: JSON.stringify\n // (toJSON) and vitest/jest equality (asymmetricMatch). The asymmetricMatch case is worse than\n // noise — vitest treats any object with a callable asymmetricMatch as an asymmetric matcher, and\n // a dispatcher returning a (truthy) Promise makes the equality SPURIOUSLY PASS. The bar for this\n // half is HIGH (these names become unreachable as dotted segments; explicit invoke still\n // reaches them): probed-and-called by ubiquitous protocols AND implausible as capability names.\n \"toJSON\",\n \"asymmetricMatch\",\n]);\n\n/** The path proxy: a function-backed Proxy (not an RpcTarget instance) — each missing property\n * extends `path`, and applying the function reduces the whole accumulated access into ONE\n * `invoke(expression)` call, `[...root, ...path.slice(0, -1), [path.at(-1), ...args]]`. */\nfunction createItxExpressionPathProxy(\n invoker: InvokeTarget,\n root: readonly string[],\n path: string[],\n): unknown {\n const valueFor = (key: string) => createItxExpressionPathProxy(invoker, root, [...path, key]);\n return new Proxy(function () {}, {\n apply(_target, _thisArg, args) {\n const method = path[path.length - 1];\n const expr: ItxExpression = [...root, ...path.slice(0, -1), [method, ...(args as unknown[])]];\n return invoker.invoke(expr);\n },\n get(target, key, receiver) {\n if (typeof key === \"symbol\") return Reflect.get(target, key, receiver);\n if (RESERVED_SEGMENT_NAMES.has(key)) return undefined;\n return valueFor(key);\n },\n getOwnPropertyDescriptor(target, key) {\n const descriptor = Reflect.getOwnPropertyDescriptor(target, key);\n if (descriptor) return descriptor;\n if (typeof key === \"symbol\" || RESERVED_SEGMENT_NAMES.has(key)) return undefined;\n // Cap'n Web's server-side path traversal probes own descriptors before reading a segment, so\n // dynamic roots must look discoverable here to reach the apply trap.\n return { configurable: true, enumerable: true, value: valueFor(key), writable: false };\n },\n has(target, key) {\n if (typeof key === \"symbol\") return key in target;\n return !RESERVED_SEGMENT_NAMES.has(key);\n },\n });\n}\n\n/** Install the hop drawn in the header on a class's PROTOTYPE CHAIN, with the scope `root` (`[\"itx\"]`\n * for the edge context, `[]` for a handle). Call ONCE per class. Constructor inheritance is\n * untouched — only `Class.prototype`'s parent link changes, and the hop forwards everything it does\n * not intercept. */\nexport function installPrototypeInvokeFallback<T extends abstract new (...args: never[]) => object>(\n cls: T,\n root: readonly string[],\n): void {\n const parentPrototype = Object.getPrototypeOf(cls.prototype) as object;\n const hop = new Proxy(Object.create(parentPrototype) as object, {\n get(hopTarget, key, receiver) {\n // Symbols and anything the parent chain already answers (dispose protocol, capnweb\n // internals, Object.prototype) pass through with the instance as receiver.\n if (typeof key === \"symbol\" || key in hopTarget) {\n return Reflect.get(hopTarget, key, receiver);\n }\n if (RESERVED_SEGMENT_NAMES.has(key)) return undefined;\n // The dynamic fallback exists for INSTANCES. A lookup whose receiver is not one — someone\n // probing `Class.prototype.foo` directly, a framework walking prototypes — must see plain\n // \"undefined\", not conjure a dispatcher over an uninitialized receiver.\n if (!(receiver instanceof (cls as unknown as abstract new (...args: never[]) => object))) {\n return undefined;\n }\n // The receiver IS the invoker (it implements invoke). Its method resolves at CALL\n // time, so a trap firing mid-construction (a property miss on `this` before field initializers\n // ran) can't bake a dispatcher over half-initialized state into the path proxy.\n return createItxExpressionPathProxy(receiver as unknown as InvokeTarget, root, [key]);\n },\n });\n Object.setPrototypeOf(cls.prototype, hop);\n}\n\n/** A branded, pipelinable handle for a MID-CHAIN capability (`facets.get(name)`, `cd(path)`,\n * `workers.get(spec)`, a lent stub) whose unknown dotted members reduce into ONE dispatch of the\n * itx-expression STEPS relative to it; the constructor's `dispatch` routes those steps into the\n * underlying object. Declared members (`invoke` / `applyRoot`) win over the fallback, so a\n * capability cannot be named either — the two reserved words this wrapper adds. */\nexport class InvokeHandle extends RpcTarget {\n readonly #dispatchItxExpressionSteps: (itxExpressionSteps: ItxExpression) => unknown;\n constructor(dispatchItxExpressionSteps: (itxExpressionSteps: ItxExpression) => unknown) {\n super();\n this.#dispatchItxExpressionSteps = dispatchItxExpressionSteps;\n }\n /** THE dispatch method the prototype hop reduces onto; the expression is RELATIVE to this handle. */\n invoke(itxExpressionSteps: ItxExpression): unknown {\n return this.#dispatchItxExpressionSteps(itxExpressionSteps);\n }\n /** Call the bare capability this handle fronts — the ANONYMOUS call step (how a rewritten call\n * whose target IS a handle calls it: `handle(events, range)`). */\n applyRoot(args: unknown[]): unknown {\n return this.#dispatchItxExpressionSteps([[\"\", ...args]]);\n }\n}\ninstallPrototypeInvokeFallback(InvokeHandle, []);\n\n/** Walk itx-expression steps off a capnweb stub; the ANONYMOUS call step (`\"\"`) calls the value\n * itself (a bare function lent as a capability). NO await inside the loop: on a capnweb stub every\n * step is a PIPELINED path, so an n-step chain costs ONE round trip, flushed by the caller's single\n * await. A DIRECT call on the stub, never `.apply`: reading `.apply` off a capnweb stub's method is\n * itself a pipelined remote path, and calling it sends the stub as an argument, which a facet stub\n * refuses with a DataCloneError. What a connector over a lent stub or a remote capnweb API walks. */\nexport function walkStepsOnRpcStub(stub: unknown, steps: ItxExpression): unknown {\n let value: unknown = stub;\n for (const step of steps) {\n if (typeof step === \"string\") value = (value as Record<string, unknown>)[step];\n else {\n const [method, ...args] = step;\n value =\n method === \"\"\n ? (value as (...a: unknown[]) => unknown)(...args)\n : (value as Record<string, (...a: unknown[]) => unknown>)[method](...args);\n }\n }\n return value;\n}\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ //#region src/lib.ts
2
+ /** A plain Error carrying `code` (+ optional `data`) as own enumerable properties. */
3
+ function codedError(code, message, data) {
4
+ return Object.assign(new Error(message), data === void 0 ? { code } : {
5
+ code,
6
+ data
7
+ });
8
+ }
9
+ const isRecord = (v) => typeof v === "object" && !!v && !Array.isArray(v);
10
+ /** Structural deep-equal over plain JSON values — order-insensitive, unbudgeted (JSON is acyclic).
11
+ * THE one deep-equal: the live-state diff's "don't emit" test AND the idempotency-body compare
12
+ * (re-exported through stream/processor.ts). The `Object.hasOwn(b, k)` guard is load-bearing — without
13
+ * it, two objects with the same key COUNT but different key SETS compare equal. */
14
+ function jsonEqual(a, b) {
15
+ if (Object.is(a, b)) return true;
16
+ if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => jsonEqual(v, b[i]));
17
+ if (isRecord(a) && isRecord(b)) {
18
+ const ka = Object.keys(a);
19
+ return ka.length === Object.keys(b).length && ka.every((k) => Object.hasOwn(b, k) && jsonEqual(a[k], b[k]));
20
+ }
21
+ return false;
22
+ }
23
+ async function withTimeout(promise, ms, what) {
24
+ let timer;
25
+ try {
26
+ return await Promise.race([promise, new Promise((_, reject) => {
27
+ timer = setTimeout(() => reject(codedError("TIMEOUT", `${typeof what === "function" ? what() : what}: no answer in ${ms / 1e3}s`)), ms);
28
+ })]);
29
+ } finally {
30
+ if (timer) clearTimeout(timer);
31
+ }
32
+ }
33
+ //#endregion
34
+ export { jsonEqual as n, withTimeout as r, codedError as t };
35
+
36
+ //# sourceMappingURL=lib-BWr-5mFO.mjs.map