@swirls/looms 0.0.2 → 0.2.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 (375) hide show
  1. package/CHANGELOG.md +24 -2
  2. package/dist/actor/cell.d.ts +9 -4
  3. package/dist/actor/cell.js +7 -11
  4. package/dist/actor/host.d.ts +7 -3
  5. package/dist/actor/host.js +57 -33
  6. package/dist/actor/index.d.ts +1 -1
  7. package/dist/actor/index.js +1 -1
  8. package/dist/actor/routing.d.ts +12 -1
  9. package/dist/actor/routing.js +57 -51
  10. package/dist/agent/agent-session.d.ts +5 -0
  11. package/dist/agent/agent-session.js +270 -0
  12. package/dist/agent/debugger/agent-chat.d.ts +2 -0
  13. package/dist/agent/debugger/agent-chat.js +176 -0
  14. package/dist/agent/debugger/index.d.ts +5 -0
  15. package/dist/agent/debugger/index.js +19 -0
  16. package/dist/agent/debugger/summarize.d.ts +2 -0
  17. package/dist/agent/debugger/summarize.js +51 -0
  18. package/dist/agent/debugger/token-usage.d.ts +3 -0
  19. package/dist/agent/debugger/token-usage.js +11 -0
  20. package/dist/agent/definitions-store.d.ts +2 -3
  21. package/dist/agent/definitions-store.js +2 -4
  22. package/dist/agent/definitions.d.ts +47 -29
  23. package/dist/agent/definitions.js +39 -12
  24. package/dist/agent/effects.d.ts +359 -2
  25. package/dist/agent/effects.js +60 -34
  26. package/dist/agent/events.d.ts +175 -19
  27. package/dist/agent/events.js +45 -10
  28. package/dist/agent/index.d.ts +1 -0
  29. package/dist/agent/index.js +1 -0
  30. package/dist/agent/llm.d.ts +11 -2
  31. package/dist/agent/llm.js +13 -3
  32. package/dist/agent/module.d.ts +812 -78
  33. package/dist/agent/module.js +16 -4
  34. package/dist/agent/scope.d.ts +174 -18
  35. package/dist/agent/signals.d.ts +26 -1
  36. package/dist/agent/signals.js +79 -0
  37. package/dist/agent/threads.d.ts +3 -33
  38. package/dist/agent/threads.js +20 -25
  39. package/dist/agent/tool-schema.d.ts +1 -17
  40. package/dist/agent/tool-schema.js +3 -43
  41. package/dist/agent/types.d.ts +49 -4
  42. package/dist/agent/types.js +5 -5
  43. package/dist/ai-vercel/index.d.ts +1 -0
  44. package/dist/ai-vercel/index.js +1 -0
  45. package/dist/ai-vercel/messages.js +22 -7
  46. package/dist/ai-vercel/vercel-evaluator.d.ts +36 -0
  47. package/dist/ai-vercel/vercel-evaluator.js +73 -0
  48. package/dist/ai-vercel/vercel-llm.js +64 -44
  49. package/dist/approval/debugger/approvals-view.d.ts +3 -0
  50. package/dist/approval/debugger/approvals-view.js +16 -0
  51. package/dist/approval/debugger/index.d.ts +4 -0
  52. package/dist/approval/debugger/index.js +11 -0
  53. package/dist/approval/debugger/summarize.d.ts +2 -0
  54. package/dist/approval/debugger/summarize.js +17 -0
  55. package/dist/approval/effects.d.ts +89 -0
  56. package/dist/approval/module.d.ts +179 -29
  57. package/dist/approval/projections.d.ts +0 -14
  58. package/dist/approval/projections.js +20 -3
  59. package/dist/approval/signals.d.ts +2 -0
  60. package/dist/approval/signals.js +18 -2
  61. package/dist/cli/cli.js +2 -0
  62. package/dist/cli/commands.js +12 -16
  63. package/dist/client/client.d.ts +1213 -18
  64. package/dist/client/client.js +120 -120
  65. package/dist/client/index.d.ts +1 -1
  66. package/dist/client/index.js +1 -1
  67. package/dist/client/sse.d.ts +13 -0
  68. package/dist/client/sse.js +38 -44
  69. package/dist/cloudflare/alarm-scheduler.d.ts +12 -2
  70. package/dist/cloudflare/alarm-scheduler.js +34 -7
  71. package/dist/cloudflare/durable-object.d.ts +10 -2
  72. package/dist/cloudflare/durable-object.js +59 -28
  73. package/dist/cloudflare/event-store.d.ts +7 -2
  74. package/dist/cloudflare/event-store.js +8 -7
  75. package/dist/cloudflare/index.d.ts +2 -2
  76. package/dist/cloudflare/index.js +2 -2
  77. package/dist/cloudflare/router.d.ts +1 -1
  78. package/dist/cloudflare/router.js +15 -16
  79. package/dist/core/catalog.d.ts +3 -3
  80. package/dist/core/catalog.js +2 -5
  81. package/dist/core/coalesce.js +5 -3
  82. package/dist/core/compose.d.ts +2 -1
  83. package/dist/core/compose.js +19 -2
  84. package/dist/core/debugger-host.d.ts +16 -0
  85. package/dist/core/debugger-host.js +1 -0
  86. package/dist/core/definition-store.d.ts +11 -0
  87. package/dist/core/definition-store.js +6 -0
  88. package/dist/core/effects.d.ts +127 -15
  89. package/dist/core/effects.js +89 -17
  90. package/dist/core/envelope.d.ts +27 -7
  91. package/dist/core/envelope.js +20 -6
  92. package/dist/core/event-codec.d.ts +4 -2
  93. package/dist/core/event-codec.js +10 -5
  94. package/dist/core/event-index.js +1 -3
  95. package/dist/core/fold.d.ts +3 -0
  96. package/dist/core/fold.js +171 -24
  97. package/dist/core/ids.d.ts +5 -0
  98. package/dist/core/ids.js +35 -2
  99. package/dist/core/index.d.ts +7 -1
  100. package/dist/core/index.js +6 -1
  101. package/dist/core/kind.d.ts +22 -0
  102. package/dist/core/kind.js +17 -0
  103. package/dist/core/limits.d.ts +34 -0
  104. package/dist/core/limits.js +46 -0
  105. package/dist/core/match.d.ts +1 -0
  106. package/dist/core/match.js +10 -1
  107. package/dist/core/module-scope.d.ts +22 -20
  108. package/dist/core/module-scope.js +38 -28
  109. package/dist/core/module.d.ts +18 -5
  110. package/dist/core/module.js +4 -0
  111. package/dist/core/projection.d.ts +13 -13
  112. package/dist/core/projection.js +9 -5
  113. package/dist/core/protocol.d.ts +68 -11
  114. package/dist/core/protocol.js +82 -11
  115. package/dist/core/published.d.ts +43 -0
  116. package/dist/core/published.js +44 -0
  117. package/dist/core/replay.d.ts +312 -3
  118. package/dist/core/replay.js +11 -1
  119. package/dist/core/schema.d.ts +11 -20
  120. package/dist/core/schema.js +63 -80
  121. package/dist/core/serialize.js +2 -4
  122. package/dist/core/snapshot-store.d.ts +6 -1
  123. package/dist/core/snapshot-store.js +15 -2
  124. package/dist/core/snapshots.d.ts +5 -1
  125. package/dist/core/snapshots.js +10 -15
  126. package/dist/core/sqlite-store.js +95 -11
  127. package/dist/core/state.d.ts +142 -4
  128. package/dist/core/state.js +78 -0
  129. package/dist/core/store.d.ts +38 -3
  130. package/dist/core/store.js +77 -22
  131. package/dist/core/thread.d.ts +10 -9
  132. package/dist/core/types.d.ts +5 -8
  133. package/dist/core/types.js +28 -24
  134. package/dist/core/validate-event.js +14 -4
  135. package/dist/core/worker-callback.d.ts +17 -0
  136. package/dist/core/worker-callback.js +1 -0
  137. package/dist/debugger/app/assets/index-B3Ah8WAE.css +2 -0
  138. package/dist/debugger/app/assets/index-Qb371t_O.js +22 -0
  139. package/dist/debugger/app/assets/rolldown-runtime-CbXtAM7H.js +1 -0
  140. package/dist/debugger/app/assets/routes-CjtEHgPQ.js +47 -0
  141. package/dist/debugger/app/index.html +14 -0
  142. package/dist/debugger/components/composer.d.ts +17 -0
  143. package/dist/debugger/components/composer.js +25 -0
  144. package/dist/debugger/components/debugger-split.d.ts +2 -1
  145. package/dist/debugger/components/debugger-split.js +2 -2
  146. package/dist/debugger/components/event-inspector.d.ts +2 -1
  147. package/dist/debugger/components/event-inspector.js +25 -16
  148. package/dist/debugger/components/event-stream.d.ts +2 -1
  149. package/dist/debugger/components/event-stream.js +14 -74
  150. package/dist/debugger/components/follow-list.d.ts +19 -0
  151. package/dist/debugger/components/follow-list.js +96 -0
  152. package/dist/debugger/components/json-tree.d.ts +11 -0
  153. package/dist/debugger/components/json-tree.js +120 -0
  154. package/dist/debugger/components/json-view.js +2 -2
  155. package/dist/debugger/components/run-tree.js +53 -20
  156. package/dist/debugger/components/status-dot.d.ts +4 -0
  157. package/dist/debugger/components/status-dot.js +6 -2
  158. package/dist/debugger/context.d.ts +14 -0
  159. package/dist/debugger/context.js +17 -0
  160. package/dist/debugger/contracts.d.ts +7 -4
  161. package/dist/debugger/events/builtin-families.d.ts +3 -0
  162. package/dist/debugger/events/builtin-families.js +9 -0
  163. package/dist/debugger/events/catalog.d.ts +3 -4
  164. package/dist/debugger/events/catalog.js +27 -17
  165. package/dist/debugger/events/protocol-summarize.d.ts +1 -2
  166. package/dist/debugger/events/protocol-summarize.js +42 -24
  167. package/dist/debugger/index.d.ts +20 -6
  168. package/dist/debugger/index.js +19 -5
  169. package/dist/debugger/lib/cn.d.ts +1 -0
  170. package/dist/debugger/lib/cn.js +3 -0
  171. package/dist/debugger/lib/event-meta.d.ts +2 -3
  172. package/dist/debugger/lib/event-meta.js +6 -12
  173. package/dist/debugger/lib/json.d.ts +25 -0
  174. package/dist/debugger/lib/json.js +87 -0
  175. package/dist/debugger/lib/payload.d.ts +7 -0
  176. package/dist/debugger/lib/payload.js +11 -0
  177. package/dist/debugger/lib/time.d.ts +3 -0
  178. package/dist/debugger/lib/time.js +24 -0
  179. package/dist/debugger/plugin.d.ts +36 -0
  180. package/dist/debugger/plugin.js +14 -0
  181. package/dist/debugger/server/index.d.ts +14 -0
  182. package/dist/debugger/server/index.js +114 -0
  183. package/dist/debugger/shell/debugger-shell.d.ts +15 -0
  184. package/dist/debugger/shell/debugger-shell.js +43 -0
  185. package/dist/debugger/shell/definition-key.d.ts +3 -0
  186. package/dist/debugger/shell/definition-key.js +4 -0
  187. package/dist/debugger/shell/events-pane.d.ts +12 -0
  188. package/dist/debugger/shell/events-pane.js +25 -0
  189. package/dist/debugger/shell/projections-panel.d.ts +4 -0
  190. package/dist/debugger/shell/projections-panel.js +59 -0
  191. package/dist/debugger/shell/run-controls.d.ts +4 -0
  192. package/dist/debugger/shell/run-controls.js +72 -0
  193. package/dist/debugger/shell/run-search.d.ts +6 -0
  194. package/dist/debugger/shell/run-search.js +102 -0
  195. package/dist/debugger/shell/run-type-menu.d.ts +6 -0
  196. package/dist/debugger/shell/run-type-menu.js +11 -0
  197. package/dist/debugger/shell/schema-form.d.ts +31 -0
  198. package/dist/debugger/shell/schema-form.js +117 -0
  199. package/dist/debugger/shell/start-form.d.ts +3 -0
  200. package/dist/debugger/shell/start-form.js +75 -0
  201. package/dist/debugger/shell/use-start-run.d.ts +14 -0
  202. package/dist/debugger/shell/use-start-run.js +32 -0
  203. package/dist/debugger/shell/workspace.d.ts +6 -0
  204. package/dist/debugger/shell/workspace.js +15 -0
  205. package/dist/debugger/styles.css +111 -27
  206. package/dist/debugger/ui/button.d.ts +24 -0
  207. package/dist/debugger/ui/button.js +24 -0
  208. package/dist/debugger/ui/command.d.ts +9 -0
  209. package/dist/debugger/ui/command.js +24 -0
  210. package/dist/debugger/ui/dropdown-menu.d.ts +13 -0
  211. package/dist/debugger/ui/dropdown-menu.js +37 -0
  212. package/dist/debugger/ui/panel.d.ts +14 -0
  213. package/dist/debugger/ui/panel.js +16 -0
  214. package/dist/debugger/ui/popover.d.ts +7 -0
  215. package/dist/debugger/ui/popover.js +17 -0
  216. package/dist/debugger/ui/resizable.d.ts +7 -0
  217. package/dist/debugger/ui/resizable.js +14 -0
  218. package/dist/debugger/ui/scroll-area.d.ts +2 -1
  219. package/dist/debugger/ui/scroll-area.js +5 -3
  220. package/dist/debugger/ui/select.d.ts +10 -0
  221. package/dist/debugger/ui/select.js +21 -0
  222. package/dist/debugger/ui/tabs.js +2 -2
  223. package/dist/debugger/ui/textarea.d.ts +3 -0
  224. package/dist/debugger/ui/textarea.js +7 -0
  225. package/dist/evaluator/definitions-store.d.ts +11 -0
  226. package/dist/evaluator/definitions-store.js +8 -0
  227. package/dist/evaluator/definitions.d.ts +18 -0
  228. package/dist/evaluator/definitions.js +4 -0
  229. package/dist/evaluator/effects.d.ts +226 -0
  230. package/dist/evaluator/effects.js +132 -0
  231. package/dist/evaluator/evaluator.d.ts +24 -0
  232. package/dist/evaluator/evaluator.js +67 -0
  233. package/dist/evaluator/events.d.ts +59 -0
  234. package/dist/evaluator/events.js +21 -0
  235. package/dist/evaluator/index.d.ts +11 -0
  236. package/dist/evaluator/index.js +11 -0
  237. package/dist/evaluator/module.d.ts +606 -0
  238. package/dist/evaluator/module.js +25 -0
  239. package/dist/evaluator/projections.d.ts +50 -0
  240. package/dist/evaluator/projections.js +20 -0
  241. package/dist/evaluator/scope.d.ts +59 -0
  242. package/dist/evaluator/scope.js +7 -0
  243. package/dist/evaluator/signals.d.ts +3 -0
  244. package/dist/evaluator/signals.js +17 -0
  245. package/dist/evaluator/threads.d.ts +62 -0
  246. package/dist/evaluator/threads.js +117 -0
  247. package/dist/evaluator/types.d.ts +125 -0
  248. package/dist/evaluator/types.js +51 -0
  249. package/dist/projectors/delivery.d.ts +83 -0
  250. package/dist/projectors/delivery.js +182 -0
  251. package/dist/projectors/index-model.d.ts +1 -1
  252. package/dist/projectors/index-model.js +4 -6
  253. package/dist/projectors/index.d.ts +2 -0
  254. package/dist/projectors/index.js +2 -0
  255. package/dist/projectors/memory.d.ts +1 -1
  256. package/dist/projectors/memory.js +10 -8
  257. package/dist/projectors/postgres.js +44 -58
  258. package/dist/projectors/projector.d.ts +17 -0
  259. package/dist/projectors/projector.js +26 -1
  260. package/dist/projectors/sqlite-delivery.d.ts +4 -0
  261. package/dist/projectors/sqlite-delivery.js +147 -0
  262. package/dist/projectors/sqlite.js +23 -23
  263. package/dist/projectors/with-projectors.d.ts +7 -2
  264. package/dist/projectors/with-projectors.js +27 -28
  265. package/dist/react/notify.js +11 -8
  266. package/dist/react/react.d.ts +2 -3
  267. package/dist/react/react.js +14 -30
  268. package/dist/react/sse.d.ts +1 -1
  269. package/dist/react/sse.js +1 -1
  270. package/dist/react/store.d.ts +3 -3
  271. package/dist/react/store.js +109 -119
  272. package/dist/runtime/cancellation.d.ts +16 -0
  273. package/dist/runtime/cancellation.js +129 -0
  274. package/dist/runtime/deadline-transition-planner.d.ts +31 -0
  275. package/dist/runtime/deadline-transition-planner.js +139 -0
  276. package/dist/runtime/dispatch-effect.d.ts +3 -2
  277. package/dist/runtime/dispatch-effect.js +7 -13
  278. package/dist/runtime/errors.d.ts +51 -0
  279. package/dist/runtime/errors.js +39 -0
  280. package/dist/runtime/{runtime-helpers.d.ts → helpers.d.ts} +24 -4
  281. package/dist/runtime/{runtime-helpers.js → helpers.js} +56 -19
  282. package/dist/runtime/index.d.ts +3 -3
  283. package/dist/runtime/index.js +3 -3
  284. package/dist/runtime/ingress.d.ts +15 -0
  285. package/dist/runtime/ingress.js +42 -0
  286. package/dist/runtime/local-effect-executor.d.ts +21 -0
  287. package/dist/runtime/local-effect-executor.js +137 -0
  288. package/dist/runtime/looms.d.ts +30 -4
  289. package/dist/runtime/looms.js +57 -37
  290. package/dist/runtime/observer.d.ts +45 -0
  291. package/dist/runtime/observer.js +11 -0
  292. package/dist/runtime/repository.d.ts +55 -0
  293. package/dist/runtime/repository.js +293 -0
  294. package/dist/runtime/retry-policy.d.ts +16 -0
  295. package/dist/runtime/retry-policy.js +29 -0
  296. package/dist/runtime/run-execution-context.d.ts +30 -0
  297. package/dist/runtime/run-execution-context.js +56 -0
  298. package/dist/runtime/run-start.d.ts +4 -0
  299. package/dist/runtime/run-start.js +44 -0
  300. package/dist/runtime/runtime.d.ts +6 -87
  301. package/dist/runtime/runtime.js +248 -470
  302. package/dist/runtime/server/auth.d.ts +41 -0
  303. package/dist/runtime/server/auth.js +45 -0
  304. package/dist/runtime/server/debugger-host.d.ts +11 -0
  305. package/dist/runtime/server/debugger-host.js +32 -0
  306. package/dist/runtime/server/fetch-handler.d.ts +18 -0
  307. package/dist/runtime/server/fetch-handler.js +62 -0
  308. package/dist/runtime/server/http.d.ts +38 -0
  309. package/dist/runtime/server/http.js +66 -0
  310. package/dist/runtime/server/route-table.d.ts +55 -0
  311. package/dist/runtime/server/route-table.js +74 -0
  312. package/dist/runtime/server/routes/definitions.d.ts +3 -0
  313. package/dist/runtime/server/routes/definitions.js +20 -0
  314. package/dist/runtime/server/routes/effects.d.ts +3 -0
  315. package/dist/runtime/server/routes/effects.js +59 -0
  316. package/dist/runtime/server/routes/operations.d.ts +3 -0
  317. package/dist/runtime/server/routes/operations.js +14 -0
  318. package/dist/runtime/server/routes/runs.d.ts +3 -0
  319. package/dist/runtime/server/routes/runs.js +199 -0
  320. package/dist/runtime/server.d.ts +4 -8
  321. package/dist/runtime/server.js +6 -211
  322. package/dist/runtime/sse.d.ts +2 -1
  323. package/dist/runtime/sse.js +24 -17
  324. package/dist/runtime/types.d.ts +117 -0
  325. package/dist/runtime/types.js +1 -0
  326. package/dist/runtime/wake-engine.d.ts +20 -0
  327. package/dist/runtime/wake-engine.js +311 -0
  328. package/dist/runtime/wake-scheduler.d.ts +3 -4
  329. package/dist/runtime/wake-scheduler.js +25 -31
  330. package/dist/runtime/worker-callbacks.d.ts +16 -0
  331. package/dist/runtime/worker-callbacks.js +167 -0
  332. package/dist/runtime/worker-effect-dispatch.d.ts +36 -0
  333. package/dist/runtime/worker-effect-dispatch.js +126 -0
  334. package/dist/s2/config.d.ts +1 -1
  335. package/dist/s2/config.js +1 -1
  336. package/dist/s2/lite.js +70 -82
  337. package/dist/s2/pagination.d.ts +15 -0
  338. package/dist/s2/pagination.js +36 -0
  339. package/dist/s2/projector.js +6 -5
  340. package/dist/s2/snapshot-store.js +188 -161
  341. package/dist/s2/store.d.ts +3 -21
  342. package/dist/s2/store.js +71 -165
  343. package/dist/testing/index.d.ts +15 -0
  344. package/dist/testing/index.js +53 -16
  345. package/dist/workflow/debugger/index.d.ts +4 -0
  346. package/dist/workflow/debugger/index.js +11 -0
  347. package/dist/workflow/debugger/nodes-view.d.ts +3 -0
  348. package/dist/workflow/debugger/nodes-view.js +13 -0
  349. package/dist/workflow/debugger/summarize.d.ts +2 -0
  350. package/dist/workflow/debugger/summarize.js +30 -0
  351. package/dist/workflow/definitions-store.d.ts +2 -3
  352. package/dist/workflow/definitions-store.js +2 -2
  353. package/dist/workflow/definitions.d.ts +78 -22
  354. package/dist/workflow/definitions.js +80 -14
  355. package/dist/workflow/effects.d.ts +453 -107
  356. package/dist/workflow/effects.js +214 -115
  357. package/dist/workflow/events.d.ts +149 -5
  358. package/dist/workflow/events.js +27 -5
  359. package/dist/workflow/graph.d.ts +56 -0
  360. package/dist/workflow/graph.js +207 -0
  361. package/dist/workflow/index.d.ts +1 -0
  362. package/dist/workflow/index.js +1 -0
  363. package/dist/workflow/module.d.ts +1047 -266
  364. package/dist/workflow/projections.d.ts +14 -12
  365. package/dist/workflow/projections.js +30 -8
  366. package/dist/workflow/scope.d.ts +146 -4
  367. package/dist/workflow/threads.d.ts +383 -155
  368. package/dist/workflow/threads.js +308 -110
  369. package/package.json +26 -2
  370. package/dist/debugger/events/domain-summarize.d.ts +0 -3
  371. package/dist/debugger/events/domain-summarize.js +0 -107
  372. package/dist/debugger/events/family.d.ts +0 -8
  373. package/dist/debugger/events/family.js +0 -44
  374. package/dist/runtime/events-api.d.ts +0 -4
  375. package/dist/runtime/events-api.js +0 -84
