@swirls/looms 0.0.1

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 (270) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +9 -0
  3. package/dist/actor/cell.d.ts +27 -0
  4. package/dist/actor/cell.js +41 -0
  5. package/dist/actor/host.d.ts +27 -0
  6. package/dist/actor/host.js +82 -0
  7. package/dist/actor/index.d.ts +3 -0
  8. package/dist/actor/index.js +3 -0
  9. package/dist/actor/routing.d.ts +15 -0
  10. package/dist/actor/routing.js +80 -0
  11. package/dist/agent/definitions-store.d.ts +11 -0
  12. package/dist/agent/definitions-store.js +10 -0
  13. package/dist/agent/definitions.d.ts +152 -0
  14. package/dist/agent/definitions.js +74 -0
  15. package/dist/agent/effects.d.ts +425 -0
  16. package/dist/agent/effects.js +318 -0
  17. package/dist/agent/events.d.ts +170 -0
  18. package/dist/agent/events.js +61 -0
  19. package/dist/agent/index.d.ts +13 -0
  20. package/dist/agent/index.js +13 -0
  21. package/dist/agent/llm.d.ts +35 -0
  22. package/dist/agent/llm.js +48 -0
  23. package/dist/agent/module.d.ts +1037 -0
  24. package/dist/agent/module.js +27 -0
  25. package/dist/agent/projections.d.ts +17 -0
  26. package/dist/agent/projections.js +81 -0
  27. package/dist/agent/scope.d.ts +171 -0
  28. package/dist/agent/scope.js +7 -0
  29. package/dist/agent/signals.d.ts +17 -0
  30. package/dist/agent/signals.js +19 -0
  31. package/dist/agent/stop-when.d.ts +12 -0
  32. package/dist/agent/stop-when.js +6 -0
  33. package/dist/agent/threads.d.ts +34 -0
  34. package/dist/agent/threads.js +407 -0
  35. package/dist/agent/tool-schema.d.ts +21 -0
  36. package/dist/agent/tool-schema.js +67 -0
  37. package/dist/agent/types.d.ts +87 -0
  38. package/dist/agent/types.js +32 -0
  39. package/dist/ai-vercel/index.d.ts +2 -0
  40. package/dist/ai-vercel/index.js +2 -0
  41. package/dist/ai-vercel/messages.d.ts +9 -0
  42. package/dist/ai-vercel/messages.js +68 -0
  43. package/dist/ai-vercel/vercel-llm.d.ts +8 -0
  44. package/dist/ai-vercel/vercel-llm.js +98 -0
  45. package/dist/approval/effects.d.ts +133 -0
  46. package/dist/approval/effects.js +13 -0
  47. package/dist/approval/events.d.ts +52 -0
  48. package/dist/approval/events.js +28 -0
  49. package/dist/approval/index.d.ts +6 -0
  50. package/dist/approval/index.js +6 -0
  51. package/dist/approval/module.d.ts +342 -0
  52. package/dist/approval/module.js +10 -0
  53. package/dist/approval/projections.d.ts +42 -0
  54. package/dist/approval/projections.js +57 -0
  55. package/dist/approval/scope.d.ts +45 -0
  56. package/dist/approval/scope.js +7 -0
  57. package/dist/approval/signals.d.ts +19 -0
  58. package/dist/approval/signals.js +34 -0
  59. package/dist/cli/cli.d.ts +2 -0
  60. package/dist/cli/cli.js +6 -0
  61. package/dist/cli/commands.d.ts +13 -0
  62. package/dist/cli/commands.js +133 -0
  63. package/dist/cli/index.d.ts +1 -0
  64. package/dist/cli/index.js +1 -0
  65. package/dist/client/client.d.ts +69 -0
  66. package/dist/client/client.js +179 -0
  67. package/dist/client/index.d.ts +2 -0
  68. package/dist/client/index.js +2 -0
  69. package/dist/client/sse.d.ts +10 -0
  70. package/dist/client/sse.js +103 -0
  71. package/dist/cloudflare/alarm-scheduler.d.ts +9 -0
  72. package/dist/cloudflare/alarm-scheduler.js +13 -0
  73. package/dist/cloudflare/durable-object.d.ts +24 -0
  74. package/dist/cloudflare/durable-object.js +43 -0
  75. package/dist/cloudflare/event-store.d.ts +10 -0
  76. package/dist/cloudflare/event-store.js +18 -0
  77. package/dist/cloudflare/index.d.ts +4 -0
  78. package/dist/cloudflare/index.js +4 -0
  79. package/dist/cloudflare/router.d.ts +12 -0
  80. package/dist/cloudflare/router.js +26 -0
  81. package/dist/core/bun-sqlite.d.ts +13 -0
  82. package/dist/core/bun-sqlite.js +19 -0
  83. package/dist/core/catalog.d.ts +49 -0
  84. package/dist/core/catalog.js +39 -0
  85. package/dist/core/coalesce.d.ts +21 -0
  86. package/dist/core/coalesce.js +178 -0
  87. package/dist/core/compose.d.ts +59 -0
  88. package/dist/core/compose.js +97 -0
  89. package/dist/core/cursor.d.ts +16 -0
  90. package/dist/core/cursor.js +46 -0
  91. package/dist/core/effects.d.ts +110 -0
  92. package/dist/core/effects.js +99 -0
  93. package/dist/core/envelope.d.ts +79 -0
  94. package/dist/core/envelope.js +68 -0
  95. package/dist/core/event-codec.d.ts +8 -0
  96. package/dist/core/event-codec.js +82 -0
  97. package/dist/core/event-index.d.ts +28 -0
  98. package/dist/core/event-index.js +86 -0
  99. package/dist/core/fold.d.ts +19 -0
  100. package/dist/core/fold.js +362 -0
  101. package/dist/core/ids.d.ts +11 -0
  102. package/dist/core/ids.js +47 -0
  103. package/dist/core/index.d.ts +26 -0
  104. package/dist/core/index.js +25 -0
  105. package/dist/core/match.d.ts +8 -0
  106. package/dist/core/match.js +58 -0
  107. package/dist/core/module-scope.d.ts +99 -0
  108. package/dist/core/module-scope.js +72 -0
  109. package/dist/core/module.d.ts +74 -0
  110. package/dist/core/module.js +3 -0
  111. package/dist/core/projection.d.ts +83 -0
  112. package/dist/core/projection.js +241 -0
  113. package/dist/core/protocol.d.ts +99 -0
  114. package/dist/core/protocol.js +101 -0
  115. package/dist/core/replay.d.ts +13 -0
  116. package/dist/core/replay.js +34 -0
  117. package/dist/core/schema.d.ts +35 -0
  118. package/dist/core/schema.js +130 -0
  119. package/dist/core/serialize.d.ts +14 -0
  120. package/dist/core/serialize.js +42 -0
  121. package/dist/core/snapshot-store.d.ts +39 -0
  122. package/dist/core/snapshot-store.js +53 -0
  123. package/dist/core/snapshots.d.ts +26 -0
  124. package/dist/core/snapshots.js +100 -0
  125. package/dist/core/sqlite-store.d.ts +31 -0
  126. package/dist/core/sqlite-store.js +328 -0
  127. package/dist/core/state.d.ts +43 -0
  128. package/dist/core/state.js +31 -0
  129. package/dist/core/store.d.ts +146 -0
  130. package/dist/core/store.js +229 -0
  131. package/dist/core/thread.d.ts +41 -0
  132. package/dist/core/thread.js +13 -0
  133. package/dist/core/types.d.ts +27 -0
  134. package/dist/core/types.js +42 -0
  135. package/dist/core/validate-event.d.ts +19 -0
  136. package/dist/core/validate-event.js +42 -0
  137. package/dist/debugger/components/debugger-split.d.ts +23 -0
  138. package/dist/debugger/components/debugger-split.js +25 -0
  139. package/dist/debugger/components/event-inspector.d.ts +5 -0
  140. package/dist/debugger/components/event-inspector.js +38 -0
  141. package/dist/debugger/components/event-stream.d.ts +13 -0
  142. package/dist/debugger/components/event-stream.js +119 -0
  143. package/dist/debugger/components/json-view.d.ts +5 -0
  144. package/dist/debugger/components/json-view.js +5 -0
  145. package/dist/debugger/components/run-tree.d.ts +14 -0
  146. package/dist/debugger/components/run-tree.js +38 -0
  147. package/dist/debugger/components/status-dot.d.ts +3 -0
  148. package/dist/debugger/components/status-dot.js +5 -0
  149. package/dist/debugger/contracts.d.ts +22 -0
  150. package/dist/debugger/contracts.js +1 -0
  151. package/dist/debugger/events/catalog.d.ts +6 -0
  152. package/dist/debugger/events/catalog.js +27 -0
  153. package/dist/debugger/events/domain-summarize.d.ts +3 -0
  154. package/dist/debugger/events/domain-summarize.js +107 -0
  155. package/dist/debugger/events/family.d.ts +8 -0
  156. package/dist/debugger/events/family.js +44 -0
  157. package/dist/debugger/events/protocol-summarize.d.ts +3 -0
  158. package/dist/debugger/events/protocol-summarize.js +70 -0
  159. package/dist/debugger/index.d.ts +17 -0
  160. package/dist/debugger/index.js +16 -0
  161. package/dist/debugger/lib/cn.d.ts +5 -0
  162. package/dist/debugger/lib/cn.js +17 -0
  163. package/dist/debugger/lib/event-counts.d.ts +5 -0
  164. package/dist/debugger/lib/event-counts.js +23 -0
  165. package/dist/debugger/lib/event-meta.d.ts +8 -0
  166. package/dist/debugger/lib/event-meta.js +20 -0
  167. package/dist/debugger/lib/project-run.d.ts +9 -0
  168. package/dist/debugger/lib/project-run.js +14 -0
  169. package/dist/debugger/lib/status.d.ts +3 -0
  170. package/dist/debugger/lib/status.js +31 -0
  171. package/dist/debugger/lib/visible-events.d.ts +8 -0
  172. package/dist/debugger/lib/visible-events.js +21 -0
  173. package/dist/debugger/styles.css +71 -0
  174. package/dist/debugger/ui/checkbox.d.ts +4 -0
  175. package/dist/debugger/ui/checkbox.js +9 -0
  176. package/dist/debugger/ui/input.d.ts +3 -0
  177. package/dist/debugger/ui/input.js +7 -0
  178. package/dist/debugger/ui/scroll-area.d.ts +9 -0
  179. package/dist/debugger/ui/scroll-area.js +10 -0
  180. package/dist/debugger/ui/tabs.d.ts +7 -0
  181. package/dist/debugger/ui/tabs.js +17 -0
  182. package/dist/index.d.ts +2 -0
  183. package/dist/index.js +2 -0
  184. package/dist/projectors/index-model.d.ts +56 -0
  185. package/dist/projectors/index-model.js +117 -0
  186. package/dist/projectors/index.d.ts +4 -0
  187. package/dist/projectors/index.js +3 -0
  188. package/dist/projectors/memory.d.ts +5 -0
  189. package/dist/projectors/memory.js +77 -0
  190. package/dist/projectors/postgres.d.ts +9 -0
  191. package/dist/projectors/postgres.js +108 -0
  192. package/dist/projectors/projector.d.ts +11 -0
  193. package/dist/projectors/projector.js +1 -0
  194. package/dist/projectors/schema.d.ts +5 -0
  195. package/dist/projectors/schema.js +43 -0
  196. package/dist/projectors/sqlite.d.ts +9 -0
  197. package/dist/projectors/sqlite.js +103 -0
  198. package/dist/projectors/with-projectors.d.ts +17 -0
  199. package/dist/projectors/with-projectors.js +55 -0
  200. package/dist/react/derived.d.ts +19 -0
  201. package/dist/react/derived.js +50 -0
  202. package/dist/react/index.d.ts +6 -0
  203. package/dist/react/index.js +5 -0
  204. package/dist/react/materialize.d.ts +3 -0
  205. package/dist/react/materialize.js +86 -0
  206. package/dist/react/notify.d.ts +18 -0
  207. package/dist/react/notify.js +80 -0
  208. package/dist/react/react.d.ts +61 -0
  209. package/dist/react/react.js +187 -0
  210. package/dist/react/register.d.ts +31 -0
  211. package/dist/react/register.js +1 -0
  212. package/dist/react/sse.d.ts +1 -0
  213. package/dist/react/sse.js +1 -0
  214. package/dist/react/store.d.ts +56 -0
  215. package/dist/react/store.js +340 -0
  216. package/dist/react/tables.d.ts +21 -0
  217. package/dist/react/tables.js +6 -0
  218. package/dist/runtime/dispatch-effect.d.ts +3 -0
  219. package/dist/runtime/dispatch-effect.js +113 -0
  220. package/dist/runtime/events-api.d.ts +4 -0
  221. package/dist/runtime/events-api.js +84 -0
  222. package/dist/runtime/index.d.ts +5 -0
  223. package/dist/runtime/index.js +5 -0
  224. package/dist/runtime/looms.d.ts +74 -0
  225. package/dist/runtime/looms.js +109 -0
  226. package/dist/runtime/runtime-helpers.d.ts +51 -0
  227. package/dist/runtime/runtime-helpers.js +230 -0
  228. package/dist/runtime/runtime.d.ts +91 -0
  229. package/dist/runtime/runtime.js +531 -0
  230. package/dist/runtime/server.d.ts +17 -0
  231. package/dist/runtime/server.js +226 -0
  232. package/dist/runtime/sse.d.ts +15 -0
  233. package/dist/runtime/sse.js +96 -0
  234. package/dist/runtime/wake-scheduler.d.ts +8 -0
  235. package/dist/runtime/wake-scheduler.js +38 -0
  236. package/dist/s2/config.d.ts +27 -0
  237. package/dist/s2/config.js +54 -0
  238. package/dist/s2/index.d.ts +6 -0
  239. package/dist/s2/index.js +6 -0
  240. package/dist/s2/lite.d.ts +26 -0
  241. package/dist/s2/lite.js +150 -0
  242. package/dist/s2/projector.d.ts +8 -0
  243. package/dist/s2/projector.js +28 -0
  244. package/dist/s2/serialize.d.ts +1 -0
  245. package/dist/s2/serialize.js +1 -0
  246. package/dist/s2/snapshot-store.d.ts +17 -0
  247. package/dist/s2/snapshot-store.js +320 -0
  248. package/dist/s2/store.d.ts +35 -0
  249. package/dist/s2/store.js +337 -0
  250. package/dist/testing/index.d.ts +11 -0
  251. package/dist/testing/index.js +63 -0
  252. package/dist/workflow/definitions-store.d.ts +11 -0
  253. package/dist/workflow/definitions-store.js +8 -0
  254. package/dist/workflow/definitions.d.ts +82 -0
  255. package/dist/workflow/definitions.js +32 -0
  256. package/dist/workflow/effects.d.ts +305 -0
  257. package/dist/workflow/effects.js +208 -0
  258. package/dist/workflow/events.d.ts +60 -0
  259. package/dist/workflow/events.js +39 -0
  260. package/dist/workflow/index.d.ts +8 -0
  261. package/dist/workflow/index.js +8 -0
  262. package/dist/workflow/module.d.ts +926 -0
  263. package/dist/workflow/module.js +19 -0
  264. package/dist/workflow/projections.d.ts +30 -0
  265. package/dist/workflow/projections.js +43 -0
  266. package/dist/workflow/scope.d.ts +61 -0
  267. package/dist/workflow/scope.js +7 -0
  268. package/dist/workflow/threads.d.ts +171 -0
  269. package/dist/workflow/threads.js +287 -0
  270. package/package.json +183 -0
