@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,13 @@
1
+ import type { RuntimeEffect } from './effects';
2
+ import type { EventEnvelope } from './envelope';
3
+ import { type FoldRegistry } from './fold';
4
+ import { type RunState } from './state';
5
+ export interface ReplayStep {
6
+ seq: number;
7
+ event: EventEnvelope;
8
+ before: RunState;
9
+ after: RunState;
10
+ effects: RuntimeEffect[];
11
+ }
12
+ export declare function replayTo(events: readonly EventEnvelope[], registry: FoldRegistry, seq: number): ReplayStep | null;
13
+ export declare function replayAll(events: readonly EventEnvelope[], registry: FoldRegistry): ReplayStep[];
@@ -0,0 +1,34 @@
1
+ import { foldEvent, foldRun } from './fold.js';
2
+ import { emptyRunState } from './state.js';
3
+ export function replayTo(events, registry, seq) {
4
+ const target = events.find((event) => event.seq === seq && !event.ephemeral);
5
+ if (!target) {
6
+ return null;
7
+ }
8
+ const prior = events.filter((event) => event.seq < seq);
9
+ const before = foldRun(prior, registry, { runId: target.runId });
10
+ const after = foldEvent(before, target, registry);
11
+ const beforeIds = new Set(before.outstandingEffects.map((item) => item.effectId));
12
+ const effects = after.outstandingEffects
13
+ .filter((item) => !beforeIds.has(item.effectId))
14
+ .map((item) => item.effect);
15
+ return { seq, event: target, before, after, effects };
16
+ }
17
+ export function replayAll(events, registry) {
18
+ const steps = [];
19
+ let state = emptyRunState(events[0]?.runId ?? 'unknown');
20
+ for (const event of events) {
21
+ if (event.ephemeral) {
22
+ continue;
23
+ }
24
+ const before = state;
25
+ const after = foldEvent(before, event, registry);
26
+ const beforeIds = new Set(before.outstandingEffects.map((item) => item.effectId));
27
+ const effects = after.outstandingEffects
28
+ .filter((item) => !beforeIds.has(item.effectId))
29
+ .map((item) => item.effect);
30
+ steps.push({ seq: event.seq, event, before, after, effects });
31
+ state = after;
32
+ }
33
+ return steps;
34
+ }
@@ -0,0 +1,35 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import { Effect, Schema } from 'effect';
3
+ import type { JsonValue } from './types';
4
+ declare const InvalidInputError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
5
+ readonly _tag: "InvalidInputError";
6
+ } & Readonly<A>;
7
+ export declare class InvalidInputError extends InvalidInputError_base<{
8
+ readonly message: string;
9
+ readonly issues: ReadonlyArray<StandardSchemaV1.Issue>;
10
+ }> {
11
+ readonly issues: ReadonlyArray<StandardSchemaV1.Issue>;
12
+ constructor(text: string, issues?: ReadonlyArray<StandardSchemaV1.Issue>);
13
+ }
14
+ export type SchemaInput = StandardSchemaV1<any, any> | Schema.Top | Schema.Schema<any> | {
15
+ _output: any;
16
+ };
17
+ export type InferSchemaOutput<T> = T extends Schema.Schema<infer Out> ? Out : T extends Schema.Top ? Schema.Schema.Type<T> : T extends StandardSchemaV1<any, infer Out> ? Out : T extends {
18
+ _output: infer Out;
19
+ } ? Out : never;
20
+ /** Output of an optional `input` / `shape` schema, or `TFallback` when the schema is omitted. */
21
+ export type InferDefinedSchema<T, TFallback = JsonValue> = [T] extends [undefined] ? TFallback : InferSchemaOutput<NonNullable<T>>;
22
+ type SchemaCandidate<TInput> = StandardSchemaV1<JsonValue, TInput> | StandardSchemaV1<unknown, TInput> | StandardSchemaV1<any, TInput> | Schema.Schema<TInput> | {
23
+ _output: TInput;
24
+ } | {
25
+ input?: StandardSchemaV1<JsonValue, TInput> | StandardSchemaV1<unknown, TInput> | StandardSchemaV1<any, TInput> | Schema.Schema<TInput> | {
26
+ _output: TInput;
27
+ };
28
+ schema?: StandardSchemaV1<JsonValue, TInput> | StandardSchemaV1<unknown, TInput> | StandardSchemaV1<any, TInput> | Schema.Schema<TInput> | {
29
+ _output: TInput;
30
+ };
31
+ } | null | undefined;
32
+ export declare function validateInputEffect<TInput = JsonValue>(schemaOrDef: SchemaCandidate<TInput>, raw: JsonValue): Effect.Effect<TInput, InvalidInputError>;
33
+ export declare function validateInput<TInput = JsonValue>(schemaOrDef: SchemaCandidate<TInput>, raw: JsonValue): Promise<TInput>;
34
+ export declare function validateInputSync<TInput = JsonValue>(schemaOrDef: SchemaCandidate<TInput>, raw: JsonValue): TInput;
35
+ export {};
@@ -0,0 +1,130 @@
1
+ import { Data, Effect, Predicate, Schema } from 'effect';
2
+ export class InvalidInputError extends Data.TaggedError('InvalidInputError') {
3
+ issues;
4
+ constructor(text, issues = []) {
5
+ super({ message: text, issues });
6
+ this.name = 'InvalidInputError';
7
+ this.issues = issues;
8
+ }
9
+ }
10
+ function toStandard(candidate) {
11
+ if (!candidate) {
12
+ return undefined;
13
+ }
14
+ if (Schema.isSchema(candidate)) {
15
+ // SAFETY: Effect Schema converts to StandardSchemaV1 via official helper.
16
+ return Schema.toStandardSchemaV1(candidate);
17
+ }
18
+ if (Predicate.isObject(candidate) && '~standard' in candidate) {
19
+ // SAFETY: verified '~standard' property exists on object.
20
+ return candidate;
21
+ }
22
+ if (Predicate.isObject(candidate) && 'safeParse' in candidate) {
23
+ // SAFETY: candidate has safeParse property verified by key check.
24
+ const safeParse = candidate.safeParse;
25
+ if (Predicate.isFunction(safeParse)) {
26
+ // SAFETY: verified safeParse is a function matching the Zod safeParse contract.
27
+ const zodSchema = candidate;
28
+ return {
29
+ '~standard': {
30
+ version: 1,
31
+ vendor: 'zod-compat',
32
+ validate(raw) {
33
+ const res = zodSchema.safeParse(raw);
34
+ if (res.success) {
35
+ return { value: res.data };
36
+ }
37
+ return {
38
+ issues: res.error.issues.map((issue) => ({
39
+ message: issue.message,
40
+ path: issue.path,
41
+ })),
42
+ };
43
+ },
44
+ },
45
+ };
46
+ }
47
+ }
48
+ return undefined;
49
+ }
50
+ function extractSchema(schemaOrDef) {
51
+ const direct = toStandard(schemaOrDef);
52
+ if (direct) {
53
+ return direct;
54
+ }
55
+ if (schemaOrDef && Predicate.isObject(schemaOrDef)) {
56
+ if ('schema' in schemaOrDef) {
57
+ // SAFETY: verified schemaOrDef has schema property.
58
+ const fromSchema = toStandard(schemaOrDef.schema);
59
+ if (fromSchema) {
60
+ return fromSchema;
61
+ }
62
+ }
63
+ if ('input' in schemaOrDef) {
64
+ // SAFETY: Verified schemaOrDef has input property.
65
+ return toStandard(schemaOrDef.input);
66
+ }
67
+ }
68
+ return undefined;
69
+ }
70
+ function formatIssue(issue) {
71
+ if (!issue.path || issue.path.length === 0) {
72
+ return issue.message;
73
+ }
74
+ const pathStr = issue.path
75
+ .map((p) => {
76
+ if (Predicate.isReadonlyObject(p) && 'key' in p) {
77
+ return String(p.key);
78
+ }
79
+ if (Predicate.isString(p) || Predicate.isNumber(p)) {
80
+ return String(p);
81
+ }
82
+ return '';
83
+ })
84
+ .filter((s) => s.length > 0)
85
+ .join('.');
86
+ return `${pathStr}: ${issue.message}`;
87
+ }
88
+ export function validateInputEffect(schemaOrDef, raw) {
89
+ const schema = extractSchema(schemaOrDef);
90
+ if (!schema) {
91
+ // SAFETY: when no schema is provided, raw JsonValue is accepted as TInput.
92
+ return Effect.succeed(raw);
93
+ }
94
+ return Effect.gen(function* () {
95
+ let result = schema['~standard'].validate(raw);
96
+ if (result instanceof Promise) {
97
+ result = yield* Effect.tryPromise({
98
+ // SAFETY: Standard Schema validate() returns this Promise when async.
99
+ try: () => result,
100
+ catch: (cause) => new InvalidInputError(`Schema validation failed: ${cause instanceof Error ? cause.message : String(cause)}`),
101
+ });
102
+ }
103
+ if (result.issues !== undefined) {
104
+ const text = result.issues.map(formatIssue).join(', ');
105
+ return yield* Effect.fail(new InvalidInputError(`Invalid input: ${text}`, result.issues));
106
+ }
107
+ return result.value;
108
+ });
109
+ }
110
+ export async function validateInput(schemaOrDef, raw) {
111
+ return Effect.runPromise(validateInputEffect(schemaOrDef, raw));
112
+ }
113
+ export function validateInputSync(schemaOrDef, raw) {
114
+ const schema = extractSchema(schemaOrDef);
115
+ if (!schema) {
116
+ // SAFETY: when no schema is provided, raw JsonValue is accepted as TInput.
117
+ return raw;
118
+ }
119
+ // SAFETY: StandardSchemaV1.validate returns Result<TInput> or Promise<Result<TInput>>.
120
+ const result = schema['~standard'].validate(raw);
121
+ if (result instanceof Promise) {
122
+ throw new Error('Async schema validation is not supported in synchronous context');
123
+ }
124
+ if (result.issues !== undefined) {
125
+ const text = result.issues.map(formatIssue).join(', ');
126
+ throw new InvalidInputError(`Invalid input: ${text}`, result.issues);
127
+ }
128
+ // SAFETY: When result.issues is undefined, result is SuccessResult.
129
+ return result.value;
130
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * FIFO serializer keyed by arbitrary identifier (stream / run / key).
3
+ * - Guarantees sequential execution of async tasks for the same key.
4
+ * - Independent keys execute concurrently.
5
+ * - A failed task does not reject or block the chain for subsequent tasks.
6
+ * - Automatically evicts completed chains from memory when idle.
7
+ */
8
+ export declare function createKeyedSerializer(): {
9
+ run: <T>(key: string, task: () => Promise<T>) => Promise<T>;
10
+ drain: (key: string) => Promise<void>;
11
+ clear: (key?: string) => void;
12
+ size: () => number;
13
+ };
14
+ export type KeyedSerializer = ReturnType<typeof createKeyedSerializer>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * FIFO serializer keyed by arbitrary identifier (stream / run / key).
3
+ * - Guarantees sequential execution of async tasks for the same key.
4
+ * - Independent keys execute concurrently.
5
+ * - A failed task does not reject or block the chain for subsequent tasks.
6
+ * - Automatically evicts completed chains from memory when idle.
7
+ */
8
+ export function createKeyedSerializer() {
9
+ const tails = new Map();
10
+ const run = (key, task) => {
11
+ const prev = tails.get(key) ?? Promise.resolve();
12
+ let cleanup;
13
+ const next = prev.then(task, task);
14
+ cleanup = next.then(() => {
15
+ if (tails.get(key) === cleanup) {
16
+ tails.delete(key);
17
+ }
18
+ }, () => {
19
+ if (tails.get(key) === cleanup) {
20
+ tails.delete(key);
21
+ }
22
+ });
23
+ tails.set(key, cleanup);
24
+ return next;
25
+ };
26
+ const drain = async (key) => {
27
+ const pending = tails.get(key);
28
+ if (pending) {
29
+ await pending;
30
+ }
31
+ };
32
+ const clear = (key) => {
33
+ if (key !== undefined) {
34
+ tails.delete(key);
35
+ }
36
+ else {
37
+ tails.clear();
38
+ }
39
+ };
40
+ const size = () => tails.size;
41
+ return { run, drain, clear, size };
42
+ }
@@ -0,0 +1,39 @@
1
+ import { Context, Effect, Option } from 'effect';
2
+ import type { RunState } from './state';
3
+ import type { EventStore } from './store';
4
+ /**
5
+ * Materialized run state at a known stream position.
6
+ *
7
+ * `cursor` is the seq of the last covered event (aligned with
8
+ * `SnapshotTakenPayload.seq`). Recovery resumes at `cursor + 1`.
9
+ */
10
+ export interface RunSnapshot {
11
+ readonly runId: string;
12
+ readonly cursor: number;
13
+ readonly stateHash: string;
14
+ readonly takenAt: number;
15
+ readonly state: RunState;
16
+ }
17
+ declare const SnapshotStoreError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
18
+ readonly _tag: "SnapshotStoreError";
19
+ } & Readonly<A>;
20
+ export declare class SnapshotStoreError extends SnapshotStoreError_base<{
21
+ readonly message: string;
22
+ readonly cause?: unknown;
23
+ }> {
24
+ constructor(text: string, cause?: unknown);
25
+ }
26
+ export interface SnapshotStore {
27
+ readonly loadLatest: (runId: string) => Effect.Effect<Option.Option<RunSnapshot>, SnapshotStoreError>;
28
+ readonly save: (snapshot: RunSnapshot) => Effect.Effect<void, SnapshotStoreError>;
29
+ readonly listCursors?: (runId: string) => Effect.Effect<number[], SnapshotStoreError>;
30
+ readonly prune?: (runId: string, keepLatest: number) => Effect.Effect<void, SnapshotStoreError>;
31
+ }
32
+ declare const SnapshotStoreTag_base: Context.ServiceClass<SnapshotStoreTag, "looms/SnapshotStore", SnapshotStore>;
33
+ export declare class SnapshotStoreTag extends SnapshotStoreTag_base {
34
+ }
35
+ /** Attach a SnapshotStore to an EventStore so facades can pair them without extra options. */
36
+ export declare function withSnapshotStore(store: EventStore, snapshots: SnapshotStore): EventStore;
37
+ export declare function snapshotStoreOf(store: EventStore): SnapshotStore | undefined;
38
+ export declare const makeMemorySnapshotStore: Effect.Effect<SnapshotStore, never, never>;
39
+ export {};
@@ -0,0 +1,53 @@
1
+ import { Context, Data, Effect, Option, Ref } from 'effect';
2
+ export class SnapshotStoreError extends Data.TaggedError('SnapshotStoreError') {
3
+ constructor(text, cause) {
4
+ super({ message: text, cause });
5
+ this.name = 'SnapshotStoreError';
6
+ }
7
+ }
8
+ export class SnapshotStoreTag extends Context.Service()('looms/SnapshotStore') {
9
+ }
10
+ const SNAPSHOT_STORE = Symbol.for('looms/EventStore.snapshotStore');
11
+ /** Attach a SnapshotStore to an EventStore so facades can pair them without extra options. */
12
+ export function withSnapshotStore(store, snapshots) {
13
+ const slot = store;
14
+ slot[SNAPSHOT_STORE] = snapshots;
15
+ return store;
16
+ }
17
+ export function snapshotStoreOf(store) {
18
+ const slot = store;
19
+ return slot[SNAPSHOT_STORE];
20
+ }
21
+ export const makeMemorySnapshotStore = Effect.gen(function* () {
22
+ const snapshots = yield* Ref.make(new Map());
23
+ const service = {
24
+ loadLatest: (runId) => Effect.gen(function* () {
25
+ const map = yield* Ref.get(snapshots);
26
+ const list = map.get(runId);
27
+ if (!list || list.length === 0) {
28
+ return Option.none();
29
+ }
30
+ return Option.some(list[list.length - 1]);
31
+ }),
32
+ save: (snapshot) => Ref.update(snapshots, (map) => {
33
+ const next = new Map(map);
34
+ const existing = next.get(snapshot.runId) ?? [];
35
+ next.set(snapshot.runId, [...existing, snapshot]);
36
+ return next;
37
+ }),
38
+ listCursors: (runId) => Effect.gen(function* () {
39
+ const map = yield* Ref.get(snapshots);
40
+ return (map.get(runId) ?? []).map((item) => item.cursor);
41
+ }),
42
+ prune: (runId, keepLatest) => Ref.update(snapshots, (map) => {
43
+ const list = map.get(runId);
44
+ if (!list || list.length <= keepLatest) {
45
+ return map;
46
+ }
47
+ const next = new Map(map);
48
+ next.set(runId, list.slice(list.length - keepLatest));
49
+ return next;
50
+ }),
51
+ };
52
+ return service;
53
+ });
@@ -0,0 +1,26 @@
1
+ import { Schema } from 'effect';
2
+ import { type EventEnvelope } from './envelope';
3
+ import { type FoldRegistry } from './fold';
4
+ import { type RunState } from './state';
5
+ import { type JsonValue } from './types';
6
+ export declare const DEFAULT_SNAPSHOT_EVERY = 200;
7
+ export declare const SnapshotTakenPayloadSchema: Schema.Struct<{
8
+ readonly seq: Schema.Number;
9
+ readonly stateHash: Schema.String;
10
+ readonly state: Schema.optional<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
11
+ }>;
12
+ export declare function hashRunState(state: RunState): string;
13
+ export declare function shouldTakeSnapshot(events: readonly EventEnvelope[], every?: number): boolean;
14
+ export declare const DEFAULT_MAX_INLINE_SNAPSHOT_BYTES: number;
15
+ export declare function runStateToJson(state: RunState): JsonValue;
16
+ export declare function buildSnapshotEvent(runId: string, events: readonly EventEnvelope[], registry: FoldRegistry, options?: {
17
+ includeState?: boolean;
18
+ }): EventEnvelope;
19
+ export declare function buildSnapshotMarker(runId: string, cursor: number, stateHash: string, options?: {
20
+ state?: RunState;
21
+ maxInlineBytes?: number;
22
+ }): EventEnvelope;
23
+ export declare function foldFromSnapshots(events: readonly EventEnvelope[], registry: FoldRegistry, options?: {
24
+ runId?: string;
25
+ }): RunState;
26
+ export declare function emptySnapshot(runId: string): RunState;
@@ -0,0 +1,100 @@
1
+ import { sha256 } from '@noble/hashes/sha2.js';
2
+ import { bytesToHex } from '@noble/hashes/utils.js';
3
+ import { Schema } from 'effect';
4
+ import { createEvent, JsonValueSchema } from './envelope.js';
5
+ import { foldRun } from './fold.js';
6
+ import { emptyRunState } from './state.js';
7
+ import { asJson, fromJsonStruct } from './types.js';
8
+ export const DEFAULT_SNAPSHOT_EVERY = 200;
9
+ export const SnapshotTakenPayloadSchema = Schema.Struct({
10
+ seq: Schema.Number,
11
+ stateHash: Schema.String,
12
+ state: Schema.optional(JsonValueSchema),
13
+ });
14
+ export function hashRunState(state) {
15
+ const json = JSON.stringify(state);
16
+ return bytesToHex(sha256(new TextEncoder().encode(json))).slice(0, 16);
17
+ }
18
+ export function shouldTakeSnapshot(events, every = DEFAULT_SNAPSHOT_EVERY) {
19
+ const durable = events.filter((e) => !e.ephemeral && e.type !== 'runtime.snapshot.taken');
20
+ if (durable.length === 0) {
21
+ return false;
22
+ }
23
+ let lastSnap;
24
+ for (let i = events.length - 1; i >= 0; i--) {
25
+ const candidate = events[i];
26
+ if (candidate?.type === 'runtime.snapshot.taken') {
27
+ lastSnap = candidate;
28
+ break;
29
+ }
30
+ }
31
+ const since = lastSnap ? durable.filter((e) => e.seq > lastSnap.seq).length : durable.length;
32
+ return since >= every;
33
+ }
34
+ export const DEFAULT_MAX_INLINE_SNAPSHOT_BYTES = 256 * 1024;
35
+ export function runStateToJson(state) {
36
+ const raw = JSON.parse(JSON.stringify(state));
37
+ return Schema.decodeUnknownSync(JsonValueSchema)(raw);
38
+ }
39
+ export function buildSnapshotEvent(runId, events, registry, options) {
40
+ const state = foldRun(events, registry, { runId });
41
+ const last = events[events.length - 1];
42
+ return buildSnapshotMarker(runId, last?.seq ?? 0, hashRunState(state), {
43
+ state: options?.includeState ? state : undefined,
44
+ });
45
+ }
46
+ /**
47
+ * Pointer-only snapshot marker. Inline `state` only when provided and
48
+ * `JSON.stringify(state).length <= maxInlineBytes` (default 256 KiB).
49
+ */
50
+ let warnedInlineSkip = false;
51
+ export function buildSnapshotMarker(runId, cursor, stateHash, options) {
52
+ const maxInlineBytes = options?.maxInlineBytes ?? DEFAULT_MAX_INLINE_SNAPSHOT_BYTES;
53
+ let payload = asJson({ seq: cursor, stateHash });
54
+ if (options?.state !== undefined) {
55
+ const encoded = runStateToJson(options.state);
56
+ const bytes = JSON.stringify(encoded).length;
57
+ if (bytes <= maxInlineBytes) {
58
+ payload = asJson({ seq: cursor, stateHash, state: encoded });
59
+ }
60
+ else if (!warnedInlineSkip) {
61
+ warnedInlineSkip = true;
62
+ console.warn(`[looms] snapshot state is ${bytes} bytes (limit ${maxInlineBytes}); writing a pointer-only marker`);
63
+ }
64
+ }
65
+ return createEvent(runId, {
66
+ type: 'runtime.snapshot.taken',
67
+ payload,
68
+ threadId: null,
69
+ origin: { type: 'system' },
70
+ });
71
+ }
72
+ export function foldFromSnapshots(events, registry, options) {
73
+ const runId = options?.runId ?? events[0]?.runId ?? 'unknown';
74
+ let latestSnapIdx = -1;
75
+ for (let i = events.length - 1; i >= 0; i--) {
76
+ if (events[i]?.type === 'runtime.snapshot.taken') {
77
+ latestSnapIdx = i;
78
+ break;
79
+ }
80
+ }
81
+ if (latestSnapIdx < 0) {
82
+ return foldRun(events, registry, { runId });
83
+ }
84
+ const snap = events[latestSnapIdx];
85
+ const decoded = Schema.decodeUnknownSync(SnapshotTakenPayloadSchema)(snap.payload);
86
+ if (decoded.state !== undefined) {
87
+ const trailing = events.filter((e) => e.seq > decoded.seq);
88
+ if (trailing.length === 0) {
89
+ return fromJsonStruct(decoded.state);
90
+ }
91
+ return foldRun(trailing, registry, {
92
+ runId,
93
+ initial: fromJsonStruct(decoded.state),
94
+ });
95
+ }
96
+ return foldRun(events, registry, { runId });
97
+ }
98
+ export function emptySnapshot(runId) {
99
+ return emptyRunState(runId);
100
+ }
@@ -0,0 +1,31 @@
1
+ import { type EventStore } from './store';
2
+ export declare const SQLITE_EVENT_STORE_SCHEMA: string;
3
+ export type SqlBinding = string | number | null;
4
+ /**
5
+ * Minimal SQL surface used by the embedded event store.
6
+ * Compatible with `bun:sqlite` and Cloudflare Durable Object `ctx.storage.sql`.
7
+ */
8
+ export interface SqliteExec {
9
+ run(sql: string, params?: readonly SqlBinding[]): void;
10
+ rows(sql: string, params?: readonly SqlBinding[]): readonly unknown[];
11
+ /**
12
+ * Run `fn` atomically. When omitted, `sqliteEventStore` uses `BEGIN IMMEDIATE` /
13
+ * `COMMIT` / `ROLLBACK`. Durable Object SQL must supply this via
14
+ * `storage.transactionSync` — DO SQLite rejects SQL transaction statements.
15
+ */
16
+ transaction?<T>(fn: () => T): T;
17
+ }
18
+ export interface SqliteEventStoreOptions {
19
+ readonly exec: SqliteExec;
20
+ }
21
+ /** Adapter for Cloudflare Durable Object `ctx.storage.sql`. */
22
+ export declare function sqlStorageExec(sql: {
23
+ exec: (query: string, ...bindings: SqlBinding[]) => Iterable<unknown>;
24
+ }, options?: {
25
+ readonly transaction?: <T>(fn: () => T) => T;
26
+ }): SqliteExec;
27
+ /**
28
+ * Embedded SQLite EventStore for actor-local execution.
29
+ * Snapshots live in the same database and are attached automatically.
30
+ */
31
+ export declare function sqliteEventStore(options: SqliteEventStoreOptions): EventStore;