@@ -1,151 +1,147 @@
1
- import { Predicate } from 'effect';
2
- import { createRunId, } from '@swirls/looms/core';
3
- import { consumeSseStream, delay, eventsFromSseData } from './sse.js';
1
+ import { Data, Effect, Option, Schema, Stream } from 'effect';
2
+ import { createRunId, DefinitionCatalogSchema, EventEnvelopeSchema, ReplayStepSchema, RunStateSchema, RunSummariesSchema, stringifyJson, } from '@swirls/looms/core';
3
+ import { consumeSseStreamEffect, eventsFromSseData, sseFrames } from './sse.js';
4
+ class LoomsClientError extends Data.TaggedError('LoomsClientError') {
5
+ constructor(message, cause) {
6
+ super({ cause, message });
7
+ this.name = 'LoomsClientError';
8
+ }
9
+ }
10
+ const decodeStreamError = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Struct({ error: Schema.String })));
11
+ const StartResultSchema = Schema.Struct({
12
+ runId: Schema.String,
13
+ threadId: Schema.String,
14
+ state: RunStateSchema,
15
+ });
16
+ const RunResultSchema = Schema.Struct({ runId: Schema.String, state: RunStateSchema });
17
+ const EventsResultSchema = Schema.Struct({
18
+ runId: Schema.String,
19
+ events: Schema.Array(EventEnvelopeSchema),
20
+ });
21
+ const ReplayResultSchema = Schema.Struct({
22
+ runId: Schema.String,
23
+ step: Schema.NullOr(ReplayStepSchema),
24
+ });
25
+ const ProjectionResultSchema = Schema.Struct({
26
+ runId: Schema.String,
27
+ name: Schema.String,
28
+ value: Schema.Unknown,
29
+ });
4
30
  export function createLoomsClient(options = {}) {
5
31
  const baseUrl = (options.baseUrl ?? '').replace(/\/$/, '');
6
32
  const fetchImpl = options.fetch ?? fetch;
7
- async function request(path, init) {
33
+ function request(path, schema, init) {
8
34
  const headers = new Headers(init?.headers);
9
35
  if (!headers.has('content-type')) {
10
36
  headers.set('content-type', 'application/json');
11
37
  }
12
- const res = await fetchImpl(`${baseUrl}${path}`, { ...init, headers });
13
- if (!res.ok) {
14
- const body = await res.text();
15
- throw new Error(body || `HTTP ${res.status}`);
16
- }
17
- // SAFETY: host JSON responses match the generic requested by each route helper.
18
- return (await res.json());
38
+ return Effect.runPromise(Effect.gen(function* () {
39
+ const res = yield* Effect.tryPromise({
40
+ try: (signal) => fetchImpl(`${baseUrl}${path}`, { ...init, headers, signal }),
41
+ catch: (cause) => new LoomsClientError('Request failed', cause),
42
+ });
43
+ if (!res.ok) {
44
+ const body = yield* Effect.promise(() => res.text());
45
+ return yield* new LoomsClientError(body || `HTTP ${res.status}`);
46
+ }
47
+ const body = yield* Effect.promise(() => res.json());
48
+ return yield* Schema.decodeUnknownEffect(schema)(body).pipe(Effect.mapError((cause) => new LoomsClientError('Invalid response body', cause)));
49
+ }));
19
50
  }
20
51
  const subscribeEvents = (runId, onEvent, reconnectDelayMs = 1_000) => {
21
52
  let fromSeq = 1;
22
53
  const controller = new AbortController();
23
- const run = async () => {
54
+ const run = Effect.gen(function* () {
24
55
  while (!controller.signal.aborted) {
25
- try {
56
+ yield* Effect.gen(function* () {
26
57
  const cursor = Math.max(0, fromSeq - 1);
27
58
  const headers = new Headers({ accept: 'text/event-stream' });
28
59
  if (cursor > 0) {
29
60
  headers.set('last-event-id', String(cursor));
30
61
  }
31
- const res = await fetchImpl(`${baseUrl}/api/events?runId=${encodeURIComponent(runId)}&live=true&cursor=${cursor}`, { headers, signal: controller.signal });
32
- if (!res.ok || !res.body) {
33
- throw new Error(res.ok ? 'event stream missing body' : `HTTP ${res.status}`);
34
- }
35
- await consumeSseStream(res.body, (frame) => {
36
- for (const event of eventsFromSseData(frame.data, runId)) {
37
- onEvent(event);
38
- fromSeq = Math.max(fromSeq, event.seq + 1);
39
- }
40
- }, controller.signal);
41
- }
42
- catch {
43
- if (controller.signal.aborted) {
44
- return;
62
+ const res = yield* Effect.tryPromise({
63
+ try: (signal) => fetchImpl(`${baseUrl}/runs/${encodeURIComponent(runId)}/events?fromSeq=${fromSeq}`, {
64
+ headers,
65
+ signal,
66
+ }),
67
+ catch: (cause) => new LoomsClientError('Event subscription failed', cause),
68
+ });
69
+ const body = res.body;
70
+ if (res.ok && body) {
71
+ return yield* consumeSseStreamEffect(body, (frame) => {
72
+ for (const event of eventsFromSseData(frame.data, runId)) {
73
+ onEvent(event);
74
+ fromSeq = Math.max(fromSeq, event.seq + 1);
75
+ }
76
+ });
45
77
  }
46
- }
78
+ return yield* new LoomsClientError(res.ok ? 'event stream missing body' : `HTTP ${res.status}`);
79
+ }).pipe(Effect.ignore);
47
80
  if (controller.signal.aborted) {
48
- return;
81
+ break;
49
82
  }
50
- await delay(reconnectDelayMs, controller.signal);
83
+ yield* Effect.sleep(reconnectDelayMs);
51
84
  }
52
- };
53
- void run();
85
+ });
86
+ void Effect.runPromise(run, { signal: controller.signal }).catch(() => undefined);
54
87
  return () => {
55
88
  controller.abort();
56
89
  };
57
90
  };
58
- const startRun = (args) => request('/runs', { method: 'POST', body: JSON.stringify(args) });
91
+ const startRun = (args) => request('/runs', StartResultSchema, { method: 'POST', body: stringifyJson(args) });
92
+ const workerCallback = (runId, input) => {
93
+ const body = input.kind === 'complete'
94
+ ? { events: input.events }
95
+ : input.kind === 'fail'
96
+ ? { error: input.error }
97
+ : {};
98
+ return request(`/runs/${encodeURIComponent(runId)}/effects/${encodeURIComponent(input.effectId)}/${input.attempt}/${input.kind}`, RunResultSchema, {
99
+ method: 'POST',
100
+ headers: options.workerCallbackToken
101
+ ? { authorization: `Bearer ${options.workerCallbackToken}` }
102
+ : undefined,
103
+ body: stringifyJson(body),
104
+ });
105
+ };
59
106
  const streamRun = (args) => {
60
107
  const runId = args.runId ?? createRunId();
61
- return {
62
- runId,
63
- async *[Symbol.asyncIterator]() {
64
- const controller = new AbortController();
65
- const res = await fetchImpl(`${baseUrl}/runs`, {
66
- method: 'POST',
67
- headers: {
68
- 'content-type': 'application/json',
69
- accept: 'text/event-stream',
70
- },
71
- body: JSON.stringify({ ...args, runId }),
72
- signal: controller.signal,
73
- });
74
- if (!res.ok || !res.body) {
75
- throw new Error(res.ok ? 'SSE stream missing body' : `HTTP ${res.status}`);
76
- }
77
- const pending = [];
78
- let notify;
79
- let finished = false;
80
- let streamError;
81
- const consume = consumeSseStream(res.body, (frame) => {
82
- if (frame.event === 'error') {
83
- try {
84
- const parsed = JSON.parse(frame.data);
85
- const message = Predicate.isReadonlyObject(parsed) && Predicate.isString(parsed.error)
86
- ? parsed.error
87
- : frame.data;
88
- streamError = new Error(message);
89
- }
90
- catch {
91
- streamError = new Error(frame.data);
92
- }
93
- return;
94
- }
95
- if (frame.event === 'done') {
96
- finished = true;
97
- notify?.();
98
- return;
99
- }
100
- for (const event of eventsFromSseData(frame.data, runId)) {
101
- pending.push(event);
102
- }
103
- notify?.();
104
- }, controller.signal)
105
- .catch((cause) => {
106
- streamError = cause instanceof Error ? cause : new Error(String(cause));
107
- })
108
- .finally(() => {
109
- finished = true;
110
- notify?.();
111
- });
112
- try {
113
- while (true) {
114
- if (pending.length === 0) {
115
- if (finished) {
116
- break;
117
- }
118
- await new Promise((resolve) => {
119
- notify = resolve;
120
- });
121
- continue;
122
- }
123
- const event = pending.shift();
124
- if (event) {
125
- yield event;
126
- }
127
- }
128
- await consume;
129
- if (streamError) {
130
- throw streamError;
131
- }
132
- }
133
- finally {
134
- controller.abort();
135
- }
136
- },
137
- };
108
+ const stream = Stream.unwrap(Effect.tryPromise({
109
+ try: (signal) => fetchImpl(`${baseUrl}/runs`, {
110
+ method: 'POST',
111
+ headers: {
112
+ 'content-type': 'application/json',
113
+ accept: 'text/event-stream',
114
+ },
115
+ body: stringifyJson({ ...args, runId }),
116
+ signal,
117
+ }),
118
+ catch: (cause) => new LoomsClientError('Failed to start event stream', cause),
119
+ }).pipe(Effect.flatMap((res) => res.ok && res.body
120
+ ? Effect.succeed(sseFrames(res.body))
121
+ : new LoomsClientError(res.ok ? 'SSE stream missing body' : `HTTP ${res.status}`)))).pipe(Stream.takeWhile((frame) => frame.event !== 'done'), Stream.mapEffect((frame) => {
122
+ if (frame.event !== 'error') {
123
+ return Effect.succeed(eventsFromSseData(frame.data, runId));
124
+ }
125
+ const decoded = decodeStreamError(frame.data);
126
+ return new LoomsClientError(Option.isSome(decoded) ? decoded.value.error : frame.data);
127
+ }), Stream.flatMap(Stream.fromIterable));
128
+ return Object.assign(Stream.toAsyncIterable(stream), { runId });
138
129
  };
139
130
  return {
140
131
  /** Start a run by `{ kind, definitionName }` when you only have names. */
141
132
  startRun,
142
133
  /** Start a run from a definition object (agent, workflow, or your own kind). */
143
- start: (definition, input) => request('/runs', {
134
+ start: (definition, input) => request('/runs', StartResultSchema, {
144
135
  method: 'POST',
145
- body: JSON.stringify({ kind: definition.kind, definitionName: definition.name, input }),
136
+ body: stringifyJson({
137
+ kind: definition.kind,
138
+ definitionName: definition.name,
139
+ definitionVersion: definition.version,
140
+ input,
141
+ }),
146
142
  }),
147
- getRun: (runId) => request(`/runs/${runId}`),
148
- getState: (runId) => request(`/runs/${runId}`),
143
+ getRun: (runId) => request(`/runs/${encodeURIComponent(runId)}`, RunResultSchema),
144
+ getState: (runId) => request(`/runs/${encodeURIComponent(runId)}`, RunResultSchema),
149
145
  getEvents: (runId, opts) => {
150
146
  const params = new URLSearchParams();
151
147
  if (opts?.fromSeq !== undefined) {
@@ -155,15 +151,18 @@ export function createLoomsClient(options = {}) {
155
151
  params.set('limit', String(opts.limit));
156
152
  }
157
153
  const q = params.toString();
158
- return request(`/runs/${runId}/events${q ? `?${q}` : ''}`);
154
+ return request(`/runs/${encodeURIComponent(runId)}/events${q ? `?${q}` : ''}`, EventsResultSchema);
159
155
  },
160
- signal: (runId, events) => request(`/runs/${runId}/events`, {
156
+ signal: (runId, events) => request(`/runs/${encodeURIComponent(runId)}/events`, RunResultSchema, {
161
157
  method: 'POST',
162
- body: JSON.stringify({ events }),
158
+ body: stringifyJson({ events }),
163
159
  }),
164
- wake: (runId) => request(`/runs/${runId}/wake`, { method: 'POST' }),
165
- replayTo: (runId, seq) => request(`/runs/${runId}/replay?seq=${seq}`),
166
- project: (runId, name) => request(`/runs/${runId}/projections/${name}`),
160
+ wake: (runId) => request(`/runs/${encodeURIComponent(runId)}/wake`, RunResultSchema, { method: 'POST' }),
161
+ workerCallback,
162
+ listRunSummaries: () => request('/runs/summaries', RunSummariesSchema),
163
+ listDefinitions: () => request('/definitions', DefinitionCatalogSchema),
164
+ replayTo: (runId, seq) => request(`/runs/${encodeURIComponent(runId)}/replay?seq=${seq}`, ReplayResultSchema),
165
+ project: (runId, name) => request(`/runs/${encodeURIComponent(runId)}/projections/${encodeURIComponent(name)}`, ProjectionResultSchema),
167
166
  subscribeEvents,
168
167
  /**
169
168
  * Subscribe to the run log, then start execution so deltas arrive live.
@@ -173,6 +172,7 @@ export function createLoomsClient(options = {}) {
173
172
  stream: (definition, input) => streamRun({
174
173
  kind: definition.kind,
175
174
  definitionName: definition.name,
175
+ definitionVersion: definition.version,
176
176
  input,
177
177
  }),
178
178
  };
@@ -1,2 +1,2 @@
1
1
  export { createLoomsClient, type LoomsClient, type LoomsClientOptions, type StartResult, type StreamHandle, } from './client';
2
- export { consumeSseStream, delay, eventsFromSseData, parseSseFrame, type SseFrame } from './sse';
2
+ export { consumeSseStream, consumeSseStreamEffect, delay, eventsFromSseData, parseSseFrame, type SseFrame, } from './sse';
@@ -1,2 +1,2 @@
1
1
  export { createLoomsClient, } from './client.js';
2
- export { consumeSseStream, delay, eventsFromSseData, parseSseFrame } from './sse.js';
2
+ export { consumeSseStream, consumeSseStreamEffect, delay, eventsFromSseData, parseSseFrame, } from './sse.js';
@@ -1,3 +1,4 @@
1
+ import { Effect, Stream } from 'effect';
1
2
  import { type EventEnvelope } from '@swirls/looms/core';
2
3
  export interface SseFrame {
3
4
  id?: string;
@@ -5,6 +6,18 @@ export interface SseFrame {
5
6
  data: string;
6
7
  }
7
8
  export declare function parseSseFrame(raw: string): SseFrame | undefined;
9
+ declare const SseStreamError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
10
+ readonly _tag: "SseStreamError";
11
+ } & Readonly<A>;
12
+ export declare class SseStreamError extends SseStreamError_base<{
13
+ readonly cause: unknown;
14
+ readonly message: string;
15
+ }> {
16
+ constructor(cause: unknown);
17
+ }
18
+ export declare function consumeSseStreamEffect(body: ReadableStream<Uint8Array>, onFrame: (frame: SseFrame) => void): Effect.Effect<void, SseStreamError>;
19
+ export declare function sseFrames(body: ReadableStream<Uint8Array>): Stream.Stream<SseFrame, SseStreamError>;
8
20
  export declare function consumeSseStream(body: ReadableStream<Uint8Array>, onFrame: (frame: SseFrame) => void, signal?: AbortSignal): Promise<void>;
9
21
  export declare function eventsFromSseData(data: string, runId: string): EventEnvelope[];
10
22
  export declare function delay(ms: number, signal?: AbortSignal): Promise<void>;
23
+ export {};
@@ -1,5 +1,6 @@
1
- import { Predicate } from 'effect';
1
+ import { Data, Effect, Option, Predicate, Queue, Schema, Stream } from 'effect';
2
2
  import { decodeLoomsEvent } from '@swirls/looms/core';
3
+ const decodeSseData = Schema.decodeOption(Schema.fromJsonString(Schema.Unknown));
3
4
  export function parseSseFrame(raw) {
4
5
  let id;
5
6
  let event;
@@ -25,20 +26,24 @@ export function parseSseFrame(raw) {
25
26
  }
26
27
  return { id, event, data: dataLines.join('\n') };
27
28
  }
28
- export async function consumeSseStream(body, onFrame, signal) {
29
- const reader = body.getReader();
29
+ export class SseStreamError extends Data.TaggedError('SseStreamError') {
30
+ constructor(cause) {
31
+ super({
32
+ cause,
33
+ message: cause instanceof Error ? cause.message : String(cause),
34
+ });
35
+ this.name = 'SseStreamError';
36
+ }
37
+ }
38
+ export function consumeSseStreamEffect(body, onFrame) {
30
39
  const decoder = new TextDecoder();
31
- let buffer = '';
32
- const abort = () => {
33
- void reader.cancel();
34
- };
35
- signal?.addEventListener('abort', abort);
36
- try {
40
+ return Effect.acquireUseRelease(Effect.sync(() => body.getReader()), (reader) => Effect.gen(function* () {
41
+ let buffer = '';
37
42
  while (true) {
38
- if (signal?.aborted) {
39
- break;
40
- }
41
- const { done, value } = await reader.read();
43
+ const { done, value } = yield* Effect.tryPromise({
44
+ try: () => reader.read(),
45
+ catch: (cause) => new SseStreamError(cause),
46
+ });
42
47
  if (done) {
43
48
  break;
44
49
  }
@@ -52,35 +57,31 @@ export async function consumeSseStream(body, onFrame, signal) {
52
57
  }
53
58
  }
54
59
  }
55
- }
56
- finally {
57
- signal?.removeEventListener('abort', abort);
58
- try {
59
- reader.releaseLock();
60
- }
61
- catch {
62
- // reader already cancelled
63
- }
64
- }
60
+ }), (reader) => Effect.promise(() => reader.cancel()).pipe(Effect.ignore, Effect.ensuring(Effect.sync(() => reader.releaseLock()).pipe(Effect.ignore))));
61
+ }
62
+ export function sseFrames(body) {
63
+ return Stream.callback((queue) => consumeSseStreamEffect(body, (frame) => {
64
+ Queue.offerUnsafe(queue, frame);
65
+ }).pipe(Effect.tap(() => Effect.sync(() => {
66
+ Queue.endUnsafe(queue);
67
+ }))));
68
+ }
69
+ export function consumeSseStream(body, onFrame, signal) {
70
+ return Effect.runPromise(consumeSseStreamEffect(body, onFrame), { signal });
65
71
  }
66
72
  export function eventsFromSseData(data, runId) {
67
- let parsed;
68
- try {
69
- parsed = JSON.parse(data);
70
- }
71
- catch {
73
+ const parsed = decodeSseData(data);
74
+ if (Option.isNone(parsed)) {
72
75
  return [];
73
76
  }
74
- if (!Predicate.isReadonlyObject(parsed)) {
77
+ if (!Predicate.isReadonlyObject(parsed.value)) {
75
78
  return [];
76
79
  }
77
- const batch = 'batch' in parsed && Array.isArray(parsed.batch) ? parsed.batch : [];
80
+ const batch = 'batch' in parsed.value && Array.isArray(parsed.value.batch) ? parsed.value.batch : [];
78
81
  const events = [];
79
82
  for (const item of batch) {
80
83
  try {
81
- events.push(decodeLoomsEvent(
82
- // SAFETY: host SSE batch items are encoded Looms event envelopes.
83
- item, runId));
84
+ events.push(decodeLoomsEvent(item, runId));
84
85
  }
85
86
  catch {
86
87
  // skip a malformed item and keep the rest of the batch
@@ -89,15 +90,8 @@ export function eventsFromSseData(data, runId) {
89
90
  return events;
90
91
  }
91
92
  export function delay(ms, signal) {
92
- return new Promise((resolve) => {
93
- if (signal?.aborted) {
94
- resolve();
95
- return;
96
- }
97
- const timer = setTimeout(resolve, ms);
98
- signal?.addEventListener('abort', () => {
99
- clearTimeout(timer);
100
- resolve();
101
- }, { once: true });
102
- });
93
+ if (signal?.aborted) {
94
+ return Promise.resolve();
95
+ }
96
+ return Effect.runPromise(Effect.sleep(ms), { signal }).catch(() => undefined);
103
97
  }
@@ -1,9 +1,19 @@
1
+ import { Effect } from 'effect';
1
2
  import type { WakeScheduler } from '@swirls/looms/runtime';
2
3
  export interface AlarmStorage {
3
4
  setAlarm(scheduledTime: number | Date): Promise<void>;
4
5
  deleteAlarm(): Promise<void>;
6
+ getAlarm?(): Promise<number | null>;
7
+ }
8
+ export interface DurableObjectAlarms {
9
+ /** Wake scheduler for the runtime; its deadline is remembered across rearms. */
10
+ readonly scheduler: WakeScheduler;
11
+ /** Ensures the alarm fires no later than `at` without forgetting the runtime deadline. */
12
+ readonly scheduleAtEarliest: (at: number) => Effect.Effect<void>;
5
13
  }
6
14
  /**
7
- * Adapter that maps actor wake timer scheduling to Cloudflare Durable Object alarms.
15
+ * The single owner of a Durable Object's one alarm. Every write is serialized and sets the alarm to
16
+ * the earliest of the runtime deadline, `additionalDeadline` (such as projector retries), and the
17
+ * requested time, so concurrent writers cannot overwrite each other's deadlines.
8
18
  */
9
- export declare function alarmScheduler(storage: AlarmStorage): WakeScheduler;
19
+ export declare function durableObjectAlarms(storage: AlarmStorage, additionalDeadline?: () => Effect.Effect<number | null>): DurableObjectAlarms;
@@ -1,13 +1,40 @@
1
+ import { Effect, Semaphore } from 'effect';
2
+ function earliest(...deadlines) {
3
+ const known = deadlines.filter((deadline) => deadline !== null && deadline !== undefined);
4
+ return known.length === 0 ? null : Math.min(...known);
5
+ }
1
6
  /**
2
- * Adapter that maps actor wake timer scheduling to Cloudflare Durable Object alarms.
7
+ * The single owner of a Durable Object's one alarm. Every write is serialized and sets the alarm to
8
+ * the earliest of the runtime deadline, `additionalDeadline` (such as projector retries), and the
9
+ * requested time, so concurrent writers cannot overwrite each other's deadlines.
3
10
  */
4
- export function alarmScheduler(storage) {
11
+ export function durableObjectAlarms(storage, additionalDeadline) {
12
+ // `undefined` until the runtime reports a deadline; until then an existing alarm is preserved.
13
+ let runtimeDeadline;
14
+ const writes = Semaphore.makeUnsafe(1);
15
+ const rearm = (requested) => Semaphore.withPermits(writes, 1, Effect.gen(function* () {
16
+ const [additional, current] = yield* Effect.all([
17
+ additionalDeadline?.() ?? Effect.succeed(null),
18
+ Effect.promise(() => storage.getAlarm?.() ?? Promise.resolve(null)),
19
+ ], { concurrency: 'unbounded' });
20
+ const preserved = runtimeDeadline === undefined ? current : null;
21
+ const desired = earliest(runtimeDeadline, additional, requested, preserved);
22
+ if (desired === current) {
23
+ return;
24
+ }
25
+ yield* Effect.promise(() => desired === null ? storage.deleteAlarm() : storage.setAlarm(desired));
26
+ }));
5
27
  return {
6
- schedule: async (_runId, at) => {
7
- await storage.setAlarm(at);
8
- },
9
- cancel: async (_runId) => {
10
- await storage.deleteAlarm();
28
+ scheduler: {
29
+ schedule: (_runId, at) => Effect.suspend(() => {
30
+ runtimeDeadline = at;
31
+ return rearm(null);
32
+ }),
33
+ cancel: () => Effect.suspend(() => {
34
+ runtimeDeadline = null;
35
+ return rearm(null);
36
+ }),
11
37
  },
38
+ scheduleAtEarliest: rearm,
12
39
  };
13
40
  }
@@ -1,14 +1,19 @@
1
1
  import { DurableObject } from 'cloudflare:workers';
2
- import type { AnyRuntimeModule } from '@swirls/looms/core';
2
+ import type { AnyRuntimeModule, EventStoreTrimCoverage } from '@swirls/looms/core';
3
3
  import type { Projector } from '@swirls/looms/projectors';
4
+ import type { Authorize } from '@swirls/looms/runtime';
4
5
  export interface LoomsDurableObjectConfig {
5
6
  readonly modules: readonly AnyRuntimeModule[];
6
7
  readonly projectors?: readonly Projector[];
7
8
  readonly snapshotEvery?: number;
8
9
  readonly maxWakeIterations?: number;
10
+ readonly maxPendingRunOperations?: number;
9
11
  readonly trimAfterSnapshot?: {
10
- keepSnapshots: number;
12
+ readonly keepSnapshots: number;
13
+ readonly coverage?: (runId: string) => EventStoreTrimCoverage | Promise<EventStoreTrimCoverage>;
11
14
  };
15
+ /** Authorizes the actor's HTTP API; see `createFetchHandler`. */
16
+ readonly authorize?: Authorize;
12
17
  }
13
18
  /**
14
19
  * Base Durable Object class for hosting a Looms actor cell.
@@ -17,8 +22,11 @@ export interface LoomsDurableObjectConfig {
17
22
  */
18
23
  export declare abstract class LoomsDurableObject<Env = unknown> extends DurableObject<Env> {
19
24
  private cellPromise?;
25
+ private store?;
20
26
  abstract configure(env: Env): LoomsDurableObjectConfig | Promise<LoomsDurableObjectConfig>;
21
27
  private getCell;
28
+ private createCell;
22
29
  fetch(req: Request): Promise<Response>;
30
+ /** A rejected alarm is retried by the platform with backoff, so failures just propagate. */
23
31
  alarm(): Promise<void>;
24
32
  }
@@ -1,6 +1,7 @@
1
1
  import { DurableObject } from 'cloudflare:workers';
2
+ import { Effect } from 'effect';
2
3
  import { createActorCell } from '@swirls/looms/actor';
3
- import { alarmScheduler } from './alarm-scheduler.js';
4
+ import { durableObjectAlarms } from './alarm-scheduler.js';
4
5
  import { durableObjectEventStore } from './event-store.js';
5
6
  /**
6
7
  * Base Durable Object class for hosting a Looms actor cell.
@@ -9,35 +10,65 @@ import { durableObjectEventStore } from './event-store.js';
9
10
  */
10
11
  export class LoomsDurableObject extends DurableObject {
11
12
  cellPromise;
12
- async getCell() {
13
- if (!this.cellPromise) {
14
- this.cellPromise = (async () => {
15
- const runId = this.ctx.id.name;
16
- if (!runId) {
17
- throw new Error('LoomsDurableObject requires a named Durable Object id (use idFromName / getByName)');
18
- }
19
- const config = await this.configure(this.env);
20
- const store = durableObjectEventStore(this.ctx, { projectors: config.projectors });
21
- const scheduler = alarmScheduler(this.ctx.storage);
22
- return createActorCell({
23
- runId,
24
- store,
25
- scheduler,
26
- modules: config.modules,
27
- snapshotEvery: config.snapshotEvery,
28
- maxWakeIterations: config.maxWakeIterations,
29
- trimAfterSnapshot: config.trimAfterSnapshot,
30
- });
31
- })();
13
+ store;
14
+ getCell() {
15
+ if (this.cellPromise) {
16
+ return this.cellPromise;
32
17
  }
33
- return this.cellPromise;
18
+ const created = this.createCell();
19
+ this.cellPromise = created;
20
+ // A failed setup must not be cached, or every later request and alarm would fail with it.
21
+ created.catch(() => {
22
+ if (this.cellPromise === created) {
23
+ this.cellPromise = undefined;
24
+ }
25
+ });
26
+ return created;
34
27
  }
35
- async fetch(req) {
36
- const cell = await this.getCell();
37
- return cell.fetch(req);
28
+ createCell() {
29
+ return Promise.resolve(this.configure(this.env)).then((config) => {
30
+ const runId = this.ctx.id.name;
31
+ if (!runId) {
32
+ throw new Error('LoomsDurableObject requires a named Durable Object id (use idFromName / getByName)');
33
+ }
34
+ const alarms = durableObjectAlarms(this.ctx.storage, () => this.store?.projectorDelivery?.nextRetryAt(runId).pipe(Effect.orDie) ??
35
+ Effect.succeed(null));
36
+ const store = durableObjectEventStore(this.ctx, {
37
+ projectors: config.projectors,
38
+ scheduleProjectorRetry: alarms.scheduleAtEarliest,
39
+ });
40
+ this.store = store;
41
+ const cell = createActorCell({
42
+ runId,
43
+ store,
44
+ scheduler: alarms.scheduler,
45
+ modules: config.modules,
46
+ snapshotEvery: config.snapshotEvery,
47
+ maxWakeIterations: config.maxWakeIterations,
48
+ maxPendingRunOperations: config.maxPendingRunOperations,
49
+ trimAfterSnapshot: config.trimAfterSnapshot,
50
+ authorize: config.authorize,
51
+ });
52
+ const delivery = store.projectorDelivery;
53
+ const recoverProjectors = delivery
54
+ ? Effect.runPromise(delivery.recover(runId).pipe(Effect.andThen(delivery.deliver(runId))))
55
+ : Promise.resolve();
56
+ return recoverProjectors.then(() => cell.recoverWake()).then(() => cell);
57
+ });
38
58
  }
39
- async alarm() {
40
- const cell = await this.getCell();
41
- await cell.wake();
59
+ fetch(req) {
60
+ return this.getCell().then((cell) => cell.fetch(req));
61
+ }
62
+ /** A rejected alarm is retried by the platform with backoff, so failures just propagate. */
63
+ alarm() {
64
+ return this.getCell()
65
+ .then((cell) => {
66
+ const delivery = this.store?.projectorDelivery;
67
+ const delivered = delivery
68
+ ? Effect.runPromise(delivery.deliver(this.ctx.id.name ?? ''))
69
+ : Promise.resolve();
70
+ return delivered.then(() => cell.wake());
71
+ })
72
+ .then(() => undefined);
42
73
  }
43
74
  }