@@ -0,0 +1,362 @@
1
+ import { Predicate, Stream } from 'effect';
2
+ import { isWithdrawnError } from './effects.js';
3
+ import { createEffectId } from './ids.js';
4
+ import { emptyRunState } from './state.js';
5
+ import { isTerminalStatus } from './thread.js';
6
+ function payloadObject(event) {
7
+ const payload = event.payload;
8
+ if (payload === null || Array.isArray(payload)) {
9
+ return {};
10
+ }
11
+ if (Predicate.isReadonlyObject(payload)) {
12
+ return payload;
13
+ }
14
+ return {};
15
+ }
16
+ function readString(obj, key) {
17
+ const value = obj[key];
18
+ return Predicate.isString(value) ? value : undefined;
19
+ }
20
+ function cloneState(state) {
21
+ return {
22
+ runId: state.runId,
23
+ status: state.status,
24
+ rootThreadId: state.rootThreadId,
25
+ threads: { ...state.threads },
26
+ waits: { ...state.waits },
27
+ outstandingEffects: [...state.outstandingEffects],
28
+ completedEffectIds: state.completedEffectIds ? [...state.completedEffectIds] : [],
29
+ processedIdempotencyKeys: state.processedIdempotencyKeys
30
+ ? [...state.processedIdempotencyKeys]
31
+ : [],
32
+ };
33
+ }
34
+ function completeEffect(state, effectId) {
35
+ const completed = state.completedEffectIds ?? [];
36
+ return {
37
+ ...state,
38
+ outstandingEffects: state.outstandingEffects.filter((item) => item.effectId !== effectId),
39
+ completedEffectIds: completed.includes(effectId) ? completed : [...completed, effectId],
40
+ };
41
+ }
42
+ function putThread(state, record) {
43
+ return {
44
+ ...state,
45
+ threads: { ...state.threads, [record.threadId]: record },
46
+ };
47
+ }
48
+ function addWait(state, record) {
49
+ return {
50
+ ...state,
51
+ waits: { ...state.waits, [record.waitId]: record },
52
+ };
53
+ }
54
+ function removeWait(state, waitId) {
55
+ const { [waitId]: _removed, ...rest } = state.waits;
56
+ return { ...state, waits: rest };
57
+ }
58
+ function threadWaits(state, threadId) {
59
+ return Object.values(state.waits).filter((record) => record.threadId === threadId);
60
+ }
61
+ function dropThreadWork(state, threadId) {
62
+ const waits = {};
63
+ for (const [waitId, record] of Object.entries(state.waits)) {
64
+ if (record.threadId !== threadId) {
65
+ waits[waitId] = record;
66
+ }
67
+ }
68
+ return {
69
+ ...state,
70
+ waits,
71
+ outstandingEffects: state.outstandingEffects.filter((item) => item.threadId !== threadId),
72
+ };
73
+ }
74
+ function applyProtocol(state, event, registry) {
75
+ const payload = payloadObject(event);
76
+ switch (event.type) {
77
+ case 'runtime.run.started': {
78
+ const rootThreadId = readString(payload, 'rootThreadId') ?? null;
79
+ return { ...state, runId: event.runId, rootThreadId, status: 'running' };
80
+ }
81
+ case 'runtime.run.completed': {
82
+ const error = payload.error;
83
+ const status = Predicate.isString(error) && error.length > 0 ? 'failed' : 'completed';
84
+ return { ...state, status };
85
+ }
86
+ case 'runtime.thread.started': {
87
+ const threadId = readString(payload, 'threadId') ?? event.threadId;
88
+ const kind = readString(payload, 'kind');
89
+ const definitionName = readString(payload, 'definitionName');
90
+ if (!threadId || !kind || !definitionName) {
91
+ return state;
92
+ }
93
+ const parent = payload.parentThreadId;
94
+ const parentThreadId = parent === null || Predicate.isString(parent) ? parent : null;
95
+ const input = payload.input ?? null;
96
+ const definition = registry.threads.get(kind);
97
+ const record = {
98
+ threadId,
99
+ kind,
100
+ definitionName,
101
+ parentThreadId,
102
+ status: definition ? 'running' : 'failed',
103
+ input,
104
+ output: null,
105
+ error: definition ? null : `Unsupported thread kind: ${kind}`,
106
+ state: definition
107
+ ? definition.initialState({
108
+ runId: event.runId,
109
+ threadId,
110
+ parentThreadId,
111
+ definitionName,
112
+ input,
113
+ })
114
+ : {},
115
+ };
116
+ let next = putThread(state, record);
117
+ if (!next.rootThreadId) {
118
+ next = { ...next, rootThreadId: threadId };
119
+ }
120
+ return next;
121
+ }
122
+ case 'runtime.thread.completed': {
123
+ const threadId = readString(payload, 'threadId') ?? event.threadId;
124
+ if (!threadId) {
125
+ return state;
126
+ }
127
+ const existing = state.threads[threadId];
128
+ if (!existing) {
129
+ return state;
130
+ }
131
+ return putThread(state, {
132
+ ...existing,
133
+ status: 'completed',
134
+ output: payload.output ?? null,
135
+ error: null,
136
+ });
137
+ }
138
+ case 'runtime.thread.failed': {
139
+ const threadId = readString(payload, 'threadId') ?? event.threadId;
140
+ if (!threadId) {
141
+ return state;
142
+ }
143
+ const existing = state.threads[threadId];
144
+ if (!existing) {
145
+ return state;
146
+ }
147
+ return dropThreadWork(putThread(state, {
148
+ ...existing,
149
+ status: 'failed',
150
+ error: readString(payload, 'error') ?? 'failed',
151
+ }), threadId);
152
+ }
153
+ case 'runtime.thread.cancelled': {
154
+ const threadId = readString(payload, 'threadId') ?? event.threadId;
155
+ if (!threadId) {
156
+ return state;
157
+ }
158
+ const existing = state.threads[threadId];
159
+ if (!existing) {
160
+ return state;
161
+ }
162
+ return dropThreadWork(putThread(state, {
163
+ ...existing,
164
+ status: 'cancelled',
165
+ error: readString(payload, 'reason') ?? existing.error,
166
+ }), threadId);
167
+ }
168
+ case 'runtime.wait.registered': {
169
+ const waitId = readString(payload, 'waitId');
170
+ const threadId = readString(payload, 'threadId') ?? event.threadId;
171
+ const on = payload.on;
172
+ if (!waitId || !threadId || !on || !Predicate.isObject(on)) {
173
+ return state;
174
+ }
175
+ // SAFETY: wait.registered payload.on is a WaitCondition written by the runtime.
176
+ const record = {
177
+ waitId,
178
+ threadId,
179
+ on: on,
180
+ };
181
+ if (payload.tag !== undefined) {
182
+ record.tag = payload.tag;
183
+ }
184
+ let next = addWait(state, record);
185
+ const existing = next.threads[threadId];
186
+ if (existing && !isTerminalStatus(existing.status)) {
187
+ next = putThread(next, { ...existing, status: 'waiting' });
188
+ }
189
+ return next;
190
+ }
191
+ case 'runtime.wait.satisfied': {
192
+ const waitId = readString(payload, 'waitId');
193
+ if (!waitId) {
194
+ return state;
195
+ }
196
+ const existingWait = state.waits[waitId];
197
+ let next = removeWait(state, waitId);
198
+ const threadId = existingWait?.threadId ?? event.threadId;
199
+ if (!threadId) {
200
+ return next;
201
+ }
202
+ const existing = next.threads[threadId];
203
+ if (existing && existing.status === 'waiting' && threadWaits(next, threadId).length === 0) {
204
+ next = putThread(next, { ...existing, status: 'running' });
205
+ }
206
+ return next;
207
+ }
208
+ case 'runtime.snapshot.taken':
209
+ case 'runtime.timer.set':
210
+ case 'runtime.timer.fired':
211
+ case 'runtime.effect.failed':
212
+ case 'runtime.signal.received':
213
+ return state;
214
+ default:
215
+ return state;
216
+ }
217
+ }
218
+ function deriveEffectId(threadId, causingSeq, index, effect, existingId) {
219
+ if ('tag' in effect && Predicate.isString(effect.tag)) {
220
+ return createEffectId(threadId, effect.tag);
221
+ }
222
+ if ('waitId' in effect && Predicate.isString(effect.waitId)) {
223
+ return createEffectId(threadId, effect.waitId);
224
+ }
225
+ if ('childThreadId' in effect && Predicate.isString(effect.childThreadId)) {
226
+ return createEffectId(threadId, `spawn_${effect.childThreadId}`);
227
+ }
228
+ if (existingId) {
229
+ return existingId;
230
+ }
231
+ return createEffectId(threadId, causingSeq, index);
232
+ }
233
+ function mapEffects(threadId, event, effects, completed, existingForThread) {
234
+ const remainingExisting = [...existingForThread];
235
+ return effects
236
+ .map((effect, index) => {
237
+ const matchIndex = remainingExisting.findIndex((item) => {
238
+ if ('tag' in effect && Predicate.isString(effect.tag)) {
239
+ return item.effectId === createEffectId(threadId, effect.tag);
240
+ }
241
+ if ('waitId' in effect && Predicate.isString(effect.waitId)) {
242
+ return item.effectId === createEffectId(threadId, effect.waitId);
243
+ }
244
+ if ('childThreadId' in effect && Predicate.isString(effect.childThreadId)) {
245
+ return item.effectId === createEffectId(threadId, `spawn_${effect.childThreadId}`);
246
+ }
247
+ return item.effect.type === effect.type;
248
+ });
249
+ const existing = matchIndex >= 0 ? remainingExisting.splice(matchIndex, 1)[0] : undefined;
250
+ const effectId = deriveEffectId(threadId, event.seq, index, effect, existing?.effectId);
251
+ return (existing ?? {
252
+ effectId,
253
+ threadId,
254
+ causingSeq: event.seq,
255
+ causingEventId: event.id,
256
+ effect,
257
+ });
258
+ })
259
+ .filter((item) => !completed.includes(item.effectId));
260
+ }
261
+ /** Reconcile a thread's outstanding effects with enabled work from `effects()`. */
262
+ function reconcileEffects(state, threadId, event, effects) {
263
+ const record = state.threads[threadId];
264
+ if (record && isTerminalStatus(record.status)) {
265
+ return {
266
+ ...state,
267
+ outstandingEffects: state.outstandingEffects.filter((item) => item.threadId !== threadId),
268
+ };
269
+ }
270
+ const completed = state.completedEffectIds ?? [];
271
+ const existingForThread = state.outstandingEffects.filter((item) => item.threadId === threadId);
272
+ const mapped = mapEffects(threadId, event, effects, completed, existingForThread);
273
+ const otherThreadEffects = state.outstandingEffects.filter((item) => item.threadId !== threadId);
274
+ return {
275
+ ...state,
276
+ outstandingEffects: [...otherThreadEffects, ...mapped],
277
+ };
278
+ }
279
+ function deliver(state, event, registry) {
280
+ const threadId = event.threadId;
281
+ if (!threadId) {
282
+ return state;
283
+ }
284
+ const record = state.threads[threadId];
285
+ if (!record) {
286
+ return state;
287
+ }
288
+ const definition = registry.threads.get(record.kind);
289
+ if (!definition) {
290
+ return state;
291
+ }
292
+ const ctx = {
293
+ runId: state.runId,
294
+ threadId,
295
+ parentThreadId: record.parentThreadId,
296
+ };
297
+ const nextState = definition.step(record.state, event, ctx);
298
+ const enabled = definition.effects(nextState, ctx);
299
+ let next = putThread(state, { ...record, state: nextState });
300
+ next = reconcileEffects(next, threadId, event, enabled);
301
+ return next;
302
+ }
303
+ function failUnhandledEffect(state, event, effectIdsBeforeDeliver) {
304
+ if (event.type !== 'runtime.effect.failed') {
305
+ return state;
306
+ }
307
+ const payload = payloadObject(event);
308
+ const error = readString(payload, 'error') ?? 'effect failed';
309
+ if (isWithdrawnError(error)) {
310
+ return state;
311
+ }
312
+ const handled = state.outstandingEffects.some((item) => !effectIdsBeforeDeliver.has(item.effectId));
313
+ if (handled) {
314
+ return state;
315
+ }
316
+ const threadId = event.threadId;
317
+ if (!threadId) {
318
+ return state;
319
+ }
320
+ const existing = state.threads[threadId];
321
+ if (!existing || isTerminalStatus(existing.status)) {
322
+ return state;
323
+ }
324
+ return dropThreadWork(putThread(state, {
325
+ ...existing,
326
+ status: 'failed',
327
+ error,
328
+ }), threadId);
329
+ }
330
+ export function foldEvent(state, event, registry) {
331
+ if (event.ephemeral) {
332
+ return state;
333
+ }
334
+ let next = cloneState(state);
335
+ if (event.runId && next.runId !== event.runId) {
336
+ next = { ...next, runId: event.runId };
337
+ }
338
+ if (event.effectId) {
339
+ next = completeEffect(next, event.effectId);
340
+ }
341
+ if (event.idempotencyKey) {
342
+ const keys = next.processedIdempotencyKeys ?? [];
343
+ if (!keys.includes(event.idempotencyKey)) {
344
+ next = { ...next, processedIdempotencyKeys: [...keys, event.idempotencyKey] };
345
+ }
346
+ }
347
+ next = applyProtocol(next, event, registry);
348
+ const effectIdsBeforeDeliver = new Set(next.outstandingEffects.map((item) => item.effectId));
349
+ next = deliver(next, event, registry);
350
+ return failUnhandledEffect(next, event, effectIdsBeforeDeliver);
351
+ }
352
+ export function foldRun(events, registry, options) {
353
+ const base = options?.initial ? cloneState(options.initial) : emptyRunState(options?.runId ?? '');
354
+ return events.reduce((acc, event) => foldEvent(acc, event, registry), base);
355
+ }
356
+ /**
357
+ * Folds an Effect Stream of events into RunState without buffering all events into memory.
358
+ */
359
+ export function foldStream(stream, registry, options) {
360
+ const getBase = () => options?.initial ? cloneState(options.initial) : emptyRunState(options?.runId ?? '');
361
+ return Stream.runFold(stream, getBase, (acc, event) => foldEvent(acc, event, registry));
362
+ }
@@ -0,0 +1,11 @@
1
+ export declare function createEventId(): string;
2
+ export declare function createRunId(): string;
3
+ export declare function createThreadId(): string;
4
+ export declare function createWaitId(scope?: string, tag?: string | number): string;
5
+ export declare function createEffectId(threadId: string, causingSeqOrTag: number | string, index?: number): string;
6
+ export declare function parseEffectId(effectId: string): {
7
+ threadId: string;
8
+ causingSeq?: number;
9
+ tag?: string;
10
+ index?: number;
11
+ } | null;
@@ -0,0 +1,47 @@
1
+ import { Predicate } from 'effect';
2
+ let counter = 0;
3
+ function token() {
4
+ counter += 1;
5
+ return `${Date.now().toString(36)}_${counter.toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
6
+ }
7
+ export function createEventId() {
8
+ return `evt_${token()}`;
9
+ }
10
+ export function createRunId() {
11
+ return `run_${token()}`;
12
+ }
13
+ export function createThreadId() {
14
+ return `thr_${token()}`;
15
+ }
16
+ export function createWaitId(scope, tag) {
17
+ if (scope !== undefined && tag !== undefined) {
18
+ return `wait_${scope}_${tag}`;
19
+ }
20
+ if (scope !== undefined) {
21
+ return `wait_${scope}_${token()}`;
22
+ }
23
+ return `wait_${token()}`;
24
+ }
25
+ export function createEffectId(threadId, causingSeqOrTag, index) {
26
+ if (Predicate.isString(causingSeqOrTag)) {
27
+ return `${threadId}:${causingSeqOrTag}`;
28
+ }
29
+ return `${threadId}:${causingSeqOrTag}:${index ?? 0}`;
30
+ }
31
+ export function parseEffectId(effectId) {
32
+ const parts = effectId.split(':');
33
+ if (parts.length < 2) {
34
+ return null;
35
+ }
36
+ if (parts.length === 3) {
37
+ const seq = Number(parts[1]);
38
+ const idx = Number(parts[2]);
39
+ if (Number.isFinite(seq) && Number.isFinite(idx)) {
40
+ return { threadId: parts[0], causingSeq: seq, index: idx };
41
+ }
42
+ }
43
+ return {
44
+ threadId: parts[0],
45
+ tag: parts.slice(1).join(':'),
46
+ };
47
+ }
@@ -0,0 +1,26 @@
1
+ export type * from './types';
2
+ export { asJson, cleanUndefined, fromJsonStruct, isJsonNumber, isJsonObject, isJsonString, } from './types';
3
+ export * from './ids';
4
+ export * from './schema';
5
+ export * from './validate-event';
6
+ export * from './envelope';
7
+ export * from './catalog';
8
+ export * from './effects';
9
+ export * from './protocol';
10
+ export * from './thread';
11
+ export * from './state';
12
+ export * from './match';
13
+ export * from './fold';
14
+ export * from './replay';
15
+ export * from './projection';
16
+ export * from './module';
17
+ export * from './module-scope';
18
+ export * from './compose';
19
+ export * from './store';
20
+ export * from './cursor';
21
+ export * from './snapshot-store';
22
+ export * from './snapshots';
23
+ export * from './event-codec';
24
+ export * from './serialize';
25
+ export * from './coalesce';
26
+ export * from './event-index';
@@ -0,0 +1,25 @@
1
+ export { asJson, cleanUndefined, fromJsonStruct, isJsonNumber, isJsonObject, isJsonString, } from './types.js';
2
+ export * from './ids.js';
3
+ export * from './schema.js';
4
+ export * from './validate-event.js';
5
+ export * from './envelope.js';
6
+ export * from './catalog.js';
7
+ export * from './effects.js';
8
+ export * from './protocol.js';
9
+ export * from './thread.js';
10
+ export * from './state.js';
11
+ export * from './match.js';
12
+ export * from './fold.js';
13
+ export * from './replay.js';
14
+ export * from './projection.js';
15
+ export * from './module.js';
16
+ export * from './module-scope.js';
17
+ export * from './compose.js';
18
+ export * from './store.js';
19
+ export * from './cursor.js';
20
+ export * from './snapshot-store.js';
21
+ export * from './snapshots.js';
22
+ export * from './event-codec.js';
23
+ export * from './serialize.js';
24
+ export * from './coalesce.js';
25
+ export * from './event-index.js';
@@ -0,0 +1,8 @@
1
+ import { type WaitCondition } from './effects';
2
+ import type { EventEnvelope } from './envelope';
3
+ import type { JsonValue } from './types';
4
+ export declare function isSubset(match: JsonValue, payload: JsonValue): boolean;
5
+ export declare function matchesWait(event: EventEnvelope, on: WaitCondition): boolean;
6
+ export declare function matchingWaits<T extends {
7
+ on: WaitCondition;
8
+ }>(event: EventEnvelope, waits: readonly T[]): T[];
@@ -0,0 +1,58 @@
1
+ import { Predicate } from 'effect';
2
+ import { isWaitOnEvent, isWaitOnTimer } from './effects.js';
3
+ export function isSubset(match, payload) {
4
+ if (match === payload) {
5
+ return true;
6
+ }
7
+ if (Array.isArray(match)) {
8
+ if (!Array.isArray(payload)) {
9
+ return false;
10
+ }
11
+ if (match.length !== payload.length) {
12
+ return false;
13
+ }
14
+ return match.every((item, index) => isSubset(item, payload[index] ?? null));
15
+ }
16
+ if (Predicate.isReadonlyObject(match)) {
17
+ if (!Predicate.isObject(payload)) {
18
+ return false;
19
+ }
20
+ for (const key of Object.keys(match)) {
21
+ const expected = match[key];
22
+ if (expected === undefined) {
23
+ continue;
24
+ }
25
+ if (!(key in payload)) {
26
+ return false;
27
+ }
28
+ const actual = payload[key];
29
+ if (actual === undefined) {
30
+ return false;
31
+ }
32
+ if (!isSubset(expected, actual)) {
33
+ return false;
34
+ }
35
+ }
36
+ return true;
37
+ }
38
+ return false;
39
+ }
40
+ export function matchesWait(event, on) {
41
+ if (isWaitOnTimer(on)) {
42
+ return event.type === 'runtime.timer.fired';
43
+ }
44
+ if (!isWaitOnEvent(on)) {
45
+ return false;
46
+ }
47
+ const matchesType = Array.isArray(on.type) ? on.type.includes(event.type) : event.type === on.type;
48
+ if (!matchesType) {
49
+ return false;
50
+ }
51
+ if (on.match === undefined) {
52
+ return true;
53
+ }
54
+ return isSubset(on.match, event.payload);
55
+ }
56
+ export function matchingWaits(event, waits) {
57
+ return waits.filter((record) => matchesWait(event, record.on));
58
+ }
@@ -0,0 +1,99 @@
1
+ import type { Layer } from 'effect';
2
+ import { type CatalogEntries, type EventCatalog, type EventInputOf, type EventsOfCatalog, type InferPayload } from './catalog';
3
+ import type { EventsOf, ProtocolEvents } from './compose';
4
+ import { type EffectDefinition, type EffectHandlerResult, type EmitEffect, type RetryPolicy, type RuntimeEffect, type ScopedEffectContext } from './effects';
5
+ import type { EventEnvelope, EventInput } from './envelope';
6
+ import { type AnyRuntimeModule, type EffectMiddleware, type RuntimeModule, type DefinitionRef, type RuntimeModuleDependency } from './module';
7
+ import { type ProjectionDefinition } from './projection';
8
+ import type { InferDefinedSchema, InferSchemaOutput, SchemaInput } from './schema';
9
+ import { type StartContext, type ThreadContext, type ThreadDefinition } from './thread';
10
+ import { type JsonValue } from './types';
11
+ export interface DefineModuleOptions<TNamespace extends string, TEvents extends EventCatalog | undefined = undefined, TObserves extends readonly EventCatalog[] | undefined = undefined> {
12
+ readonly namespace: TNamespace;
13
+ readonly protocolVersion: string;
14
+ readonly events?: TEvents;
15
+ readonly observes?: TObserves;
16
+ readonly dependencies?: readonly RuntimeModuleDependency[];
17
+ }
18
+ export type ScopeUniverse<TEvents extends EventCatalog | undefined, TObserves extends readonly EventCatalog[] | undefined> = ProtocolEvents | (TEvents extends EventCatalog ? EventsOfCatalog<TEvents> : never) | (TObserves extends readonly EventCatalog[] ? EventsOfCatalog<TObserves[number]> : never);
19
+ export type EffectRequirementsOf<TEffects> = TEffects extends {
20
+ readonly [key: string]: EffectDefinition<any, any, any>;
21
+ } ? {
22
+ [K in keyof TEffects]: TEffects[K] extends EffectDefinition<any, infer R, any> ? R : never;
23
+ }[keyof TEffects] : never;
24
+ export type ModuleServicesConfig<TR> = [TR] extends [never] ? {
25
+ readonly services?: () => Layer.Layer<any>;
26
+ } : {
27
+ readonly services: () => Layer.Layer<TR>;
28
+ };
29
+ export interface ModuleScope<TNamespace extends string, U extends EventEnvelope<any, any>, TEvents extends EventCatalog | undefined, TObserves extends readonly EventCatalog[] | undefined = readonly EventCatalog[] | undefined> {
30
+ readonly namespace: TNamespace;
31
+ readonly protocolVersion: string;
32
+ readonly events?: TEvents;
33
+ readonly observes?: TObserves;
34
+ readonly dependencies?: readonly RuntimeModuleDependency[];
35
+ thread<TShape, TInputSchema extends SchemaInput | undefined = undefined>(def: {
36
+ readonly kind: string;
37
+ readonly shape: TShape;
38
+ readonly input?: TInputSchema;
39
+ initialState(ctx: StartContext<InferDefinedSchema<TInputSchema>>): InferSchemaOutput<TShape>;
40
+ step(state: InferSchemaOutput<TShape>, event: U, ctx: ThreadContext): InferSchemaOutput<TShape>;
41
+ effects?(state: InferSchemaOutput<TShape>, ctx: ThreadContext): RuntimeEffect[];
42
+ }): ThreadDefinition<InferSchemaOutput<TShape>>;
43
+ thread<S = JsonValue, TInputSchema extends SchemaInput | undefined = undefined>(def: {
44
+ readonly kind: string;
45
+ readonly shape?: unknown;
46
+ readonly input?: TInputSchema;
47
+ initialState(ctx: StartContext<InferDefinedSchema<TInputSchema>>): S;
48
+ step(state: S, event: U, ctx: ThreadContext): S;
49
+ effects?(state: S, ctx: ThreadContext): RuntimeEffect[];
50
+ }): ThreadDefinition<S>;
51
+ projection<TShape>(def: {
52
+ readonly name: string;
53
+ readonly shape: TShape;
54
+ readonly initialState: InferSchemaOutput<TShape>;
55
+ reduce(state: InferSchemaOutput<TShape>, event: U): InferSchemaOutput<TShape>;
56
+ }): ProjectionDefinition<InferSchemaOutput<TShape>>;
57
+ projection<S>(def: {
58
+ readonly name: string;
59
+ readonly shape?: unknown;
60
+ readonly initialState: S;
61
+ reduce(state: S, event: U): S;
62
+ }): ProjectionDefinition<S>;
63
+ effect<TSchema = undefined, TInput = InferDefinedSchema<TSchema>, R = never, E extends EventInputOf<U> = EventInputOf<U>>(def: {
64
+ type: `${TNamespace}.${string}`;
65
+ input?: TSchema;
66
+ retry?: RetryPolicy;
67
+ execute: (input: TInput, ctx: ScopedEffectContext<EventInputOf<U>>) => EffectHandlerResult<R, EventInputOf<U>>;
68
+ }): NoInfer<EffectDefinition<TInput, R, E>>;
69
+ input<K extends keyof (TEvents extends EventCatalog<any, infer E> ? E : never) & string>(key: K, payload: InferPayload<(TEvents extends EventCatalog<any, infer E> ? E : never)[K]>, meta?: Omit<EventInput, 'type' | 'payload'>): Omit<EventInput, 'type' | 'payload'> & {
70
+ readonly type: `${TNamespace}.${K}`;
71
+ readonly payload: InferPayload<(TEvents extends EventCatalog<any, infer E> ? E : never)[K]>;
72
+ };
73
+ emit<K extends keyof (TEvents extends EventCatalog<any, infer E> ? E : never) & string>(key: K, payload: InferPayload<(TEvents extends EventCatalog<any, infer E> ? E : never)[K]>, meta?: Omit<EventInput, 'type' | 'payload'>): EmitEffect;
74
+ }
75
+ export type EventOf<T> = T extends ModuleScope<any, infer U, any> ? U : T extends AnyRuntimeModule ? EventsOf<T> : never;
76
+ export type ThreadStateOf<T> = T extends ThreadDefinition<infer S> ? S : never;
77
+ export declare function createModuleScope<TNamespace extends string, TEvents extends EventCatalog | undefined = undefined, TObserves extends readonly EventCatalog[] | undefined = undefined>(options: DefineModuleOptions<TNamespace, TEvents, TObserves>): ModuleScope<TNamespace, ScopeUniverse<TEvents, TObserves>, TEvents, TObserves>;
78
+ /** A catalog may be supplied directly, or created from inline event schemas. */
79
+ export type ModuleCatalog<N extends string, E> = E extends EventCatalog ? E : E extends CatalogEntries ? EventCatalog<N, E> : undefined;
80
+ /** Construct a complete module. Only members returned by setup are installed. */
81
+ export declare function defineModule<const N extends string, E extends EventCatalog | CatalogEntries | undefined = undefined, O extends readonly EventCatalog[] | undefined = undefined, FX extends {
82
+ readonly [key: string]: EffectDefinition<any, any, any>;
83
+ } = {}, TH extends {
84
+ readonly [key: string]: ThreadDefinition;
85
+ } = {}, PR extends {
86
+ readonly [key: string]: ProjectionDefinition;
87
+ } = {}>(options: Omit<DefineModuleOptions<N, ModuleCatalog<N, E>, O>, 'events'> & {
88
+ readonly events?: E;
89
+ }, setup: (scope: ModuleScope<N, ScopeUniverse<ModuleCatalog<N, E>, O>, ModuleCatalog<N, E>, O>) => {
90
+ readonly effects?: FX;
91
+ readonly threads?: TH;
92
+ readonly projections?: PR;
93
+ readonly definitions?: readonly DefinitionRef[];
94
+ readonly middleware?: readonly EffectMiddleware[];
95
+ } & ModuleServicesConfig<EffectRequirementsOf<FX>>): RuntimeModule<N, Extract<ModuleCatalog<N, E>, EventCatalog>, FX, TH, PR> & {
96
+ readonly effects: FX;
97
+ readonly threads: TH;
98
+ readonly projections: PR;
99
+ };