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,75 +0,0 @@
1
- //#region src/next/project-ingress.ts
2
- /** A DNS label: lowercase letters and digits, single hyphens inside. */
3
- const DNS_LABEL = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4
- /** An app label: a DNS label that is also an itx identifier (it becomes a step, `itx.apps.<label>`). */
5
- const APP_LABEL = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
6
- /** The labels `host` has under `hostname` — `site--p.iterate.app` ⇒ `["site--p"]` — lowercased, a
7
- * trailing dot (a fully-qualified Host, `site--p.base.`) dropped; null when `host` is not under
8
- * `hostname` at all. */
9
- function labelsUnder(host, hostname) {
10
- const name = host.toLowerCase().replace(/\.$/, "");
11
- const suffix = `.${hostname.toLowerCase()}`;
12
- return name.endsWith(suffix) ? name.slice(0, -suffix.length).split(".") : null;
13
- }
14
- /** The project + app `url` names under `routing`, or null when it names none. Pure. */
15
- function projectAddressOf(routing, url, platformOrigin) {
16
- if (!routing) return null;
17
- if (routing.type === "subdomains") {
18
- const labels = labelsUnder(url.hostname, routing.hostname);
19
- if (!labels || labels.length > 2) return null;
20
- const [first, second] = labels;
21
- const separator = first.startsWith("xn--") ? -1 : first.indexOf("--");
22
- const [app, project] = second !== void 0 ? [first, second] : separator === -1 ? [null, first] : [first.slice(0, separator), first.slice(separator + 2)];
23
- if (!DNS_LABEL.test(project) || app !== null && !APP_LABEL.test(app)) return null;
24
- return {
25
- app,
26
- project,
27
- basePath: ""
28
- };
29
- }
30
- if (url.origin !== new URL(platformOrigin).origin) return null;
31
- const [, prefix, project = "", app] = url.pathname.split("/");
32
- if (prefix !== "projects" || !DNS_LABEL.test(project)) return null;
33
- if (app === void 0 || app === "") return {
34
- app: null,
35
- project,
36
- basePath: `/projects/${project}`
37
- };
38
- if (!APP_LABEL.test(app)) return null;
39
- return {
40
- app,
41
- project,
42
- basePath: `/projects/${project}/${app}`
43
- };
44
- }
45
- /** A CUSTOM HOSTNAME — one of a deployment's own that IS a project's apex (os-next
46
- * `urls.temporaryCustomHostnames`, `{ "iterate2.com": "iterate" }`): the apex shape, `app: null`, so the
47
- * project's config worker `fetch` answers exactly as it does on `<project>.<hostname>`. Null for a
48
- * hostname the map does not name — the map's spelling, case and a trailing dot forgiven, no wildcard
49
- * under it. Pure. */
50
- function customProjectHostOf(hostname, hostnames) {
51
- const project = hostnames[hostname.toLowerCase().replace(/\.$/, "")];
52
- return project ? {
53
- app: null,
54
- project
55
- } : null;
56
- }
57
- /** The URL of `app` (null ⇒ the apex, the config worker) in `project` under `routing`, at `path`
58
- * (default "/", must start with "/"). Null when there is no ingress, or when the result would not
59
- * parse back to the same address (a bad label; a `path` that climbs out of its app). subdomains:
60
- * the protocol and port are `platformOrigin`'s (local dev is `http://localhost:8788`, so
61
- * `http://<app>--<project>.localhost:8788/…`); paths: `<platformOrigin>/projects/<project>[/<app>]<path>`. Pure. */
62
- function projectUrlOf(routing, platformOrigin, target) {
63
- if (!routing) return null;
64
- const path = target.path || "/";
65
- if (!path.startsWith("/")) throw new Error(`projectUrlOf: path must start with "/": ${path}`);
66
- const app = target.app || null;
67
- const origin = new URL(platformOrigin);
68
- const url = routing.type === "subdomains" ? new URL(path, `${origin.protocol}//${app ? `${app}--` : ""}${target.project}.${routing.hostname}${origin.port ? `:${origin.port}` : ""}`) : new URL(`/projects/${target.project}${app ? `/${app}` : ""}${path}`, origin.origin);
69
- const parsed = projectAddressOf(routing, url, platformOrigin);
70
- return parsed && parsed.project === target.project && parsed.app === app ? url : null;
71
- }
72
- //#endregion
73
- export { customProjectHostOf, projectAddressOf, projectUrlOf };
74
-
75
- //# sourceMappingURL=project-ingress.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"project-ingress.mjs","names":[],"sources":["../../src/next/project-ingress.ts"],"sourcesContent":["// next/project-ingress.ts — HOW PROJECTS ARE REACHED OVER HTTP, both directions in ONE pure file. A\n// deployment's `urls.ingressRouting` (os-next app-config.ts) names the mechanism; `projectAddressOf`\n// parses a request's URL into the project and app it names, `projectUrlOf` composes the URL of an\n// app in a project. The platform's edge parses; the platform, the dash and an app compose — one\n// implementation, table-tested to round-trip (project-ingress.test.ts). No imports.\n//\n// subdomains `<app>--<project>.<hostname>`, `<app>.<project>.<hostname>`, the apex `<project>.<hostname>`\n// — every app its own origin, under one wildcard on `hostname`.\n// paths `<platformOrigin>/projects/<project>/<app>/…`, the apex `<platformOrigin>/projects/<project>/`\n// — one origin (workers.dev has no wildcard), every project under `/projects/` so the\n// platform's own paths (`/api`, `/mcp`, `/login`, …) need no reserved list; the edge\n// sandboxes what an app serves.\n\n/** How projects are reached over HTTP; null ⇒ no ingress (`/api` and `/mcp` still answer). */\nexport type IngressRouting = { type: \"subdomains\"; hostname: string } | { type: \"paths\" } | null;\n\n/** What a request names: the project (its slug, as written — whether it EXISTS is the directory's\n * answer), the app (null ⇒ the apex: the project's config worker answers), and the path prefix the\n * edge strips before the app sees the URL (\"\" under subdomains; \"/projects/<project>\" or\n * \"/projects/<project>/<app>\" under paths). */\nexport type ProjectAddress = { project: string; app: string | null; basePath: string };\n\n/** A DNS label: lowercase letters and digits, single hyphens inside. */\nconst DNS_LABEL = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n/** An app label: a DNS label that is also an itx identifier (it becomes a step, `itx.apps.<label>`). */\nconst APP_LABEL = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;\n\n/** The labels `host` has under `hostname` — `site--p.iterate.app` ⇒ `[\"site--p\"]` — lowercased, a\n * trailing dot (a fully-qualified Host, `site--p.base.`) dropped; null when `host` is not under\n * `hostname` at all. */\nfunction labelsUnder(host: string, hostname: string): string[] | null {\n const name = host.toLowerCase().replace(/\\.$/, \"\");\n const suffix = `.${hostname.toLowerCase()}`;\n return name.endsWith(suffix) ? name.slice(0, -suffix.length).split(\".\") : null;\n}\n\n/** The project + app `url` names under `routing`, or null when it names none. Pure. */\nexport function projectAddressOf(\n routing: IngressRouting,\n url: URL,\n platformOrigin: string,\n): ProjectAddress | null {\n if (!routing) return null;\n if (routing.type === \"subdomains\") {\n const labels = labelsUnder(url.hostname, routing.hostname);\n if (!labels || labels.length > 2) return null; // deeper than `<app>.<project>` is not a project host\n const [first, second] = labels as [string, string?];\n const separator = first.startsWith(\"xn--\") ? -1 : first.indexOf(\"--\"); // `xn--…` is an IDN label (punycode), never `<app>--<project>`\n const [app, project] =\n // oxlint-disable-next-line iterate/simple-truthiness-check -- a PRESENT-but-empty second label (`<app>..<base>`) is the `<app>.<project>` shape (rejected below by DNS_LABEL), not the single-label `<project>` shape a truthiness check would route it to\n second !== undefined\n ? [first, second] // `<app>.<project>`\n : separator === -1\n ? [null, first] // the apex, `<project>`\n : [first.slice(0, separator), first.slice(separator + 2)]; // `<app>--<project>`\n // oxlint-disable-next-line iterate/simple-truthiness-check -- an empty app label (`--<project>.<base>`) must still be rejected by APP_LABEL; truthiness would skip the check and admit it\n if (!DNS_LABEL.test(project) || (app !== null && !APP_LABEL.test(app))) return null;\n return { app, project, basePath: \"\" };\n }\n if (url.origin !== new URL(platformOrigin).origin) return null;\n const [, prefix, project = \"\", app] = url.pathname.split(\"/\");\n if (prefix !== \"projects\" || !DNS_LABEL.test(project)) return null;\n // oxlint-disable-next-line iterate/simple-truthiness-check -- `/projects/<project>` and `/projects/<project>/` are both the apex; a present-but-empty next segment is not an app\n if (app === undefined || app === \"\")\n return { app: null, project, basePath: `/projects/${project}` };\n if (!APP_LABEL.test(app)) return null;\n return { app, project, basePath: `/projects/${project}/${app}` };\n}\n\n/** A CUSTOM HOSTNAME — one of a deployment's own that IS a project's apex (os-next\n * `urls.temporaryCustomHostnames`, `{ \"iterate2.com\": \"iterate\" }`): the apex shape, `app: null`, so the\n * project's config worker `fetch` answers exactly as it does on `<project>.<hostname>`. Null for a\n * hostname the map does not name — the map's spelling, case and a trailing dot forgiven, no wildcard\n * under it. Pure. */\nexport function customProjectHostOf(\n hostname: string,\n hostnames: Record<string, string>,\n): { app: null; project: string } | null {\n const project = hostnames[hostname.toLowerCase().replace(/\\.$/, \"\")];\n return project ? { app: null, project } : null;\n}\n\n/** The URL of `app` (null ⇒ the apex, the config worker) in `project` under `routing`, at `path`\n * (default \"/\", must start with \"/\"). Null when there is no ingress, or when the result would not\n * parse back to the same address (a bad label; a `path` that climbs out of its app). subdomains:\n * the protocol and port are `platformOrigin`'s (local dev is `http://localhost:8788`, so\n * `http://<app>--<project>.localhost:8788/…`); paths: `<platformOrigin>/projects/<project>[/<app>]<path>`. Pure. */\nexport function projectUrlOf(\n routing: IngressRouting,\n platformOrigin: string,\n target: { project: string; app?: string | null; path?: string },\n): URL | null {\n if (!routing) return null;\n const path = target.path || \"/\";\n if (!path.startsWith(\"/\")) throw new Error(`projectUrlOf: path must start with \"/\": ${path}`);\n const app = target.app || null;\n const origin = new URL(platformOrigin);\n const url =\n routing.type === \"subdomains\"\n ? new URL(\n path,\n `${origin.protocol}//${app ? `${app}--` : \"\"}${target.project}.${routing.hostname}${origin.port ? `:${origin.port}` : \"\"}`,\n )\n : new URL(`/projects/${target.project}${app ? `/${app}` : \"\"}${path}`, origin.origin);\n const parsed = projectAddressOf(routing, url, platformOrigin);\n return parsed && parsed.project === target.project && parsed.app === app ? url : null;\n}\n"],"mappings":";;AAuBA,MAAM,YAAY;;AAElB,MAAM,YAAY;;;;AAKlB,SAAS,YAAY,MAAc,UAAmC;CACpE,MAAM,OAAO,KAAK,YAAY,CAAC,CAAC,QAAQ,OAAO,EAAE;CACjD,MAAM,SAAS,IAAI,SAAS,YAAY;CACxC,OAAO,KAAK,SAAS,MAAM,IAAI,KAAK,MAAM,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,GAAG,IAAI;AAC5E;;AAGA,SAAgB,iBACd,SACA,KACA,gBACuB;CACvB,IAAI,CAAC,SAAS,OAAO;CACrB,IAAI,QAAQ,SAAS,cAAc;EACjC,MAAM,SAAS,YAAY,IAAI,UAAU,QAAQ,QAAQ;EACzD,IAAI,CAAC,UAAU,OAAO,SAAS,GAAG,OAAO;EACzC,MAAM,CAAC,OAAO,UAAU;EACxB,MAAM,YAAY,MAAM,WAAW,MAAM,IAAI,KAAK,MAAM,QAAQ,IAAI;EACpE,MAAM,CAAC,KAAK,WAEV,WAAW,KAAA,IACP,CAAC,OAAO,MAAM,IACd,cAAc,KACZ,CAAC,MAAM,KAAK,IACZ,CAAC,MAAM,MAAM,GAAG,SAAS,GAAG,MAAM,MAAM,YAAY,CAAC,CAAC;EAE9D,IAAI,CAAC,UAAU,KAAK,OAAO,KAAM,QAAQ,QAAQ,CAAC,UAAU,KAAK,GAAG,GAAI,OAAO;EAC/E,OAAO;GAAE;GAAK;GAAS,UAAU;EAAG;CACtC;CACA,IAAI,IAAI,WAAW,IAAI,IAAI,cAAc,CAAC,CAAC,QAAQ,OAAO;CAC1D,MAAM,GAAG,QAAQ,UAAU,IAAI,OAAO,IAAI,SAAS,MAAM,GAAG;CAC5D,IAAI,WAAW,cAAc,CAAC,UAAU,KAAK,OAAO,GAAG,OAAO;CAE9D,IAAI,QAAQ,KAAA,KAAa,QAAQ,IAC/B,OAAO;EAAE,KAAK;EAAM;EAAS,UAAU,aAAa;CAAU;CAChE,IAAI,CAAC,UAAU,KAAK,GAAG,GAAG,OAAO;CACjC,OAAO;EAAE;EAAK;EAAS,UAAU,aAAa,QAAQ,GAAG;CAAM;AACjE;;;;;;AAOA,SAAgB,oBACd,UACA,WACuC;CACvC,MAAM,UAAU,UAAU,SAAS,YAAY,CAAC,CAAC,QAAQ,OAAO,EAAE;CAClE,OAAO,UAAU;EAAE,KAAK;EAAM;CAAQ,IAAI;AAC5C;;;;;;AAOA,SAAgB,aACd,SACA,gBACA,QACY;CACZ,IAAI,CAAC,SAAS,OAAO;CACrB,MAAM,OAAO,OAAO,QAAQ;CAC5B,IAAI,CAAC,KAAK,WAAW,GAAG,GAAG,MAAM,IAAI,MAAM,2CAA2C,MAAM;CAC5F,MAAM,MAAM,OAAO,OAAO;CAC1B,MAAM,SAAS,IAAI,IAAI,cAAc;CACrC,MAAM,MACJ,QAAQ,SAAS,eACb,IAAI,IACF,MACA,GAAG,OAAO,SAAS,IAAI,MAAM,GAAG,IAAI,MAAM,KAAK,OAAO,QAAQ,GAAG,QAAQ,WAAW,OAAO,OAAO,IAAI,OAAO,SAAS,IACxH,IACA,IAAI,IAAI,aAAa,OAAO,UAAU,MAAM,IAAI,QAAQ,KAAK,QAAQ,OAAO,MAAM;CACxF,MAAM,SAAS,iBAAiB,SAAS,KAAK,cAAc;CAC5D,OAAO,UAAU,OAAO,YAAY,OAAO,WAAW,OAAO,QAAQ,MAAM,MAAM;AACnF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"react.mjs","names":[],"sources":["../../src/next/client/react.tsx"],"sourcesContent":["/** @jsxImportSource react */\n// client/react.tsx — the REACT binding for clean-room live state, shared by every UI (the hosted\n// /demo and the control-plane console). `useLiveState` subscribes a component to a producer's live\n// state (a processor slug, a mini-app key), seeds through its door, and re-renders on every synced\n// delta via `useSyncExternalStore` over the LiveStateStore. The transport and the store\n// (client/live-state.ts) stay framework-free, so this is the ONE file that imports React.\n//\n// Adapted from apps/os's `useLiveState` (packages/iterate/src/sdk/capnweb/react.tsx), kept to the one\n// shape a UI/test needs — no reconnect/backoff/ping-watchdog (that policy belongs to whoever owns the\n// capnweb session; here the caller passes a ready `itx`).\nimport { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\nimport {\n connectLiveState,\n type LiveStateItx,\n type LiveStateSeed,\n type LiveStateStore,\n} from \"./live-state.ts\";\n\nexport type LiveStateStatus = \"connecting\" | \"live\" | \"error\";\n\n/** One live state as a component reads it: the latest value (undefined until the first seed lands),\n * the revision it is at, whether its subscription is connecting, live or failed, and the failure. */\nexport type LiveStateResult<S = unknown> = {\n value: S | undefined;\n rev: number | null;\n status: LiveStateStatus;\n error?: string;\n};\n\n/** Subscribe to a producer's live state and render its latest value. Pass a ready `itx` (a capnweb\n * `api.authenticate(credentials).user` or `.projects.get(id)`), the producer's `key`, and a `door`\n * thunk that reads `{rev, state}` (`() => itx.invoke(\"itx.facets.get('slug').liveSnapshot()\")`).\n * Re-subscribes when the session, `key`, or `name` changes; unmount (and every re-subscribe)\n * disposes the previous server-side subscription. */\nexport function useLiveState<S>(\n itx: LiveStateItx | undefined,\n opts: { key: string; name?: string; door: () => Promise<LiveStateSeed<S>> },\n): LiveStateResult<S> {\n const [store, setStore] = useState<LiveStateStore<S> | undefined>();\n const [status, setStatus] = useState<LiveStateStatus>(\"connecting\");\n const [error, setError] = useState<string | undefined>();\n // The door thunk is a fresh arrow every render; hold the latest so the effect need not re-run per\n // render. The effect SNAPSHOTS it at connect time, so an old subscription's gap heal can never\n // read a NEWER key's door (cross-key contamination after a key/session switch).\n const doorRef = useRef(opts.door);\n doorRef.current = opts.door;\n\n useEffect(() => {\n setStore(undefined);\n setStatus(\"connecting\");\n setError(undefined);\n if (!itx) return;\n const door = doorRef.current; // pinned to THIS key/session for the connection's whole life\n let disposed = false;\n let dispose: (() => Promise<void>) | undefined;\n const unmounted = new AbortController(); // an unmount while the first seed is pending recalls the row\n connectLiveState<S>(itx, {\n key: opts.key,\n name: opts.name,\n door,\n signal: unmounted.signal,\n onResync: (r) => {\n if (disposed) return;\n if (r === \"healed\") {\n setStatus(\"live\");\n setError(undefined);\n } else {\n // the store keeps its last value; the next delta retries the heal\n setStatus(\"error\");\n setError(r.message);\n }\n },\n }).then(\n (conn) => {\n dispose = conn.dispose;\n if (disposed) {\n void conn.dispose(); // unmounted while connecting — still tear the mount down\n return;\n }\n setStore(conn.store);\n setStatus(\"live\");\n },\n (e: unknown) => {\n if (disposed) return;\n setError(e instanceof Error ? e.message : String(e));\n setStatus(\"error\");\n },\n );\n return () => {\n disposed = true;\n unmounted.abort();\n void dispose?.();\n };\n }, [itx, opts.key, opts.name]);\n\n const subscribe = useCallback(\n (cb: () => void) => (store ? store.subscribe(cb) : () => {}),\n [store],\n );\n const value = useSyncExternalStore(\n subscribe,\n () => store?.get(),\n () => undefined,\n );\n return { value, rev: store?.rev() ?? null, status, error };\n}\n\n// ── the iterate context ── the data half of a general-purpose context view (packages/ui\n// `components/context-view`, the rendering half): every committed event of a context, live; the rows\n// of its processors table; who is here; named facets' live state. ONE hook here, pure components\n// there, so the UI kit stays free of the SDK and any app — the dash, the agents app — composes the two.\n\n/** One committed event as the hook hands it out: the itx envelope, structurally. */\nexport type IterateContextEvent = {\n offset: number;\n type: string;\n createdAt: string;\n payload?: unknown;\n metadata?: Record<string, unknown>;\n idempotencyKey?: string;\n source?: {\n principal?: { actor: string; email?: string };\n grant?: string;\n processor?: { slug: string; version: string };\n };\n};\n\n/** One row of a context's processors table (`itx.processors.list()`), structurally. */\nexport type IterateContextProcessorRow = {\n name: string;\n target: string;\n consumes?: string[];\n configuredAtOffset: number;\n hostedFacet?: { name: string; className: string; cacheKey?: string; restarts: number };\n};\n\n/** One presence: who acted on the context and when last, from the log's stamps. */\nexport type IterateContextPresence = {\n actor: string;\n email?: string;\n grant?: string;\n lastSeenAt: string;\n};\n\n/** The slice of a context handle `useIterateContext` reads — a capnweb `IterateContextApi` stub\n * satisfies it structurally. `rpcStubs` is optional: a handle typed without the census (a project\n * context's client type) still gets the log, the table and the actors. `invoke` seeds a named\n * facet's live state (`itx.facets.get('<name>').liveSnapshot()`, as an expression). */\nexport type IterateContextHandle = LiveStateItx & {\n readEvents(\n afterOffset?: number,\n limit?: number,\n ): Promise<{ events: unknown[]; atHead: boolean; scannedThroughOffset: number }>;\n processors: { list(): Promise<IterateContextProcessorRow[]> | IterateContextProcessorRow[] };\n rpcStubs?: { list(): Promise<string[]> | string[] };\n invoke(call: string): Promise<unknown>;\n};\n\n/** A wire event (a capnweb proxy value or a plain object) as an `IterateContextEvent`, or null when\n * it is not a committed row. Structural, not a schema: the transport validated it; this only refuses\n * a shape the view cannot place (no offset, type or time). */\nfunction toIterateContextEvent(raw: unknown): IterateContextEvent | null {\n const value = JSON.parse(JSON.stringify(raw)) as Record<string, unknown> | null;\n if (\n !value ||\n typeof value.offset !== \"number\" ||\n typeof value.type !== \"string\" ||\n typeof value.createdAt !== \"string\"\n )\n return null;\n return value as unknown as IterateContextEvent; // the three fields checked are all the hook indexes by\n}\n\n/** A named live state before its first seed lands — and before the effect that opens it has run. */\nconst LIVE_STATE_CONNECTING: LiveStateResult = {\n value: undefined,\n rev: null,\n status: \"connecting\",\n};\n\n/** THE ITERATE CONTEXT, live — one hook, one stream subscription. THE LOG: subscribe to every\n * committed event (or `consumes`) BEFORE the catch-up read, so nothing lands between the two;\n * pushes and pages both dedupe into one map by offset; `caughtUp` once the read reached the head;\n * `error` when the connect failed. Off that same log, THE PROCESSORS TABLE, re-read whenever the\n * log grows a row-changing event (a subscription configured, halted or resumed — the table is core\n * state, one call away, no push of its own), and WHO IS HERE: the rpc stubs lent right now\n * (`itx.rpcStubs.list()` — physical, re-read at every new head, since presence changes are\n * ephemeral facts) and, from the log, every principal that acted, newest first. And named facets'\n * LIVE STATE, each seeded through `itx.facets.get('<name>').liveSnapshot()` — one entry per name,\n * always. `liveState` OMITTED opens `core` (the core reduce answers under that name) plus every\n * hosted facet in the processors table the hook holds, following the table as it loads and changes;\n * `liveState` GIVEN is exactly the names to open, no implicit `core`. Re-connects when `itx`\n * changes; unmount disposes every server-side subscription. */\nexport function useIterateContext(\n itx: IterateContextHandle | undefined,\n opts: { consumes?: string[]; liveState?: string[] } = {},\n): {\n events: IterateContextEvent[];\n caughtUp: boolean;\n error?: string;\n processors: { rows: IterateContextProcessorRow[]; loaded: boolean; error?: string };\n presence: { actors: IterateContextPresence[]; rpcStubs: string[] };\n liveState: Record<string, LiveStateResult>;\n} {\n // ── the log ──\n const [events, setEvents] = useState<Map<number, IterateContextEvent>>(() => new Map());\n const [caughtUp, setCaughtUp] = useState(false);\n const [error, setError] = useState<string | undefined>();\n const consumesKey = JSON.stringify(opts.consumes || [\"*\"]);\n useEffect(() => {\n setEvents(new Map());\n setCaughtUp(false);\n setError(undefined);\n if (!itx) return;\n let disposed = false;\n const merge = (batch: unknown[]) =>\n setEvents((held) => {\n const next = new Map(held);\n for (const raw of batch) {\n const event = toIterateContextEvent(raw);\n if (event) next.set(event.offset, event);\n }\n return next;\n });\n let subscription: { [Symbol.dispose](): void } | undefined;\n (async () => {\n const handle = await itx.subscribe({\n consumes: JSON.parse(consumesKey) as string[],\n target: (batch) => !disposed && merge(batch),\n });\n // An unmount while the subscribe was pending ran the cleanup before this handle existed:\n // release it here, or the server keeps delivering to nobody.\n if (disposed) {\n handle[Symbol.dispose]();\n return;\n }\n subscription = handle;\n for (let after = 0; ; ) {\n const page = await itx.readEvents(after, 500);\n if (disposed) return;\n merge(page.events);\n if (page.atHead || page.scannedThroughOffset <= after) break;\n after = page.scannedThroughOffset;\n }\n setCaughtUp(true);\n })().catch((e: unknown) => !disposed && setError(e instanceof Error ? e.message : String(e)));\n return () => {\n disposed = true;\n subscription?.[Symbol.dispose]();\n };\n }, [itx, consumesKey]);\n const sorted = useMemo(() => [...events.values()].sort((a, b) => a.offset - b.offset), [events]);\n\n // ── the processors table ──\n // The table and the last failure remember WHICH itx they came from: a page that swaps contexts\n // (one route, another organization) shows an empty, not-yet-loaded table for the new one rather\n // than the old one's rows or error until the new read lands.\n const [table, setTable] = useState<{\n itx: IterateContextHandle;\n rows: IterateContextProcessorRow[];\n }>();\n const [failure, setFailure] = useState<{ itx: IterateContextHandle; message: string }>();\n const tableVersion = sorted.reduce(\n (last, event) =>\n event.type.startsWith(\"events.iterate.com/stream/subscription-\") ? event.offset : last,\n 0,\n );\n useEffect(() => {\n if (!itx) return;\n let disposed = false;\n Promise.resolve(itx.processors.list()).then(\n (list) => {\n if (disposed) return;\n setTable({ itx, rows: list });\n setFailure(undefined); // a read that recovered clears the last failure\n },\n (e: unknown) =>\n !disposed && setFailure({ itx, message: e instanceof Error ? e.message : String(e) }),\n );\n return () => {\n disposed = true;\n };\n }, [itx, tableVersion]);\n const currentTable = itx && table?.itx === itx ? table : undefined;\n\n // ── who is here ──\n const [census, setCensus] = useState<{ itx: IterateContextHandle; rpcStubs: string[] }>();\n const head = sorted.at(-1)?.offset ?? 0;\n useEffect(() => {\n if (!itx?.rpcStubs) return;\n let disposed = false;\n Promise.resolve(itx.rpcStubs.list()).then(\n (list) => !disposed && setCensus({ itx, rpcStubs: list }),\n () => undefined, // presence is nice to have; a failed census shows nothing\n );\n return () => {\n disposed = true;\n };\n }, [itx, head]);\n // keyed by its itx: a swapped context shows no census until its own lands\n const rpcStubs = itx && census?.itx === itx ? census.rpcStubs : [];\n const actors = useMemo(() => {\n const byActor = new Map<string, IterateContextPresence>();\n for (const event of sorted) {\n const principal = event.source?.principal;\n if (!principal) continue;\n byActor.set(principal.actor, {\n actor: principal.actor,\n email: principal.email,\n grant: event.source?.grant,\n lastSeenAt: event.createdAt,\n });\n }\n return [...byActor.values()].sort((a, b) => b.lastSeenAt.localeCompare(a.lastSeenAt));\n }, [sorted]);\n\n // ── named facets' live state ──\n // N subscriptions in ONE effect keyed by the name set — it changes at runtime as the processors\n // table loads (the default set is `core` plus the table's hosted facets) — since hooks cannot run\n // in a loop: client/live-state.ts's store reduces each, and this mirrors every change into React\n // state. The entries remember WHICH itx and name set they came from (as the table does), so a\n // swapped context or a changed set shows fresh connecting entries, never the last one's values.\n const liveStateKey = JSON.stringify(\n opts.liveState || [\n \"core\",\n ...(currentTable?.rows || []).flatMap((row) =>\n row.hostedFacet ? [row.hostedFacet.name] : [],\n ),\n ],\n );\n const [liveStates, setLiveStates] = useState<{\n itx: IterateContextHandle;\n key: string;\n entries: Record<string, LiveStateResult>;\n }>();\n useEffect(() => {\n if (!itx) return;\n const names = JSON.parse(liveStateKey) as string[];\n if (names.length === 0) return;\n let disposed = false;\n const unmounted = new AbortController(); // an unmount while a first seed is pending recalls that row\n const disposers: Array<() => void | Promise<void>> = [];\n const patch = (name: string, change: Partial<LiveStateResult>) =>\n setLiveStates((held) =>\n held && held.itx === itx && held.key === liveStateKey\n ? { ...held, entries: { ...held.entries, [name]: { ...held.entries[name], ...change } } }\n : held,\n );\n setLiveStates({\n itx,\n key: liveStateKey,\n entries: Object.fromEntries(names.map((name) => [name, LIVE_STATE_CONNECTING])),\n });\n for (const name of names) {\n connectLiveState<unknown>(itx, {\n key: name,\n door: async () =>\n // the engine's own `{ rev, state }` seed, as `liveSnapshot()` answers it\n (await itx.invoke(`itx.facets.get('${name}').liveSnapshot()`)) as LiveStateSeed<unknown>,\n signal: unmounted.signal,\n onResync: (result) => {\n if (disposed) return;\n if (result === \"healed\") patch(name, { status: \"live\", error: undefined });\n // the store keeps its last value; the next delta retries the heal\n else patch(name, { status: \"error\", error: result.message });\n },\n }).then(\n (connection) => {\n if (disposed) {\n void connection.dispose(); // unmounted while connecting — still tear the row down\n return;\n }\n disposers.push(connection.dispose);\n disposers.push(\n connection.store.subscribe(() =>\n patch(name, { value: connection.store.get(), rev: connection.store.rev() }),\n ),\n );\n patch(name, {\n value: connection.store.get(),\n rev: connection.store.rev(),\n status: \"live\",\n });\n },\n (e: unknown) => {\n if (disposed) return;\n patch(name, { status: \"error\", error: e instanceof Error ? e.message : String(e) });\n },\n );\n }\n return () => {\n disposed = true;\n unmounted.abort();\n for (const dispose of disposers) void dispose();\n };\n }, [itx, liveStateKey]);\n // One entry per name, always: a name the effect has not reached yet (the render right after the\n // set changed) reads as connecting rather than missing.\n const liveState = useMemo(() => {\n const names = JSON.parse(liveStateKey) as string[];\n const held =\n itx && liveStates?.itx === itx && liveStates.key === liveStateKey ? liveStates.entries : {};\n return Object.fromEntries(names.map((name) => [name, held[name] || LIVE_STATE_CONNECTING]));\n }, [itx, liveStateKey, liveStates]);\n\n return {\n events: sorted,\n caughtUp,\n error,\n processors: {\n rows: currentTable?.rows || [],\n loaded: Boolean(currentTable),\n error: itx && failure?.itx === itx ? failure.message : undefined,\n },\n presence: { actors, rpcStubs },\n liveState,\n };\n}\n"],"mappings":"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"}
@@ -1,5 +0,0 @@
1
- /** Project ingress strips public identity headers and stamps the verified caller.
2
- * This guard runs locally in the config worker, before it proxies an app. */
3
- export declare const auth: {
4
- require(request: Request): Response | null;
5
- };
@@ -1,112 +0,0 @@
1
- import { DurableObject, WorkerEntrypoint } from "cloudflare:workers";
2
- import type { IterateContextApi } from "../api.ts";
3
- import { type ScannedRange, type StreamProcessor, type StreamEvent, type StreamEventInput } from "../stream/processor.ts";
4
- import { auth } from "./auth.ts";
5
- export { auth };
6
- export { StreamProcessor, defineProcessorContract, jsonEqual, type ConsumedEvent, type EventCatalog, type EventDefinition, type EmittedEventInput, type EventInput, type ProcessorContract, type ProcessorState, type ProcessorStream, type ProcessEventArgs, type ReduceArgs, type ScannedRange, type StreamEvent, type StreamEventInput, } from "../stream/processor.ts";
7
- export { z } from "zod";
8
- export { newHttpBatchRpcSession, newWebSocketRpcSession, newWorkersRpcResponse } from "capnweb";
9
- export { applyPatch, diff, type PatchOp } from "../lib.ts";
10
- export { LiveState, type LiveStateSink } from "../stream/processor.ts";
11
- /** What the parent mints the class with — the whole identity. */
12
- export type StreamProcessorProps = {
13
- iterateContextName: string;
14
- name: string;
15
- };
16
- /** The itx scope as `env.ITX.get()` hands it over: a context's declared API (api.ts) — a capnweb stub
17
- * of os-next's `IterateContextRpcTarget`, which satisfies it. */
18
- export type ItxScope = IterateContextApi;
19
- /** What hands the scope over: the loopback entrypoint a loaded worker has as `env.ITX`, or the one a
20
- * class of the platform's own worker mints from `ctx.exports`. */
21
- export type ItxEntrypointService = {
22
- get(): ItxScope;
23
- };
24
- /** The least a host needs of its scope: the fixed-point log calls the engine makes. The platform's own
25
- * facets pass the Workers-RPC STUB of a context (every dotted step pipelined; a property there is a
26
- * promise), which no plain-promise interface can name — so the constraint is this, not `ItxScope`. */
27
- export type ProcessorScope = {
28
- append(...events: StreamEventInput[]): Promise<unknown>;
29
- readEvents(afterOffset?: number, limit?: number): Promise<unknown>;
30
- /** The engine's claim on the context's alarm (processor.ts rule 3): "come back by `at`", or null. */
31
- processors: {
32
- claim(name: string, at: number | null): Promise<unknown>;
33
- };
34
- /** Another context of the project — where `appendTo` lands — by its dotted surface (`.append`),
35
- * which the platform's handle and a loaded worker's alike answer. Through the table like every
36
- * other word here: a loaded processor's `cd` goes down only (the app wall), the platform's own
37
- * go anywhere. */
38
- cd(path: string): {
39
- append(...events: StreamEventInput[]): Promise<unknown>;
40
- };
41
- };
42
- /** THE SCOPE ACCESSOR a host hands its processor: one pipelined round trip on the context's itx,
43
- * released after (`StreamProcessorDurableObject.withItx`). A processor that needs an effect —
44
- * `itx.cfArtifacts.create(path)`, `itx.ai.run(…)` — takes this and nothing else, so a unit test
45
- * hands it a fake and the e2e lends one by rule on the context. */
46
- export type WithItx<Scope = ItxScope> = <T>(call: (itx: Scope) => T) => Promise<Awaited<T>>;
47
- export declare abstract class StreamProcessorDurableObject<State = unknown, Env extends {
48
- ITX?: ItxEntrypointService;
49
- } = {
50
- ITX: ItxEntrypointService;
51
- }, Scope extends ProcessorScope = ItxScope> extends DurableObject<Env, StreamProcessorProps> {
52
- #private;
53
- /** The processor this object hosts — `processor = new PresenceProcessor()` at the top of the subclass. */
54
- abstract readonly processor: StreamProcessor<State>;
55
- /** After a runtime field on the processor moved OUTSIDE a batch (an RPC method on this object);
56
- * inside `processEvent` the engine re-projects on its own. */
57
- protected publishLiveState(): void;
58
- /** THE push door: the context hands over each committed batch with its scanned-range proof. */
59
- processEventBatch(events: StreamEvent[], range: ScannedRange): Promise<void>;
60
- /** Catch up from the log (the read-your-writes entry after an eviction). */
61
- catchUpFromLog(): Promise<void>;
62
- /** THE REVIVE: the context's alarm pass calls it for a due claim — catch up, then run the
63
- * at-head pass, so an attempt the last incarnation was running is started again from state. */
64
- revive(): Promise<void>;
65
- /** Caught up through the log, then `{ offset, state }`. */
66
- snapshot(): Promise<{
67
- offset: number;
68
- state: State;
69
- }>;
70
- /** The live-state seed door: `{ rev, state: projectLiveState(reduced) }`. */
71
- liveSnapshot(): Promise<{
72
- rev: number;
73
- state: unknown;
74
- }>;
75
- /** The barrier: resolves once processed at least through `offset` (default timeout 10s). */
76
- waitUntilProcessed(input: {
77
- offset: number;
78
- timeoutMs?: number;
79
- }): Promise<void>;
80
- /** ONE pipelined round trip on the itx scope, then RELEASE it: `env.ITX.get()` and the call
81
- * pipelined on it PIN THE PARENT DO until GC (the "GC is too late" defect the DO's facet door
82
- * fixes in the other direction). Await the answer — plain data, the wire already copied it —
83
- * then dispose the call AND the get. Protected: a host with methods of its own (the workspace,
84
- * src/workspace/durable-object.ts) reaches its context the same way. */
85
- protected withItx<T>(call: (itx: Scope) => T): Promise<Awaited<T>>;
86
- }
87
- export type ConfigWorkerItx = ItxScope;
88
- export type ConfigEventArgs = {
89
- event: StreamEvent;
90
- range: ScannedRange;
91
- itx: ConfigWorkerItx;
92
- };
93
- export declare abstract class ConfigWorker<Env extends {
94
- ITX: ItxEntrypointService;
95
- } = {
96
- ITX: ItxEntrypointService;
97
- }> extends WorkerEntrypoint<Env> {
98
- /** At fetch entry: `const denied = this.auth.require(request); if (denied) return denied;` */
99
- protected readonly auth: {
100
- require(request: Request): Response | null;
101
- };
102
- /** Process an explicitly subscribed batch with this worker's context scope. */
103
- processEventBatch(events: StreamEvent[], range: ScannedRange): Promise<void>;
104
- /** THE AUTHOR HOOK — one event at a time, in offset order. Append reactions through the itx scope;
105
- * make them idempotent (a redelivery must be a no-op). Default: ignore the event. */
106
- processEvent(_args: ConfigEventArgs): void | Promise<void>;
107
- /** THE WEB ROOT — the Request a project host with no app label rode in on (`x-iterate-app` absent;
108
- * the project's configured ingress target). Route by hostname to `this.env.ITX.get().apps.<x>.fetch(request)`
109
- * or answer it here. Default: not found. */
110
- fetch(_request: Request): Response | Promise<Response>;
111
- }
112
- export { RunContract, RunRequested, RunSettled } from "../stream/run.ts";
package/dist/next/sdk.mjs DELETED
@@ -1,139 +0,0 @@
1
- import { applyPatch, diff, jsonEqual } from "./lib.mjs";
2
- import { LiveState, ProcessorEngine, ReduceCheckpointTable, StreamProcessor, defineProcessorContract } from "./stream/processor.mjs";
3
- import { ITX_PRINCIPAL_HEADER } from "./principal.mjs";
4
- import { RunContract, RunRequested, RunSettled } from "./stream/run.mjs";
5
- import { DurableObject, WorkerEntrypoint } from "cloudflare:workers";
6
- import { z, z as z$1 } from "zod";
7
- import { newHttpBatchRpcSession, newWebSocketRpcSession, newWorkersRpcResponse } from "capnweb";
8
- //#region src/next/sdk/auth.ts
9
- const Principal = z$1.object({
10
- actor: z$1.string().min(1),
11
- email: z$1.string().optional()
12
- });
13
- /** Project ingress strips public identity headers and stamps the verified caller.
14
- * This guard runs locally in the config worker, before it proxies an app. */
15
- const auth = { require(request) {
16
- const url = new URL(request.url);
17
- if (![
18
- "GET",
19
- "HEAD",
20
- "OPTIONS"
21
- ].includes(request.method)) {
22
- const origin = request.headers.get("origin");
23
- if (origin && origin !== url.origin) return new Response("Cross-site request refused", { status: 403 });
24
- }
25
- const principal = request.headers.get(ITX_PRINCIPAL_HEADER);
26
- if (principal) {
27
- Principal.parse(JSON.parse(principal));
28
- return null;
29
- }
30
- if (!["GET", "HEAD"].includes(request.method)) return new Response("Sign in first", { status: 401 });
31
- return new Response(null, {
32
- status: 302,
33
- headers: {
34
- Location: `/.auth/login?next=${encodeURIComponent(url.pathname + url.search)}`,
35
- "Cache-Control": "no-store"
36
- }
37
- });
38
- } };
39
- //#endregion
40
- //#region src/next/sdk/index.ts
41
- var StreamProcessorDurableObject = class extends DurableObject {
42
- /** After a runtime field on the processor moved OUTSIDE a batch (an RPC method on this object);
43
- * inside `processEvent` the engine re-projects on its own. */
44
- publishLiveState() {
45
- this.#engine.publishLiveState();
46
- }
47
- /** THE push door: the context hands over each committed batch with its scanned-range proof. */
48
- processEventBatch(events, range) {
49
- return this.#engine.processEventBatch(events, range);
50
- }
51
- /** Catch up from the log (the read-your-writes entry after an eviction). */
52
- catchUpFromLog() {
53
- return this.#engine.catchUpFromLog();
54
- }
55
- /** THE REVIVE: the context's alarm pass calls it for a due claim — catch up, then run the
56
- * at-head pass, so an attempt the last incarnation was running is started again from state. */
57
- revive() {
58
- return this.#engine.revive();
59
- }
60
- /** Caught up through the log, then `{ offset, state }`. */
61
- snapshot() {
62
- return this.#engine.snapshot();
63
- }
64
- /** The live-state seed door: `{ rev, state: projectLiveState(reduced) }`. */
65
- liveSnapshot() {
66
- return this.#engine.liveSnapshot();
67
- }
68
- /** The barrier: resolves once processed at least through `offset` (default timeout 10s). */
69
- waitUntilProcessed(input) {
70
- return this.#engine.waitUntilProcessed(input);
71
- }
72
- /** The loopback to this facet's context: a LOADED class gets it as `env.ITX` (the loader bakes the
73
- * stub in, worker-loader.ts); a class of THIS worker hosted through `ctx.exports` has the
74
- * worker's real env and mints the same stub itself from its props — `ctx.exports` is populated
75
- * inside a facet (__workers-tests__/facet-props.test.ts). */
76
- #itxEntrypoint() {
77
- return this.env.ITX ?? this.ctx.exports.ItxEntrypoint({ props: {
78
- iterateContextName: this.ctx.props.iterateContextName,
79
- platform: true
80
- } });
81
- }
82
- #engineBuiltOnFirstUse;
83
- get #engine() {
84
- return this.#engineBuiltOnFirstUse ??= new ProcessorEngine(this.processor, {
85
- stream: {
86
- append: (...events) => this.withItx((itx) => itx.append(...events)),
87
- appendTo: (path, ...events) => this.withItx((itx) => itx.cd(path).append(...events)),
88
- read: (after, limit) => this.withItx((itx) => itx.readEvents(after, limit)),
89
- claim: (at) => this.withItx((itx) => itx.processors.claim(this.ctx.props.name, at))
90
- },
91
- storage: new ReduceCheckpointTable(this.ctx.storage.sql)
92
- });
93
- }
94
- /** ONE pipelined round trip on the itx scope, then RELEASE it: `env.ITX.get()` and the call
95
- * pipelined on it PIN THE PARENT DO until GC (the "GC is too late" defect the DO's facet door
96
- * fixes in the other direction). Await the answer — plain data, the wire already copied it —
97
- * then dispose the call AND the get. Protected: a host with methods of its own (the workspace,
98
- * src/workspace/durable-object.ts) reaches its context the same way. */
99
- async withItx(call) {
100
- const itx = this.#itxEntrypoint().get();
101
- const result = call(itx);
102
- try {
103
- return await result;
104
- } finally {
105
- result[Symbol.dispose]?.();
106
- itx[Symbol.dispose]?.();
107
- }
108
- }
109
- };
110
- var ConfigWorker = class extends WorkerEntrypoint {
111
- /** At fetch entry: `const denied = this.auth.require(request); if (denied) return denied;` */
112
- auth = auth;
113
- /** Process an explicitly subscribed batch with this worker's context scope. */
114
- async processEventBatch(events, range) {
115
- const itx = this.env.ITX.get();
116
- try {
117
- for (const event of events) await this.processEvent({
118
- event,
119
- range,
120
- itx
121
- });
122
- } finally {
123
- itx[Symbol.dispose]?.();
124
- }
125
- }
126
- /** THE AUTHOR HOOK — one event at a time, in offset order. Append reactions through the itx scope;
127
- * make them idempotent (a redelivery must be a no-op). Default: ignore the event. */
128
- processEvent(_args) {}
129
- /** THE WEB ROOT — the Request a project host with no app label rode in on (`x-iterate-app` absent;
130
- * the project's configured ingress target). Route by hostname to `this.env.ITX.get().apps.<x>.fetch(request)`
131
- * or answer it here. Default: not found. */
132
- fetch(_request) {
133
- return new Response("Not found\n", { status: 404 });
134
- }
135
- };
136
- //#endregion
137
- export { ConfigWorker, LiveState, RunContract, RunRequested, RunSettled, StreamProcessor, StreamProcessorDurableObject, applyPatch, auth, defineProcessorContract, diff, jsonEqual, newHttpBatchRpcSession, newWebSocketRpcSession, newWorkersRpcResponse, z };
138
-
139
- //# sourceMappingURL=sdk.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sdk.mjs","names":["z","#engine","#engineBuiltOnFirstUse","#itxEntrypoint"],"sources":["../../src/next/sdk/auth.ts","../../src/next/sdk/index.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { ITX_PRINCIPAL_HEADER } from \"../principal.ts\";\n\nconst Principal = z.object({ actor: z.string().min(1), email: z.string().optional() });\n\n/** Project ingress strips public identity headers and stamps the verified caller.\n * This guard runs locally in the config worker, before it proxies an app. */\nexport const auth = {\n require(request: Request): Response | null {\n const url = new URL(request.url);\n if (![\"GET\", \"HEAD\", \"OPTIONS\"].includes(request.method)) {\n const origin = request.headers.get(\"origin\");\n if (origin && origin !== url.origin)\n return new Response(\"Cross-site request refused\", { status: 403 });\n }\n const principal = request.headers.get(ITX_PRINCIPAL_HEADER);\n if (principal) {\n Principal.parse(JSON.parse(principal)); // Platform-owned stamp; malformed means a defect.\n return null;\n }\n if (![\"GET\", \"HEAD\"].includes(request.method))\n return new Response(\"Sign in first\", { status: 401 });\n return new Response(null, {\n status: 302,\n headers: {\n Location: `/.auth/login?next=${encodeURIComponent(url.pathname + url.search)}`,\n \"Cache-Control\": \"no-store\",\n },\n });\n },\n};\n","// sdk/index.ts — THE userspace SDK surface, bundled (zod included — the owner's call) into every\n// loaded isolate as `processor.js` (os-next's scripts/vite-plugin-processor-sdk.ts bundles it):\n//\n// import { StreamProcessor, StreamProcessorDurableObject, defineProcessorContract, z } from \"./processor.js\";\n//\n// The two workerd HOSTS live here too (this file imports cloudflare:workers; the node lane never imports it):\n// StreamProcessorDurableObject — the `DurableObject` shell that hosts ONE `StreamProcessor` as a facet\n// ConfigWorker — the stateless `WorkerEntrypoint` a project's one event handler extends\n\nimport { DurableObject, WorkerEntrypoint } from \"cloudflare:workers\";\nimport type { IterateContextApi, StreamPage } from \"../api.ts\";\nimport {\n ProcessorEngine,\n type ScannedRange,\n type StreamProcessor,\n ReduceCheckpointTable,\n type StreamEvent,\n type StreamEventInput,\n} from \"../stream/processor.ts\";\nimport { auth } from \"./auth.ts\";\nexport { auth };\nexport {\n StreamProcessor,\n defineProcessorContract,\n jsonEqual,\n type ConsumedEvent,\n type EventCatalog,\n type EventDefinition,\n type EmittedEventInput,\n type EventInput,\n type ProcessorContract,\n type ProcessorState,\n type ProcessorStream,\n type ProcessEventArgs,\n type ReduceArgs,\n type ScannedRange,\n type StreamEvent,\n type StreamEventInput,\n} from \"../stream/processor.ts\";\nexport { z } from \"zod\";\n// capnweb's CLIENT constructors, so userspace can dial a remote capnweb API from inside its isolate\n// through the context's own egress, and `newWorkersRpcResponse`, the SERVER half, so a loaded worker\n// can serve a capnweb API over its `fetch`. The HTTP batch is exported ON PURPOSE beside the\n// WebSocket session: a stateless entrypoint answering one method with one remote call has no session\n// to hold across calls, and a one-shot POST is the honest shape (the lint rule targets long-lived workers).\n// eslint-disable-next-line iterate/no-capnweb-http-batch -- userspace one-shot remote calls; see above\nexport { newHttpBatchRpcSession, newWebSocketRpcSession, newWorkersRpcResponse } from \"capnweb\";\nexport { applyPatch, diff, type PatchOp } from \"../lib.ts\";\nexport { LiveState, type LiveStateSink } from \"../stream/processor.ts\";\n// LIVE STATE for a mini-app DO that is NOT a processor (a processor's base owns one internally):\n// `new LiveState({ append: (e) => env.ITX.get().append(e) }, \"chat\", {…})` — a field initializer\n// cannot await — then `set` to mutate and `snapshot()` as the client seed door (stream/processor.ts).\n// ── StreamProcessorDurableObject ── THE SDK HOST: the `DurableObject` shell that hosts ONE\n// `StreamProcessor` as a facet of its context. An author writes the pure processor and its host,\n// one line long:\n//\n// export class PresenceDurableObject extends StreamProcessorDurableObject {\n// processor = new PresenceProcessor();\n// }\n//\n// hosted through the ordinary `itx.facets.get('presence', { source, className: 'PresenceDurableObject' })`\n// — a processor is a named facet that additionally gets pushed every commit. `processor` is a FIELD\n// so it can take what its effects need from this object (`new Notifier(this.env.ITX)`), and so the\n// same class is constructed bare in a test.\n//\n// IDENTITY is `ctx.props` — `{ iterateContextName, name }`, minted by the parent, the only party\n// that knows it (pinned in __workers-tests__/facet-props.test.ts). THE STREAM is the itx scope\n// behind `env.ITX.get()` (iterate-context.ts `ItxEntrypoint`); the engine's `append`/`read` ride it like any other\n// dotted call.\n//\n// NEVER define alarm(): facets have none (workerd#6810 — the runtime answers \"Facets currently\n// cannot set alarms.\"); a timer, when one is needed, is a scheduled append on the context. The\n// engine's own recovery is a CLAIM on the context's alarm (processor.ts, rule 3): while a\n// `runInBackground` attempt is in flight the context owes this facet a `revive()`, so a host that\n// dies mid-attempt is re-materialized and runs its at-head pass again\n// (__workers-tests__/agent-revive.test.ts: an LLM call survives its context's death).\n\n/** What the parent mints the class with — the whole identity. */\nexport type StreamProcessorProps = { iterateContextName: string; name: string };\n\n/** The itx scope as `env.ITX.get()` hands it over: a context's declared API (api.ts) — a capnweb stub\n * of os-next's `IterateContextRpcTarget`, which satisfies it. */\nexport type ItxScope = IterateContextApi;\n/** What hands the scope over: the loopback entrypoint a loaded worker has as `env.ITX`, or the one a\n * class of the platform's own worker mints from `ctx.exports`. */\nexport type ItxEntrypointService = { get(): ItxScope };\n/** The least a host needs of its scope: the fixed-point log calls the engine makes. The platform's own\n * facets pass the Workers-RPC STUB of a context (every dotted step pipelined; a property there is a\n * promise), which no plain-promise interface can name — so the constraint is this, not `ItxScope`. */\nexport type ProcessorScope = {\n append(...events: StreamEventInput[]): Promise<unknown>;\n readEvents(afterOffset?: number, limit?: number): Promise<unknown>;\n /** The engine's claim on the context's alarm (processor.ts rule 3): \"come back by `at`\", or null. */\n processors: { claim(name: string, at: number | null): Promise<unknown> };\n /** Another context of the project — where `appendTo` lands — by its dotted surface (`.append`),\n * which the platform's handle and a loaded worker's alike answer. Through the table like every\n * other word here: a loaded processor's `cd` goes down only (the app wall), the platform's own\n * go anywhere. */\n cd(path: string): { append(...events: StreamEventInput[]): Promise<unknown> };\n};\n\n/** THE SCOPE ACCESSOR a host hands its processor: one pipelined round trip on the context's itx,\n * released after (`StreamProcessorDurableObject.withItx`). A processor that needs an effect —\n * `itx.cfArtifacts.create(path)`, `itx.ai.run(…)` — takes this and nothing else, so a unit test\n * hands it a fake and the e2e lends one by rule on the context. */\nexport type WithItx<Scope = ItxScope> = <T>(call: (itx: Scope) => T) => Promise<Awaited<T>>;\n\nexport abstract class StreamProcessorDurableObject<\n State = unknown,\n Env extends { ITX?: ItxEntrypointService } = { ITX: ItxEntrypointService },\n Scope extends ProcessorScope = ItxScope,\n> extends DurableObject<Env, StreamProcessorProps> {\n /** The processor this object hosts — `processor = new PresenceProcessor()` at the top of the subclass. */\n abstract readonly processor: StreamProcessor<State>;\n\n // ── what an author reaches (the itx scope is `this.env.ITX.get()`, typed; identity is `this.ctx.props`) ──\n\n /** After a runtime field on the processor moved OUTSIDE a batch (an RPC method on this object);\n * inside `processEvent` the engine re-projects on its own. */\n protected publishLiveState(): void {\n this.#engine.publishLiveState();\n }\n\n // ── the doors the delivery loop and `itx.facets.get(name)` reach ──\n\n /** THE push door: the context hands over each committed batch with its scanned-range proof. */\n processEventBatch(events: StreamEvent[], range: ScannedRange): Promise<void> {\n return this.#engine.processEventBatch(events, range);\n }\n /** Catch up from the log (the read-your-writes entry after an eviction). */\n catchUpFromLog(): Promise<void> {\n return this.#engine.catchUpFromLog();\n }\n /** THE REVIVE: the context's alarm pass calls it for a due claim — catch up, then run the\n * at-head pass, so an attempt the last incarnation was running is started again from state. */\n revive(): Promise<void> {\n return this.#engine.revive();\n }\n /** Caught up through the log, then `{ offset, state }`. */\n snapshot(): Promise<{ offset: number; state: State }> {\n return this.#engine.snapshot();\n }\n /** The live-state seed door: `{ rev, state: projectLiveState(reduced) }`. */\n liveSnapshot(): Promise<{ rev: number; state: unknown }> {\n return this.#engine.liveSnapshot();\n }\n /** The barrier: resolves once processed at least through `offset` (default timeout 10s). */\n waitUntilProcessed(input: { offset: number; timeoutMs?: number }): Promise<void> {\n return this.#engine.waitUntilProcessed(input);\n }\n\n /** The loopback to this facet's context: a LOADED class gets it as `env.ITX` (the loader bakes the\n * stub in, worker-loader.ts); a class of THIS worker hosted through `ctx.exports` has the\n * worker's real env and mints the same stub itself from its props — `ctx.exports` is populated\n * inside a facet (__workers-tests__/facet-props.test.ts). */\n #itxEntrypoint(): { get(): Scope } {\n return (this.env.ITX ??\n (\n this.ctx.exports as unknown as {\n ItxEntrypoint: (options: { props: object }) => ItxEntrypointService;\n }\n ).ItxEntrypoint({\n // PLATFORM: this worker's own class, minted from its own exports — the full handle, the fixed\n // point spellable, `cd` free to go up. A LOADED class never reaches this branch (it has\n // `env.ITX`, baked in by the loader, and its own module's exports).\n props: { iterateContextName: this.ctx.props.iterateContextName, platform: true },\n })) as unknown as {\n get(): Scope;\n };\n }\n // ── the engine: one ProcessorEngine over `processor` and this object's storage, built on first use —\n // `processor` is a subclass field, which does not exist yet while this base class constructs. ──\n #engineBuiltOnFirstUse?: ProcessorEngine<State>;\n get #engine(): ProcessorEngine<State> {\n return (this.#engineBuiltOnFirstUse ??= new ProcessorEngine(this.processor, {\n // The engine's own emits, catch-up and gap repair are the CONTEXT ROOTS `append`, `readEvents`,\n // `processors.claim` — implicit in every context (itx-expression-rewriting.ts rule 3), so they\n // resolve to this log with no row and no hop; a row at `itx.append` is the OWNER's deliberate\n // wall (a jailed processor halts visibly), never a loaded worker's — the fixed point is not a\n // loaded worker's word. `appendTo` is `cd` through the same table: a first-party saga (the\n // platform's mint) reaches `/`; a loaded processor's `cd` goes down only.\n stream: {\n // A stub scope's answers are pipelined shapes by type and plain data on the wire (the\n // engine awaits them): the engine's own types, asserted.\n append: (...events) =>\n this.withItx((itx) => itx.append(...events)) as Promise<StreamEvent[]>,\n appendTo: (path, ...events) =>\n this.withItx((itx) => itx.cd(path).append(...events)) as Promise<StreamEvent[]>,\n read: (after, limit) =>\n this.withItx((itx) => itx.readEvents(after, limit)) as Promise<StreamPage>,\n claim: (at) => this.withItx((itx) => itx.processors.claim(this.ctx.props.name, at)),\n },\n storage: new ReduceCheckpointTable(this.ctx.storage.sql),\n }));\n }\n\n /** ONE pipelined round trip on the itx scope, then RELEASE it: `env.ITX.get()` and the call\n * pipelined on it PIN THE PARENT DO until GC (the \"GC is too late\" defect the DO's facet door\n * fixes in the other direction). Await the answer — plain data, the wire already copied it —\n * then dispose the call AND the get. Protected: a host with methods of its own (the workspace,\n * src/workspace/durable-object.ts) reaches its context the same way. */\n protected async withItx<T>(call: (itx: Scope) => T): Promise<Awaited<T>> {\n const itx = this.#itxEntrypoint().get();\n const result = call(itx);\n try {\n return await result;\n } finally {\n (result as unknown as Disposable)[Symbol.dispose]?.();\n (itx as unknown as Disposable)[Symbol.dispose]?.();\n }\n }\n}\n\n// ConfigWorker is a stateless event handler loaded with an explicit workers.get spec.\n// Subscribe its processEventBatch method explicitly; fetch routing is configured separately.\nexport type ConfigWorkerItx = ItxScope;\nexport type ConfigEventArgs = { event: StreamEvent; range: ScannedRange; itx: ConfigWorkerItx };\n\nexport abstract class ConfigWorker<\n Env extends { ITX: ItxEntrypointService } = { ITX: ItxEntrypointService },\n> extends WorkerEntrypoint<Env> {\n /** At fetch entry: `const denied = this.auth.require(request); if (denied) return denied;` */\n protected readonly auth = auth;\n /** Process an explicitly subscribed batch with this worker's context scope. */\n async processEventBatch(events: StreamEvent[], range: ScannedRange): Promise<void> {\n const itx = this.env.ITX.get();\n try {\n for (const event of events) {\n await this.processEvent({ event, range, itx });\n }\n } finally {\n (itx as unknown as Disposable)[Symbol.dispose]?.();\n }\n }\n\n /** THE AUTHOR HOOK — one event at a time, in offset order. Append reactions through the itx scope;\n * make them idempotent (a redelivery must be a no-op). Default: ignore the event. */\n processEvent(_args: ConfigEventArgs): void | Promise<void> {}\n\n /** THE WEB ROOT — the Request a project host with no app label rode in on (`x-iterate-app` absent;\n * the project's configured ingress target). Route by hostname to `this.env.ITX.get().apps.<x>.fetch(request)`\n * or answer it here. Default: not found. */\n override fetch(_request: Request): Response | Promise<Response> {\n return new Response(\"Not found\\n\", { status: 404 });\n }\n}\n\nexport { RunContract, RunRequested, RunSettled } from \"../stream/run.ts\";\n"],"mappings":"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"}