@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
package/dist/s2/store.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { AppendInput, AppendRecord, S2, S2Endpoints, S2Error } from '@s2-dev/streamstore';
2
- import { Effect, Layer, Option, Predicate, Queue, Schema, Stream } from 'effect';
3
- import { createKeyedSerializer, EventStoreConflictError, EventStoreError, EventStoreFencedError, EventStoreTag, EventEnvelopeSchema, fromWireEvent, withSnapshotStore, } from '@swirls/looms/core';
2
+ import { DateTime, Effect, Layer, Predicate, Schema, Stream } from 'effect';
3
+ import { createKeyedSerializer, assertEventByteLimits, EventStoreConflictError, EventStoreError, EventStoreFencedError, EventPayloadTooLargeError, EventStoreTag, EventEnvelopeSchema, fromWireEvent, withSnapshotStore, } from '@swirls/looms/core';
4
4
  import { S2ConfigSchema, streamNameForRun } from './config.js';
5
+ import { paginateS2Stream } from './pagination.js';
5
6
  import { s2SnapshotStore } from './snapshot-store.js';
6
7
  function toStoreError(cause, message) {
7
8
  if (cause instanceof EventStoreError) {
@@ -20,50 +21,8 @@ function isUnsatisfiableCause(cause) {
20
21
  const text = cause instanceof Error ? cause.message : String(cause);
21
22
  return text.includes('out of range') || text.includes('Range not satisfiable');
22
23
  }
23
- /**
24
- * Paginate an S2 stream lazily using Effect Streams.
25
- *
26
- * Pulls pages on demand until the stream tail is reached, an empty page is returned,
27
- * or the caller's optional limit is satisfied.
28
- */
29
- export function paginateS2Stream(fetchPage, options) {
30
- const fromSeq = options?.fromSeq ?? 0;
31
- const userLimit = options?.limit;
32
- const maxPageSize = options?.maxPageSize ?? 1000;
33
- if (userLimit !== undefined && userLimit <= 0) {
34
- return Stream.empty;
35
- }
36
- const baseStream = Stream.paginate({ cursor: fromSeq, remaining: userLimit }, (state) => Effect.gen(function* () {
37
- if (state.remaining !== undefined && state.remaining <= 0) {
38
- return [[], Option.none()];
39
- }
40
- const count = state.remaining !== undefined ? Math.min(maxPageSize, state.remaining) : maxPageSize;
41
- const page = yield* fetchPage(state.cursor, count);
42
- if (!page || page.records.length === 0) {
43
- return [[], Option.none()];
44
- }
45
- const records = page.records.filter((r) => r.seqNum >= fromSeq);
46
- const nextRemaining = state.remaining !== undefined ? state.remaining - records.length : undefined;
47
- const lastRecord = page.records[page.records.length - 1];
48
- if (!lastRecord || (nextRemaining !== undefined && nextRemaining <= 0)) {
49
- return [records, Option.none()];
50
- }
51
- const nextCursor = Math.max(state.cursor + 1, lastRecord.seqNum + 1);
52
- if (nextCursor <= state.cursor) {
53
- return [records, Option.none()];
54
- }
55
- // A byte-capped page is shorter than `count`. Do not trust `page.tail`
56
- // there — S2 may report the batch end, not the stream tail.
57
- const shortPage = page.records.length < count;
58
- const reachedTail = !shortPage && page.tail?.seqNum !== undefined && nextCursor >= page.tail.seqNum;
59
- if (reachedTail) {
60
- return [records, Option.none()];
61
- }
62
- return [records, Option.some({ cursor: nextCursor, remaining: nextRemaining })];
63
- }));
64
- return userLimit !== undefined ? baseStream.pipe(Stream.take(userLimit)) : baseStream;
65
- }
66
- export async function readAllPages(fetchPage, options) {
24
+ export { paginateS2Stream };
25
+ export function readAllPages(fetchPage, options) {
67
26
  const fetchPageEffect = (cursor, count) => Effect.tryPromise({
68
27
  try: () => fetchPage(cursor, count),
69
28
  catch: (cause) => toStoreError(cause, 'S2 page fetch failed'),
@@ -101,61 +60,49 @@ function createClient(config) {
101
60
  * Expected tail mismatches during `append` map to `EventStoreConflictError`.
102
61
  */
103
62
  export function s2(config) {
104
- const parsed = Schema.decodeUnknownSync(S2ConfigSchema)(config);
63
+ const parsed = Schema.decodeSync(S2ConfigSchema)(config);
105
64
  const client = createClient(parsed);
106
65
  const basin = client.basin(parsed.basin);
107
66
  const ensured = new Set();
108
67
  const appends = createKeyedSerializer();
109
68
  let basinEnsured = false;
110
- const ensureBasin = Effect.tryPromise({
111
- try: async () => {
112
- if (basinEnsured) {
113
- return;
114
- }
115
- try {
116
- await client.basins.create({ basin: parsed.basin });
117
- }
118
- catch (err) {
119
- // 409 = already exists
120
- if (!(err instanceof S2Error && err.status === 409)) {
121
- throw err;
122
- }
123
- }
69
+ const ensureBasin = Effect.suspend(() => {
70
+ if (basinEnsured) {
71
+ return Effect.void;
72
+ }
73
+ return Effect.tryPromise({
74
+ try: () => client.basins.create({ basin: parsed.basin }),
75
+ catch: (cause) => toStoreError(cause, `Failed to ensure basin ${parsed.basin}`),
76
+ }).pipe(Effect.catchIf((error) => error.cause instanceof S2Error && error.cause.status === 409, () => Effect.void), Effect.tap(() => Effect.sync(() => {
124
77
  basinEnsured = true;
125
- },
126
- catch: (cause) => toStoreError(cause, `Failed to ensure basin ${parsed.basin}`),
78
+ })), Effect.asVoid);
127
79
  });
128
80
  const ensureStream = (runId) => Effect.gen(function* () {
129
81
  yield* ensureBasin;
130
- return yield* Effect.tryPromise({
131
- try: async () => {
132
- const name = streamNameForRun(runId);
133
- if (ensured.has(name)) {
134
- return basin.stream(name);
135
- }
136
- try {
137
- await basin.streams.create({
138
- stream: name,
139
- config: parsed.streamConfig,
140
- });
141
- }
142
- catch (err) {
143
- // 409 = already exists
144
- if (!(err instanceof S2Error && err.status === 409)) {
145
- // Some local servers may not support create; try using the stream anyway.
146
- if (!(err instanceof S2Error && (err.status === 404 || err.status === 405))) {
147
- throw err;
148
- }
149
- }
150
- }
151
- ensured.add(name);
152
- return basin.stream(name);
153
- },
154
- catch: (cause) => toStoreError(cause, `Failed to open stream for ${runId}`),
155
- });
82
+ const name = streamNameForRun(runId);
83
+ if (!ensured.has(name)) {
84
+ yield* Effect.tryPromise({
85
+ try: () => basin.streams.create({
86
+ stream: name,
87
+ config: parsed.streamConfig,
88
+ }),
89
+ catch: (cause) => toStoreError(cause, `Failed to open stream for ${runId}`),
90
+ }).pipe(Effect.catchIf((error) => error.cause instanceof S2Error &&
91
+ (error.cause.status === 409 ||
92
+ error.cause.status === 404 ||
93
+ error.cause.status === 405), () => Effect.void));
94
+ ensured.add(name);
95
+ }
96
+ return basin.stream(name);
156
97
  });
157
98
  const service = {
158
99
  append: (runId, events, options) => Effect.gen(function* () {
100
+ yield* Effect.try({
101
+ try: () => assertEventByteLimits(runId, events),
102
+ catch: (cause) => cause instanceof EventPayloadTooLargeError
103
+ ? cause
104
+ : new EventStoreError('event size validation failed', cause),
105
+ });
159
106
  if (events.length === 0 && options?.fence === undefined) {
160
107
  const tail = yield* service.tail(runId);
161
108
  return { sequences: [], tail };
@@ -186,7 +133,7 @@ export function s2(config) {
186
133
  cause.status === 412 &&
187
134
  (message.includes('fenc') || message.includes('token'));
188
135
  if (isFence) {
189
- return yield* Effect.fail(new EventStoreFencedError(runId, options?.fencingToken ?? options?.fence ?? ''));
136
+ return yield* new EventStoreFencedError(runId, options?.fencingToken ?? options?.fence ?? '');
190
137
  }
191
138
  if (cause instanceof S2Error &&
192
139
  (cause.status === 409 ||
@@ -195,9 +142,9 @@ export function s2(config) {
195
142
  message.includes('match'))) {
196
143
  const tailCheck = yield* Effect.promise(() => stream.checkTail().catch(() => null));
197
144
  const actualTail = tailCheck?.tail?.seqNum ?? 0;
198
- return yield* Effect.fail(new EventStoreConflictError(runId, options?.expectedTail ?? 0, actualTail));
145
+ return yield* new EventStoreConflictError(runId, options?.expectedTail ?? 0, actualTail);
199
146
  }
200
- return yield* Effect.fail(toStoreError(cause, `Append failed for ${runId}`));
147
+ return yield* toStoreError(cause, `Append failed for ${runId}`);
201
148
  }
202
149
  const ack = appendResult.ack;
203
150
  const sequences = [];
@@ -213,23 +160,13 @@ export function s2(config) {
213
160
  return Stream.unwrap(Effect.gen(function* () {
214
161
  const stream = yield* ensureStream(runId);
215
162
  const fetchPage = (cursor, count) => Effect.tryPromise({
216
- try: async () => {
217
- try {
218
- return await stream.read({
219
- start: { from: { seqNum: cursor }, clamp: true },
220
- stop: { limits: { count } },
221
- ignoreCommandRecords: true,
222
- });
223
- }
224
- catch (cause) {
225
- if (isUnsatisfiableCause(cause)) {
226
- return null;
227
- }
228
- throw cause;
229
- }
230
- },
163
+ try: () => stream.read({
164
+ start: { from: { seqNum: cursor }, clamp: true },
165
+ stop: { limits: { count } },
166
+ ignoreCommandRecords: true,
167
+ }),
231
168
  catch: (cause) => toStoreError(cause, `Read failed for ${runId}`),
232
- });
169
+ }).pipe(Effect.catchIf((error) => isUnsatisfiableCause(error.cause), () => Effect.succeed(null)));
233
170
  const recordStream = paginateS2Stream(fetchPage, {
234
171
  fromSeq: s2From,
235
172
  limit: options?.limit,
@@ -246,31 +183,21 @@ export function s2(config) {
246
183
  const res = yield* Effect.tryPromise({
247
184
  try: () => stream.checkTail(),
248
185
  catch: (cause) => toStoreError(cause, `Tail failed for ${runId}`),
249
- }).pipe(Effect.catch((err) => err.cause instanceof S2Error && err.cause.status === 404
250
- ? Effect.succeed({ tail: { seqNum: 0, timestamp: new Date(0) } })
251
- : Effect.fail(err)));
186
+ }).pipe(Effect.catchIf((err) => err.cause instanceof S2Error && err.cause.status === 404, () => Effect.succeed({
187
+ tail: { seqNum: 0, timestamp: DateTime.toDateUtc(DateTime.makeUnsafe(0)) },
188
+ })));
252
189
  return res.tail.seqNum;
253
190
  }),
254
191
  bounds: (runId) => Effect.gen(function* () {
255
192
  const stream = yield* ensureStream(runId);
256
193
  const page = yield* Effect.tryPromise({
257
- try: async () => {
258
- try {
259
- return await stream.read({
260
- start: { from: { seqNum: 0 }, clamp: true },
261
- stop: { limits: { count: 1 } },
262
- ignoreCommandRecords: false,
263
- });
264
- }
265
- catch (cause) {
266
- if (isUnsatisfiableCause(cause)) {
267
- return null;
268
- }
269
- throw cause;
270
- }
271
- },
194
+ try: () => stream.read({
195
+ start: { from: { seqNum: 0 }, clamp: true },
196
+ stop: { limits: { count: 1 } },
197
+ ignoreCommandRecords: false,
198
+ }),
272
199
  catch: (cause) => toStoreError(cause, `Bounds failed for ${runId}`),
273
- });
200
+ }).pipe(Effect.catchIf((error) => isUnsatisfiableCause(error.cause), () => Effect.succeed(null)));
274
201
  const tail = page?.tail?.seqNum ?? 0;
275
202
  const first = page?.records[0];
276
203
  const head = first ? first.seqNum + 1 : tail + 1;
@@ -284,49 +211,28 @@ export function s2(config) {
284
211
  catch: (cause) => toStoreError(cause, `Trim failed for ${runId}`),
285
212
  });
286
213
  }),
287
- subscribe: (runId, options) => Stream.callback((queue) => Effect.callback((resume) => {
214
+ subscribe: (runId, options) => {
288
215
  const fromSeq = options?.fromSeq ?? 1;
289
216
  const s2From = Math.max(0, fromSeq - 1);
290
- let stopped = false;
291
- const run = async () => {
292
- try {
293
- const stream = await Effect.runPromise(ensureStream(runId));
294
- const session = await stream.readSession({
217
+ return Stream.unwrap(Effect.gen(function* () {
218
+ const stream = yield* ensureStream(runId);
219
+ const session = yield* Effect.tryPromise({
220
+ try: () => stream.readSession({
295
221
  start: { from: { seqNum: s2From }, clamp: true },
296
222
  ignoreCommandRecords: true,
297
- });
298
- for await (const record of session) {
299
- if (stopped) {
300
- break;
301
- }
302
- if (record.seqNum < s2From) {
303
- continue;
304
- }
305
- Queue.offerUnsafe(queue, parseEvent(record.body, record.seqNum));
306
- }
307
- Queue.endUnsafe(queue);
308
- resume(Effect.void);
309
- }
310
- catch (cause) {
311
- await Effect.runPromise(Queue.fail(queue, toStoreError(cause, `Subscribe failed for ${runId}`)));
312
- resume(Effect.void);
313
- }
314
- };
315
- void run();
316
- return Effect.sync(() => {
317
- stopped = true;
318
- });
319
- })),
320
- listRuns: () => Effect.tryPromise({
321
- try: async () => {
322
- const listed = await basin.streams.list({ prefix: 'runs/' });
323
- return listed.streams
324
- .map((s) => s.name)
325
- .filter((name) => name.startsWith('runs/'))
326
- .map((name) => name.slice('runs/'.length));
327
- },
223
+ }),
224
+ catch: (cause) => toStoreError(cause, `Subscribe failed for ${runId}`),
225
+ });
226
+ return Stream.fromAsyncIterable(session, (cause) => toStoreError(cause, `Subscribe failed for ${runId}`)).pipe(Stream.filter((record) => record.seqNum >= s2From), Stream.map((record) => parseEvent(record.body, record.seqNum)));
227
+ }));
228
+ },
229
+ listRuns: Effect.tryPromise({
230
+ try: () => basin.streams.list({ prefix: 'runs/' }),
328
231
  catch: (cause) => toStoreError(cause, 'listRuns failed'),
329
- }),
232
+ }).pipe(Effect.map((listed) => listed.streams
233
+ .map((stream) => stream.name)
234
+ .filter((name) => name.startsWith('runs/'))
235
+ .map((name) => name.slice('runs/'.length)))),
330
236
  };
331
237
  return withSnapshotStore(service, s2SnapshotStore(parsed));
332
238
  }
@@ -9,3 +9,18 @@ export interface TestRuntime {
9
9
  export declare function createTestRuntime(modules: readonly AnyRuntimeModule[]): Promise<TestRuntime>;
10
10
  export declare function assertReplayDeterministic(test: TestRuntime, runId: string): Promise<void>;
11
11
  export declare function moduleConformance(module: AnyRuntimeModule): string[];
12
+ export type EventStoreFaultOperation = 'append' | 'read' | 'tail' | 'bounds' | 'trim' | 'listRuns';
13
+ export interface EventStoreFaultContext {
14
+ readonly operation: EventStoreFaultOperation;
15
+ readonly runId: string | null;
16
+ readonly call: number;
17
+ }
18
+ export interface FaultInjectingEventStore {
19
+ readonly store: EventStore;
20
+ readonly calls: ReadonlyMap<EventStoreFaultOperation, number>;
21
+ }
22
+ /**
23
+ * Wraps an EventStore with deterministic delay/failure injection. Throw from
24
+ * `before` to fail a call, or await inside it to pause a race at an exact store boundary.
25
+ */
26
+ export declare function createFaultInjectingEventStore(source: EventStore, before: (context: EventStoreFaultContext) => void | Promise<void>): FaultInjectingEventStore;
@@ -1,5 +1,5 @@
1
1
  import { Effect, Predicate } from 'effect';
2
- import { composeModules, EventStoreTag, foldRun, makeMemoryEventStore, ModuleCompositionError, } from '@swirls/looms/core';
2
+ import { composeModules, EventStoreTag, foldRun, makeMemoryEventStore, ModuleCompositionError, EventStoreError, snapshotStoreOf, stringifyJson, withSnapshotStore, } from '@swirls/looms/core';
3
3
  import { createRuntime } from '@swirls/looms/runtime';
4
4
  function namedEffects(effects) {
5
5
  const listed = [];
@@ -15,22 +15,24 @@ function namedEffects(effects) {
15
15
  }
16
16
  return listed;
17
17
  }
18
- export async function createTestRuntime(modules) {
19
- const store = await Effect.runPromise(makeMemoryEventStore);
20
- const runtime = createRuntime({ modules, store });
21
- return {
22
- runtime,
23
- store,
24
- run: (effect) => Effect.runPromise(Effect.provideService(effect, EventStoreTag, store)),
25
- };
18
+ export function createTestRuntime(modules) {
19
+ return Effect.runPromise(makeMemoryEventStore.pipe(Effect.map((store) => {
20
+ const runtime = createRuntime({ modules, store });
21
+ return {
22
+ runtime,
23
+ store,
24
+ run: (effect) => Effect.runPromise(Effect.provideService(effect, EventStoreTag, store)),
25
+ };
26
+ })));
26
27
  }
27
- export async function assertReplayDeterministic(test, runId) {
28
- const events = await test.run(test.runtime.getEvents(runId));
29
- const first = foldRun(events, test.runtime.registry, { runId });
30
- const second = foldRun(events, test.runtime.registry, { runId });
31
- if (JSON.stringify(first) !== JSON.stringify(second)) {
32
- throw new Error(`Replay is not deterministic for run ${runId}`);
33
- }
28
+ export function assertReplayDeterministic(test, runId) {
29
+ return test.run(test.runtime.getEvents(runId)).then((events) => {
30
+ const first = foldRun(events, test.runtime.registry, { runId });
31
+ const second = foldRun(events, test.runtime.registry, { runId });
32
+ if (stringifyJson(first) !== stringifyJson(second)) {
33
+ throw new Error(`Replay is not deterministic for run ${runId}`);
34
+ }
35
+ });
34
36
  }
35
37
  export function moduleConformance(module) {
36
38
  const errors = [];
@@ -61,3 +63,38 @@ export function moduleConformance(module) {
61
63
  }
62
64
  return errors;
63
65
  }
66
+ /**
67
+ * Wraps an EventStore with deterministic delay/failure injection. Throw from
68
+ * `before` to fail a call, or await inside it to pause a race at an exact store boundary.
69
+ */
70
+ export function createFaultInjectingEventStore(source, before) {
71
+ const calls = new Map();
72
+ const inject = (operation, runId) => Effect.tryPromise({
73
+ try: () => {
74
+ const call = (calls.get(operation) ?? 0) + 1;
75
+ calls.set(operation, call);
76
+ return Promise.resolve(before({ operation, runId, call }));
77
+ },
78
+ catch: (cause) => cause instanceof EventStoreError
79
+ ? cause
80
+ : new EventStoreError(`Injected ${operation} fault`, cause),
81
+ });
82
+ const store = {
83
+ append: (runId, events, options) => inject('append', runId).pipe(Effect.andThen(source.append(runId, events, options))),
84
+ read: (runId, options) => inject('read', runId).pipe(Effect.andThen(source.read(runId, options))),
85
+ tail: (runId) => inject('tail', runId).pipe(Effect.andThen(source.tail(runId))),
86
+ subscribe: source.subscribe,
87
+ listRuns: inject('listRuns', null).pipe(Effect.andThen(source.listRuns)),
88
+ };
89
+ if (source.bounds) {
90
+ store.bounds = (runId) => inject('bounds', runId).pipe(Effect.andThen(source.bounds(runId)));
91
+ }
92
+ if (source.trim) {
93
+ store.trim = (runId, beforeSeq) => inject('trim', runId).pipe(Effect.andThen(source.trim(runId, beforeSeq)));
94
+ }
95
+ const snapshots = snapshotStoreOf(source);
96
+ if (snapshots) {
97
+ withSnapshotStore(store, snapshots);
98
+ }
99
+ return { store, calls };
100
+ }
@@ -0,0 +1,4 @@
1
+ import type { DebuggerPlugin } from '@swirls/looms/debugger';
2
+ import { WorkflowNodesView } from './nodes-view';
3
+ export declare const workflowDebugger: DebuggerPlugin;
4
+ export { WorkflowNodesView };
@@ -0,0 +1,11 @@
1
+ import { nodes } from '../projections.js';
2
+ import { WorkflowNodesView } from './nodes-view.js';
3
+ import { summarizeWorkflowEvent } from './summarize.js';
4
+ export const workflowDebugger = {
5
+ name: 'workflow',
6
+ families: [
7
+ { family: 'workflow', color: 'oklch(0.76 0.12 300)', summarize: summarizeWorkflowEvent },
8
+ ],
9
+ projections: [{ name: nodes.name, component: WorkflowNodesView }],
10
+ };
11
+ export { WorkflowNodesView };
@@ -0,0 +1,3 @@
1
+ export declare function WorkflowNodesView({ runId }: {
2
+ runId: string;
3
+ }): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { asJson } from '@swirls/looms/core';
3
+ import { JsonTree, StatusBadge } from '@swirls/looms/debugger';
4
+ import { useProjection, useRunStore } from '@swirls/looms/react';
5
+ import { nodes } from '../projections.js';
6
+ export function WorkflowNodesView({ runId }) {
7
+ const state = useProjection(useRunStore(runId), nodes);
8
+ const entries = Object.entries(state.nodes);
9
+ if (entries.length === 0) {
10
+ return _jsx("p", { className: "text-muted-foreground text-xs", children: "No nodes yet." });
11
+ }
12
+ return (_jsx("ul", { className: "grid gap-2", children: entries.map(([id, node]) => (_jsxs("li", { className: "border-border bg-card/60 rounded-lg border p-2.5 text-xs", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "min-w-0 flex-1 truncate font-mono font-medium", children: id }), _jsx(StatusBadge, { status: node.status })] }), node.result !== null ? (_jsx(JsonTree, { value: asJson(node.result), className: "mt-1.5" })) : null, node.error ? _jsx("p", { className: "text-status-failed mt-1.5", children: node.error }) : null] }, id))) }));
13
+ }
@@ -0,0 +1,2 @@
1
+ import { type DebuggerEvent, type EventSummary } from '@swirls/looms/debugger';
2
+ export declare function summarizeWorkflowEvent(event: DebuggerEvent): EventSummary | undefined;
@@ -0,0 +1,30 @@
1
+ import { compactJson, jsonFields, jsonText, } from '@swirls/looms/debugger';
2
+ export function summarizeWorkflowEvent(event) {
3
+ const payload = jsonFields(event.payload);
4
+ switch (event.type) {
5
+ case 'workflow.node.started':
6
+ return { title: 'node started', detail: jsonText(payload.nodeId) };
7
+ case 'workflow.node.finished':
8
+ return {
9
+ title: jsonText(payload.error) ? 'node failed' : 'node finished',
10
+ detail: jsonText(payload.error) ??
11
+ `${jsonText(payload.nodeId) ?? ''} ${compactJson(payload.result ?? null)}`,
12
+ };
13
+ case 'workflow.node.skipped':
14
+ return {
15
+ title: 'node skipped',
16
+ detail: `${jsonText(payload.nodeId) ?? ''} · ${jsonText(payload.reason) ?? ''}`,
17
+ };
18
+ case 'workflow.spawn.requested':
19
+ return {
20
+ title: 'workflow spawn',
21
+ detail: `${jsonText(payload.definitionName) ?? 'unknown'} from ${jsonText(payload.nodeId) ?? ''}`,
22
+ };
23
+ case 'workflow.sleep.requested':
24
+ return { title: 'sleep', detail: jsonText(payload.nodeId) };
25
+ case 'workflow.effects.requested':
26
+ return { title: 'workflow effects', detail: jsonText(payload.nodeId) };
27
+ default:
28
+ return undefined;
29
+ }
30
+ }
@@ -1,8 +1,7 @@
1
1
  import { Context, Layer } from 'effect';
2
+ import { type DefinitionStore } from '@swirls/looms/core';
2
3
  import type { WorkflowDefinition } from './definitions';
3
- export interface WorkflowDefinitionStore {
4
- get(name: string): WorkflowDefinition | undefined;
5
- }
4
+ export type WorkflowDefinitionStore = DefinitionStore<WorkflowDefinition>;
6
5
  declare const WorkflowDefinitionsTag_base: Context.ServiceClass<WorkflowDefinitionsTag, "looms/WorkflowDefinitions", WorkflowDefinitionStore>;
7
6
  export declare class WorkflowDefinitionsTag extends WorkflowDefinitionsTag_base {
8
7
  }
@@ -1,8 +1,8 @@
1
1
  import { Context, Layer } from 'effect';
2
+ import { makeDefinitionStore } from '@swirls/looms/core';
2
3
  export class WorkflowDefinitionsTag extends Context.Service()('looms/WorkflowDefinitions') {
3
4
  }
4
5
  export function makeWorkflowDefinitionStore(definitions) {
5
- const map = new Map(definitions.map((def) => [def.name, def]));
6
- return { get: (name) => map.get(name) };
6
+ return makeDefinitionStore('workflow', definitions);
7
7
  }
8
8
  export const WorkflowDefinitionsLive = (definitions) => Layer.succeed(WorkflowDefinitionsTag, makeWorkflowDefinitionStore(definitions));