@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,10 +1,16 @@
1
- import { Predicate } from 'effect';
2
- import { decodeLoomsEvent, } from '@swirls/looms/core';
1
+ import { Data, Effect, Fiber, Option, Predicate, Schema } from 'effect';
2
+ import { decodeLoomsEvent, stringifyJson, } from '@swirls/looms/core';
3
3
  import { EventIndex, ProjectionCache } from './derived.js';
4
4
  import { materializeEvents } from './materialize.js';
5
5
  import { createNotifyScheduler } from './notify.js';
6
- import { consumeSseStream, delay, eventsFromSseData } from './sse.js';
6
+ import { consumeSseStreamEffect, eventsFromSseData } from './sse.js';
7
7
  import { emptyTables } from './tables.js';
8
+ class LoomsStoreError extends Data.TaggedError('LoomsStoreError') {
9
+ constructor(message, cause) {
10
+ super({ cause, message });
11
+ this.name = 'LoomsStoreError';
12
+ }
13
+ }
8
14
  function eventSourceCtor() {
9
15
  if (typeof EventSource === 'undefined') {
10
16
  return undefined;
@@ -36,13 +42,12 @@ export function createLoomsStore(options) {
36
42
  let liveStarted = false;
37
43
  let liveAbort;
38
44
  let eventSource;
39
- let reconnectTimer;
45
+ let reconnectFiber;
40
46
  let syncing = null;
41
47
  const publishedEvents = () => {
42
48
  if (!eventsSnapshotDirty) {
43
49
  return eventsSnapshot;
44
50
  }
45
- // SAFETY: events array only contains TEvent values pushed from applyBatch.
46
51
  eventsSnapshot = Object.freeze(events.slice());
47
52
  eventsSnapshotDirty = false;
48
53
  return eventsSnapshot;
@@ -68,7 +73,6 @@ export function createLoomsStore(options) {
68
73
  continue;
69
74
  }
70
75
  seenIds.add(event.id);
71
- // SAFETY: wire events match the app-registered TEvent catalog at the type boundary.
72
76
  fresh.push(event);
73
77
  fromSeq = Math.max(fromSeq, event.seq + 1);
74
78
  }
@@ -88,85 +92,67 @@ export function createLoomsStore(options) {
88
92
  applyBatch(eventsFromSseData(data, options.storeId));
89
93
  };
90
94
  const liveUrl = () => {
91
- const cursor = Math.max(0, fromSeq - 1);
92
- return `${endpoint}/api/events?runId=${encodeURIComponent(options.storeId)}&live=true&cursor=${cursor}`;
95
+ return `${endpoint}/runs/${encodeURIComponent(options.storeId)}/events?live=true&fromSeq=${fromSeq}`;
93
96
  };
94
- const pull = async () => {
95
- try {
96
- const cursor = Math.max(0, fromSeq - 1);
97
- const url = `${endpoint}/api/events?runId=${encodeURIComponent(options.storeId)}&cursor=${cursor}`;
98
- const res = await fetchFn(url);
99
- if (!res.ok) {
100
- if (res.status === 404 || res.status === 416) {
101
- return;
102
- }
103
- const text = await res.text();
104
- if (text.includes('out of range') || text.includes('Range not satisfiable')) {
105
- return;
106
- }
107
- throw new Error(`event pull failed: ${res.status}`);
108
- }
109
- const body = await res.json();
110
- if (!Predicate.isReadonlyObject(body)) {
111
- return;
97
+ const pull = Effect.gen(function* () {
98
+ const url = `${endpoint}/runs/${encodeURIComponent(options.storeId)}/events?fromSeq=${fromSeq}`;
99
+ const res = yield* Effect.tryPromise({
100
+ try: (signal) => fetchFn(url, { signal }),
101
+ catch: (cause) => new LoomsStoreError('Event pull failed', cause),
102
+ });
103
+ if (!res.ok) {
104
+ if (res.status === 404 || res.status === 416) {
105
+ return undefined;
112
106
  }
113
- const batch = 'batch' in body && Array.isArray(body.batch) ? body.batch : [];
114
- applyBatch(batch.map((raw) => decodeLoomsEvent(
115
- // SAFETY: host pull batch items are encoded Looms event envelopes.
116
- raw, options.storeId)));
117
- const head = 'head' in body && Predicate.isNumber(body.head) ? body.head : undefined;
118
- if (head !== undefined) {
119
- fromSeq = Math.max(fromSeq, head + 1);
107
+ const text = yield* Effect.promise(() => res.text());
108
+ if (text.includes('out of range') || text.includes('Range not satisfiable')) {
109
+ return undefined;
120
110
  }
111
+ return yield* new LoomsStoreError(`Event pull failed: ${res.status}`);
121
112
  }
122
- catch (err) {
123
- if (disposed) {
124
- return;
125
- }
126
- // When offline or host is restarting, suppress connection refused/fetch errors during sync
127
- if (err instanceof Error &&
128
- (err.message.includes('fetch failed') ||
129
- err.message.includes('ConnectionRefused') ||
130
- err.message.includes('Unable to connect'))) {
131
- return;
132
- }
133
- throw err;
113
+ const rawBody = yield* Effect.promise(() => res.json());
114
+ const decodedBody = Schema.decodeUnknownOption(Schema.Json)(rawBody);
115
+ if (Option.isNone(decodedBody) || !Predicate.isReadonlyObject(decodedBody.value)) {
116
+ return undefined;
134
117
  }
135
- };
136
- const sync = async () => {
118
+ const body = decodedBody.value;
119
+ const pulledEvents = 'events' in body && Array.isArray(body.events) ? body.events : [];
120
+ applyBatch(pulledEvents.map((raw) => decodeLoomsEvent(raw, options.storeId)));
121
+ return undefined;
122
+ }).pipe(Effect.catchIf((error) => disposed ||
123
+ error.message.includes('fetch failed') ||
124
+ error.message.includes('ConnectionRefused') ||
125
+ error.message.includes('Unable to connect'), () => Effect.void));
126
+ const sync = () => {
137
127
  if (disposed) {
138
- return;
128
+ return Promise.resolve();
139
129
  }
140
130
  if (syncing) {
141
131
  return syncing;
142
132
  }
143
- syncing = (async () => {
144
- try {
145
- await pull();
146
- }
147
- finally {
148
- syncing = null;
149
- }
150
- })();
133
+ syncing = Effect.runPromise(pull).finally(() => {
134
+ syncing = null;
135
+ });
151
136
  return syncing;
152
137
  };
153
138
  const scheduleReconnect = (Ctor) => {
154
- if (disposed || !liveStarted || reconnectTimer !== undefined) {
139
+ if (disposed || !liveStarted || reconnectFiber !== undefined) {
155
140
  return;
156
141
  }
157
- reconnectTimer = setTimeout(() => {
158
- reconnectTimer = undefined;
142
+ reconnectFiber = Effect.runFork(Effect.gen(function* () {
143
+ yield* Effect.sleep(reconnectDelayMs);
144
+ reconnectFiber = undefined;
159
145
  if (disposed || !liveStarted) {
160
146
  return;
161
147
  }
162
- void sync()
163
- .catch(() => { })
164
- .finally(() => {
165
- if (!disposed && liveStarted) {
166
- startEventSource(Ctor);
167
- }
168
- });
169
- }, reconnectDelayMs);
148
+ yield* Effect.tryPromise({
149
+ try: sync,
150
+ catch: (cause) => new LoomsStoreError('Event resync failed', cause),
151
+ }).pipe(Effect.ignore);
152
+ if (!disposed && liveStarted) {
153
+ startEventSource(Ctor);
154
+ }
155
+ }));
170
156
  };
171
157
  const startEventSource = (Ctor) => {
172
158
  if (disposed || !liveStarted) {
@@ -199,45 +185,46 @@ export function createLoomsStore(options) {
199
185
  }
200
186
  });
201
187
  };
202
- const startFetchStream = async () => {
203
- while (true) {
204
- if (disposed) {
205
- break;
206
- }
207
- const controller = new AbortController();
208
- liveAbort = controller;
209
- try {
210
- const headers = new Headers({ accept: 'text/event-stream' });
211
- const cursor = Math.max(0, fromSeq - 1);
212
- if (cursor > 0) {
213
- headers.set('last-event-id', String(cursor));
214
- }
215
- const res = await fetchFn(liveUrl(), { headers, signal: controller.signal });
216
- if (!res.ok) {
217
- throw new Error(`event stream failed: ${res.status}`);
218
- }
219
- if (!res.body) {
220
- throw new Error('event stream missing body');
221
- }
222
- live = true;
223
- setStatus('live');
224
- await consumeSseStream(res.body, (frame) => {
225
- applyEncoded(frame.data);
226
- }, controller.signal);
227
- }
228
- catch {
229
- live = false;
188
+ const startFetchStream = () => {
189
+ const controller = new AbortController();
190
+ liveAbort = controller;
191
+ const run = Effect.gen(function* () {
192
+ while (true) {
230
193
  if (disposed) {
231
194
  return;
232
195
  }
233
- setStatus('reconnecting');
234
- }
235
- live = false;
236
- if (disposed) {
237
- return;
196
+ yield* Effect.gen(function* () {
197
+ const headers = new Headers({ accept: 'text/event-stream' });
198
+ const cursor = Math.max(0, fromSeq - 1);
199
+ if (cursor > 0) {
200
+ headers.set('last-event-id', String(cursor));
201
+ }
202
+ const res = yield* Effect.tryPromise({
203
+ try: (signal) => fetchFn(liveUrl(), { headers, signal }),
204
+ catch: (cause) => new LoomsStoreError('Event stream failed', cause),
205
+ });
206
+ const body = res.body;
207
+ if (!res.ok || !body) {
208
+ return yield* new LoomsStoreError(res.ok ? 'Event stream missing body' : `Event stream failed: ${res.status}`);
209
+ }
210
+ live = true;
211
+ setStatus('live');
212
+ return yield* consumeSseStreamEffect(body, (frame) => {
213
+ applyEncoded(frame.data);
214
+ });
215
+ }).pipe(Effect.catch(() => Effect.sync(() => {
216
+ live = false;
217
+ if (!disposed) {
218
+ setStatus('reconnecting');
219
+ }
220
+ })));
221
+ live = false;
222
+ if (!disposed) {
223
+ yield* Effect.sleep(reconnectDelayMs);
224
+ }
238
225
  }
239
- await delay(reconnectDelayMs, controller.signal);
240
- }
226
+ });
227
+ void Effect.runPromise(run, { signal: controller.signal }).catch(() => undefined);
241
228
  };
242
229
  const startLive = () => {
243
230
  if (!options.storeId || (liveStarted && !disposed)) {
@@ -250,15 +237,15 @@ export function createLoomsStore(options) {
250
237
  startEventSource(ctor);
251
238
  }
252
239
  else {
253
- void startFetchStream();
240
+ startFetchStream();
254
241
  }
255
242
  };
256
243
  const stopLive = () => {
257
244
  liveStarted = false;
258
245
  live = false;
259
- if (reconnectTimer !== undefined) {
260
- clearTimeout(reconnectTimer);
261
- reconnectTimer = undefined;
246
+ if (reconnectFiber !== undefined) {
247
+ Effect.runFork(Fiber.interrupt(reconnectFiber));
248
+ reconnectFiber = undefined;
262
249
  }
263
250
  eventSource?.close();
264
251
  eventSource = undefined;
@@ -307,26 +294,29 @@ export function createLoomsStore(options) {
307
294
  project: (definition) => projectionCache.project(definition, publishedEvents()),
308
295
  fold: (definition) => projectionCache.fold(definition, publishedEvents()),
309
296
  flush: notify.flush,
310
- sync: async () => {
311
- const res = await sync();
297
+ sync: () => sync().then(() => {
312
298
  notify.flush();
313
- return res;
314
- },
315
- commit: async (event) => {
316
- const res = await fetchFn(`${endpoint}/runs/${encodeURIComponent(options.storeId)}/events`, {
317
- method: 'POST',
318
- headers: { 'content-type': 'application/json' },
319
- body: JSON.stringify(event),
299
+ }),
300
+ commit: (event) => Effect.runPromise(Effect.gen(function* () {
301
+ const res = yield* Effect.tryPromise({
302
+ try: (signal) => fetchFn(`${endpoint}/runs/${encodeURIComponent(options.storeId)}/events`, {
303
+ method: 'POST',
304
+ headers: { 'content-type': 'application/json' },
305
+ body: stringifyJson(Array.isArray(event) ? { events: event } : event),
306
+ signal,
307
+ }),
308
+ catch: (cause) => new LoomsStoreError('Run signal failed', cause),
320
309
  });
321
310
  if (!res.ok) {
322
- const text = await res.text();
323
- throw new Error(`run signal failed: ${res.status} ${text}`);
311
+ const text = yield* Effect.promise(() => res.text());
312
+ return yield* new LoomsStoreError(`Run signal failed: ${res.status} ${text}`);
324
313
  }
325
314
  if (!live) {
326
- await sync();
315
+ yield* Effect.promise(sync);
327
316
  }
328
317
  notify.flush();
329
- },
318
+ return undefined;
319
+ })),
330
320
  dispose: () => {
331
321
  disposed = true;
332
322
  notify.cancel();
@@ -0,0 +1,16 @@
1
+ import { type ComposedRegistry } from '@swirls/looms/core';
2
+ import type { RunIngress } from './ingress';
3
+ import { type RuntimeObserver } from './observer';
4
+ import type { RuntimeRepository } from './repository';
5
+ import type { RunExecutionContexts } from './run-execution-context';
6
+ import type { EffectWorker, LoomsRuntime } from './types';
7
+ export interface CancellationOptions {
8
+ readonly registry: ComposedRegistry;
9
+ readonly repository: RuntimeRepository;
10
+ readonly executionContexts: RunExecutionContexts;
11
+ readonly worker?: EffectWorker;
12
+ readonly observer?: RuntimeObserver;
13
+ readonly wake: LoomsRuntime['wake'];
14
+ readonly withRunIngress: RunIngress;
15
+ }
16
+ export declare function createCancellation(options: CancellationOptions): LoomsRuntime['cancel'];
@@ -0,0 +1,129 @@
1
+ import { Clock, Effect } from 'effect';
2
+ import { createEvent, EventStoreTag, foldRun, } from '@swirls/looms/core';
3
+ import { RuntimeExecutionError } from './errors.js';
4
+ import { waitSatisfiedEvents, workerTask } from './helpers.js';
5
+ import { notifyObserver } from './observer.js';
6
+ import { isWorkerEffect } from './worker-effect-dispatch.js';
7
+ function descendantThreadIds(state, targetThreadId) {
8
+ return Object.values(state.threads)
9
+ .filter((record) => {
10
+ let parentThreadId = record.parentThreadId;
11
+ while (parentThreadId) {
12
+ if (parentThreadId === targetThreadId) {
13
+ return true;
14
+ }
15
+ parentThreadId = state.threads[parentThreadId]?.parentThreadId ?? null;
16
+ }
17
+ return false;
18
+ })
19
+ .map((record) => record.threadId);
20
+ }
21
+ /**
22
+ * Cancel a thread and its descendants. Worker attempts in flight are asked to stop and must
23
+ * acknowledge; attempts waiting to retry were never handed out and are cancelled outright.
24
+ */
25
+ function planCancellation(input) {
26
+ const { runId, state, registry, now } = input;
27
+ const targetThreadId = input.threadId ?? state.rootThreadId ?? '';
28
+ const idempotencyKey = `cancel:${targetThreadId}`;
29
+ if (state.processedIdempotencyKeys.includes(idempotencyKey)) {
30
+ return { events: [], threadIds: new Set(), workerCancellations: [] };
31
+ }
32
+ const descendants = descendantThreadIds(state, targetThreadId);
33
+ const threadIds = new Set([...descendants, targetThreadId]);
34
+ const workerCancellations = [];
35
+ const effectEvents = [];
36
+ for (const item of state.outstandingEffects) {
37
+ const execution = state.effectExecutions[item.effectId];
38
+ if (!execution ||
39
+ !threadIds.has(item.threadId) ||
40
+ !isWorkerEffect({ runId, item, execution, registry, worker: input.worker })) {
41
+ continue;
42
+ }
43
+ if (execution.status === 'retry_wait') {
44
+ effectEvents.push(createEvent(runId, {
45
+ type: 'runtime.effect.cancelled',
46
+ payload: { effectId: item.effectId, attempt: execution.attempt },
47
+ threadId: item.threadId,
48
+ effectId: item.effectId,
49
+ origin: { type: 'system' },
50
+ idempotencyKey,
51
+ }));
52
+ continue;
53
+ }
54
+ const timeoutMs = registry.effects.get(item.effect.type)?.retry?.cancellationTimeoutMs;
55
+ workerCancellations.push({ item, execution });
56
+ effectEvents.push(createEvent(runId, {
57
+ type: 'runtime.effect.cancel.requested',
58
+ payload: {
59
+ effectId: item.effectId,
60
+ attempt: execution.attempt,
61
+ deadlineAt: timeoutMs ? now + timeoutMs : null,
62
+ },
63
+ threadId: item.threadId,
64
+ origin: { type: 'system' },
65
+ idempotencyKey,
66
+ }));
67
+ }
68
+ const threadEvents = [
69
+ ...descendants.map((_, index) => descendants[descendants.length - 1 - index]),
70
+ targetThreadId,
71
+ ]
72
+ .filter((threadId) => threadId.length > 0)
73
+ .map((threadId) => createEvent(runId, {
74
+ type: 'runtime.thread.cancelled',
75
+ payload: { threadId, reason: 'cancelled' },
76
+ threadId,
77
+ origin: { type: 'external' },
78
+ idempotencyKey,
79
+ }));
80
+ const batch = [...effectEvents, ...threadEvents];
81
+ const folded = foldRun(batch, registry, { runId, initial: state });
82
+ const satisfied = waitSatisfiedEvents(folded, batch).map((event) => createEvent(runId, event));
83
+ return { events: [...batch, ...satisfied], threadIds, workerCancellations };
84
+ }
85
+ export function createCancellation(options) {
86
+ const { registry, repository, executionContexts, worker } = options;
87
+ return (runId, threadId) => Effect.gen(function* () {
88
+ const committed = yield* options.withRunIngress(runId, Effect.gen(function* () {
89
+ const store = yield* EventStoreTag;
90
+ const now = yield* Clock.currentTimeMillis;
91
+ return yield* repository.commit(store, runId, (current) => {
92
+ const plan = planCancellation({
93
+ runId,
94
+ state: current.state,
95
+ threadId,
96
+ registry,
97
+ worker,
98
+ now,
99
+ });
100
+ return Effect.succeed({
101
+ events: plan.events,
102
+ value: { plan, now },
103
+ idempotencyKey: `signal:cancel:${threadId ?? current.state.rootThreadId ?? ''}`,
104
+ });
105
+ });
106
+ }));
107
+ const { plan, now } = committed.value;
108
+ if (committed.append) {
109
+ executionContexts.abort(runId, plan.threadIds, 'Effect cancelled');
110
+ for (const { item, execution } of plan.workerCancellations) {
111
+ notifyObserver(options.observer, {
112
+ type: 'effect.cancellation',
113
+ runId,
114
+ at: now,
115
+ effectId: item.effectId,
116
+ attempt: execution.attempt,
117
+ });
118
+ }
119
+ }
120
+ const cancelledState = yield* options.wake(runId);
121
+ if (worker?.cancel && committed.append) {
122
+ yield* Effect.forEach(plan.workerCancellations, ({ item, execution }) => Effect.tryPromise({
123
+ try: () => Promise.resolve(worker.cancel?.(workerTask(runId, item, execution.attempt))),
124
+ catch: (cause) => new RuntimeExecutionError(cause instanceof Error ? cause.message : String(cause)),
125
+ }).pipe(Effect.ignore), { concurrency: 'unbounded', discard: true });
126
+ }
127
+ return cancelledState;
128
+ });
129
+ }
@@ -0,0 +1,31 @@
1
+ import { type ComposedRegistry, type EventEnvelope, type RunState } from '@swirls/looms/core';
2
+ export interface TimerLateNotice {
3
+ readonly timerId: string;
4
+ readonly latenessMs: number;
5
+ }
6
+ export interface EffectTimeoutNotice {
7
+ readonly effectId: string;
8
+ readonly attempt: number;
9
+ readonly timeout: 'schedule-to-start' | 'heartbeat' | 'start-to-close';
10
+ }
11
+ export interface DeadlineTransitionPlan {
12
+ readonly events: ReadonlyArray<EventEnvelope>;
13
+ readonly timerNotices: ReadonlyArray<TimerLateNotice>;
14
+ readonly timeoutNotices: ReadonlyArray<EffectTimeoutNotice>;
15
+ }
16
+ export declare function planDeadlineTransitions(input: {
17
+ readonly runId: string;
18
+ readonly state: RunState;
19
+ readonly registry: ComposedRegistry;
20
+ readonly now: number;
21
+ }): DeadlineTransitionPlan;
22
+ export declare function planTerminalCancellationTransitions(input: {
23
+ readonly runId: string;
24
+ readonly state: RunState;
25
+ readonly now: number;
26
+ }): ReadonlyArray<EventEnvelope>;
27
+ /**
28
+ * The earliest deadline after `after` that needs a wake. A terminal run only waits on
29
+ * unacknowledged worker cancellations; its leftover timers can never fire anything.
30
+ */
31
+ export declare function nextRuntimeDeadline(state: RunState, after?: number): number | undefined;
@@ -0,0 +1,139 @@
1
+ import { createEvent, foldRun, isRunTerminal, isWaitOnTimer, } from '@swirls/looms/core';
2
+ import { waitSatisfiedEvents } from './helpers.js';
3
+ import { planAttemptFailure } from './retry-policy.js';
4
+ const EMPTY_PLAN = {
5
+ events: [],
6
+ timerNotices: [],
7
+ timeoutNotices: [],
8
+ };
9
+ export function planDeadlineTransitions(input) {
10
+ const timerPlan = planDueTimers(input);
11
+ if (timerPlan.events.length > 0) {
12
+ return timerPlan;
13
+ }
14
+ return planExpiredExecutions(input);
15
+ }
16
+ function planDueTimers(input) {
17
+ const due = Object.values(input.state.waits).filter((record) => isWaitOnTimer(record.on) && record.on.timerAt <= input.now + 5);
18
+ if (due.length === 0) {
19
+ return EMPTY_PLAN;
20
+ }
21
+ const fired = due.flatMap((record) => isWaitOnTimer(record.on)
22
+ ? [
23
+ createEvent(input.runId, {
24
+ type: 'runtime.timer.fired',
25
+ payload: { timerId: record.waitId, waitId: record.waitId },
26
+ threadId: record.threadId,
27
+ origin: { type: 'system' },
28
+ }),
29
+ ]
30
+ : []);
31
+ const stateAfterFired = foldRun(fired, input.registry, {
32
+ runId: input.runId,
33
+ initial: input.state,
34
+ });
35
+ const satisfied = waitSatisfiedEvents(stateAfterFired, fired).map((event) => createEvent(input.runId, event));
36
+ return {
37
+ events: [...fired, ...satisfied],
38
+ timerNotices: due.flatMap((record) => isWaitOnTimer(record.on)
39
+ ? [{ timerId: record.waitId, latenessMs: Math.max(0, input.now - record.on.timerAt) }]
40
+ : []),
41
+ timeoutNotices: [],
42
+ };
43
+ }
44
+ function planExpiredExecutions(input) {
45
+ const expired = Object.values(input.state.effectExecutions).filter((execution) => (execution.status === 'dispatched' ||
46
+ execution.status === 'started' ||
47
+ execution.status === 'heartbeat' ||
48
+ execution.status === 'cancel_requested') &&
49
+ execution.deadlineAt !== null &&
50
+ execution.deadlineAt <= input.now);
51
+ if (expired.length === 0) {
52
+ return EMPTY_PLAN;
53
+ }
54
+ const events = [];
55
+ const timeoutNotices = [];
56
+ for (const execution of expired) {
57
+ const item = input.state.outstandingEffects.find((candidate) => candidate.effectId === execution.effectId);
58
+ if (!item) {
59
+ if (execution.status === 'cancel_requested') {
60
+ events.push(cancellationAmbiguous(input.runId, execution.effectId, execution.attempt));
61
+ }
62
+ continue;
63
+ }
64
+ const retry = input.registry.effects.get(item.effect.type)?.retry;
65
+ const timeout = timeoutKind(execution, retry, input.now);
66
+ timeoutNotices.push({
67
+ effectId: item.effectId,
68
+ attempt: execution.attempt,
69
+ timeout,
70
+ });
71
+ events.push(createEvent(input.runId, {
72
+ type: 'runtime.effect.timed_out',
73
+ payload: { effectId: item.effectId, attempt: execution.attempt, timeout },
74
+ threadId: item.threadId,
75
+ causationId: item.causingEventId,
76
+ origin: { type: 'system' },
77
+ }));
78
+ events.push(planAttemptFailure({
79
+ runId: input.runId,
80
+ item,
81
+ attempt: execution.attempt,
82
+ error: `${timeout} timeout`,
83
+ now: input.now,
84
+ retry,
85
+ origin: { type: 'system' },
86
+ }).event);
87
+ }
88
+ return { events, timerNotices: [], timeoutNotices };
89
+ }
90
+ function timeoutKind(execution, retry, now) {
91
+ if (execution.status === 'dispatched') {
92
+ return 'schedule-to-start';
93
+ }
94
+ const startToCloseAt = execution.startedAt !== null && retry?.startToCloseTimeoutMs
95
+ ? execution.startedAt + retry.startToCloseTimeoutMs
96
+ : null;
97
+ return execution.lastHeartbeatAt !== null && (startToCloseAt === null || startToCloseAt > now)
98
+ ? 'heartbeat'
99
+ : 'start-to-close';
100
+ }
101
+ export function planTerminalCancellationTransitions(input) {
102
+ if (!isRunTerminal(input.state)) {
103
+ return [];
104
+ }
105
+ return Object.values(input.state.effectExecutions)
106
+ .filter((execution) => execution.status === 'cancel_requested' &&
107
+ execution.deadlineAt !== null &&
108
+ execution.deadlineAt <= input.now)
109
+ .map((execution) => cancellationAmbiguous(input.runId, execution.effectId, execution.attempt));
110
+ }
111
+ /**
112
+ * The earliest deadline after `after` that needs a wake. A terminal run only waits on
113
+ * unacknowledged worker cancellations; its leftover timers can never fire anything.
114
+ */
115
+ export function nextRuntimeDeadline(state, after = Number.NEGATIVE_INFINITY) {
116
+ const terminal = isRunTerminal(state);
117
+ const timers = terminal
118
+ ? []
119
+ : Object.values(state.waits).flatMap((record) => isWaitOnTimer(record.on) && record.on.timerAt > after ? [record.on.timerAt] : []);
120
+ const executions = Object.values(state.effectExecutions).flatMap((execution) => (!terminal || execution.status === 'cancel_requested') &&
121
+ execution.deadlineAt !== null &&
122
+ execution.deadlineAt > after
123
+ ? [execution.deadlineAt]
124
+ : []);
125
+ const deadlines = [...timers, ...executions];
126
+ return deadlines.length > 0 ? Math.min(...deadlines) : undefined;
127
+ }
128
+ function cancellationAmbiguous(runId, effectId, attempt) {
129
+ return createEvent(runId, {
130
+ type: 'runtime.effect.ambiguous',
131
+ payload: {
132
+ effectId,
133
+ attempt,
134
+ error: 'worker did not acknowledge cancellation',
135
+ },
136
+ threadId: null,
137
+ origin: { type: 'system' },
138
+ });
139
+ }
@@ -1,3 +1,4 @@
1
- import { Effect, type Layer } from 'effect';
1
+ import type { Context, ManagedRuntime } from 'effect';
2
+ import { Effect } from 'effect';
2
3
  import { type ComposedRegistry, type EffectContext, type EventInput, type RegisteredDefinition, type RuntimeEffect } from '@swirls/looms/core';
3
- export declare function dispatchEffect(registry: ComposedRegistry, services: Layer.Layer<any>, definitions: ReadonlyMap<string, RegisteredDefinition>, effect: RuntimeEffect, ctx: EffectContext): Effect.Effect<ReadonlyArray<EventInput>, Error>;
4
+ export declare function dispatchEffect(registry: ComposedRegistry, services: ManagedRuntime.ManagedRuntime<Context.Service.Any, never>, definitions: ReadonlyMap<string, RegisteredDefinition>, effect: RuntimeEffect, ctx: EffectContext): Effect.Effect<ReadonlyArray<EventInput>, Error>;