@swirls/looms 0.0.1 → 0.1.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 (257) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +2 -0
  3. package/dist/actor/cell.d.ts +9 -4
  4. package/dist/actor/cell.js +7 -11
  5. package/dist/actor/host.d.ts +7 -3
  6. package/dist/actor/host.js +55 -32
  7. package/dist/actor/index.d.ts +1 -1
  8. package/dist/actor/index.js +1 -1
  9. package/dist/actor/routing.d.ts +12 -1
  10. package/dist/actor/routing.js +57 -51
  11. package/dist/agent/agent-session.d.ts +5 -0
  12. package/dist/agent/agent-session.js +270 -0
  13. package/dist/agent/definitions-store.d.ts +2 -3
  14. package/dist/agent/definitions-store.js +2 -4
  15. package/dist/agent/definitions.d.ts +46 -20
  16. package/dist/agent/definitions.js +36 -8
  17. package/dist/agent/effects.d.ts +359 -2
  18. package/dist/agent/effects.js +60 -34
  19. package/dist/agent/events.d.ts +175 -19
  20. package/dist/agent/events.js +45 -10
  21. package/dist/agent/index.d.ts +1 -0
  22. package/dist/agent/index.js +1 -0
  23. package/dist/agent/llm.d.ts +11 -2
  24. package/dist/agent/llm.js +13 -3
  25. package/dist/agent/module.d.ts +812 -78
  26. package/dist/agent/module.js +16 -4
  27. package/dist/agent/scope.d.ts +174 -18
  28. package/dist/agent/signals.d.ts +24 -1
  29. package/dist/agent/signals.js +77 -0
  30. package/dist/agent/threads.d.ts +3 -33
  31. package/dist/agent/threads.js +20 -25
  32. package/dist/agent/tool-schema.js +0 -1
  33. package/dist/agent/types.d.ts +49 -4
  34. package/dist/agent/types.js +5 -5
  35. package/dist/ai-vercel/index.d.ts +1 -0
  36. package/dist/ai-vercel/index.js +1 -0
  37. package/dist/ai-vercel/messages.js +22 -7
  38. package/dist/ai-vercel/vercel-evaluator.d.ts +23 -0
  39. package/dist/ai-vercel/vercel-evaluator.js +68 -0
  40. package/dist/ai-vercel/vercel-llm.js +64 -44
  41. package/dist/approval/effects.d.ts +89 -0
  42. package/dist/approval/module.d.ts +179 -29
  43. package/dist/approval/projections.d.ts +0 -14
  44. package/dist/approval/projections.js +20 -3
  45. package/dist/approval/signals.d.ts +2 -0
  46. package/dist/approval/signals.js +18 -2
  47. package/dist/cli/cli.js +2 -0
  48. package/dist/cli/commands.js +12 -16
  49. package/dist/client/client.d.ts +1195 -18
  50. package/dist/client/client.js +118 -120
  51. package/dist/client/index.d.ts +1 -1
  52. package/dist/client/index.js +1 -1
  53. package/dist/client/sse.d.ts +13 -0
  54. package/dist/client/sse.js +38 -44
  55. package/dist/cloudflare/alarm-scheduler.d.ts +12 -2
  56. package/dist/cloudflare/alarm-scheduler.js +34 -7
  57. package/dist/cloudflare/durable-object.d.ts +10 -2
  58. package/dist/cloudflare/durable-object.js +59 -28
  59. package/dist/cloudflare/event-store.d.ts +7 -2
  60. package/dist/cloudflare/event-store.js +8 -7
  61. package/dist/cloudflare/index.d.ts +2 -2
  62. package/dist/cloudflare/index.js +2 -2
  63. package/dist/cloudflare/router.js +13 -14
  64. package/dist/core/catalog.d.ts +3 -3
  65. package/dist/core/catalog.js +2 -5
  66. package/dist/core/coalesce.js +5 -3
  67. package/dist/core/compose.d.ts +2 -1
  68. package/dist/core/compose.js +18 -2
  69. package/dist/core/definition-store.d.ts +11 -0
  70. package/dist/core/definition-store.js +6 -0
  71. package/dist/core/effects.d.ts +127 -15
  72. package/dist/core/effects.js +89 -17
  73. package/dist/core/envelope.d.ts +27 -7
  74. package/dist/core/envelope.js +20 -6
  75. package/dist/core/event-codec.d.ts +4 -2
  76. package/dist/core/event-codec.js +10 -5
  77. package/dist/core/event-index.js +1 -3
  78. package/dist/core/fold.d.ts +3 -0
  79. package/dist/core/fold.js +171 -24
  80. package/dist/core/ids.d.ts +5 -0
  81. package/dist/core/ids.js +35 -2
  82. package/dist/core/index.d.ts +5 -1
  83. package/dist/core/index.js +5 -1
  84. package/dist/core/kind.d.ts +22 -0
  85. package/dist/core/kind.js +17 -0
  86. package/dist/core/limits.d.ts +34 -0
  87. package/dist/core/limits.js +46 -0
  88. package/dist/core/match.d.ts +1 -0
  89. package/dist/core/match.js +10 -1
  90. package/dist/core/module-scope.d.ts +22 -20
  91. package/dist/core/module-scope.js +38 -28
  92. package/dist/core/module.d.ts +16 -5
  93. package/dist/core/module.js +4 -0
  94. package/dist/core/projection.d.ts +13 -13
  95. package/dist/core/projection.js +9 -5
  96. package/dist/core/protocol.d.ts +68 -11
  97. package/dist/core/protocol.js +82 -11
  98. package/dist/core/replay.d.ts +312 -3
  99. package/dist/core/replay.js +11 -1
  100. package/dist/core/schema.d.ts +8 -19
  101. package/dist/core/schema.js +25 -80
  102. package/dist/core/serialize.js +2 -4
  103. package/dist/core/snapshot-store.d.ts +2 -1
  104. package/dist/core/snapshot-store.js +8 -2
  105. package/dist/core/snapshots.d.ts +5 -1
  106. package/dist/core/snapshots.js +10 -15
  107. package/dist/core/sqlite-store.js +56 -11
  108. package/dist/core/state.d.ts +142 -4
  109. package/dist/core/state.js +78 -0
  110. package/dist/core/store.d.ts +38 -3
  111. package/dist/core/store.js +77 -22
  112. package/dist/core/thread.d.ts +10 -9
  113. package/dist/core/types.d.ts +5 -8
  114. package/dist/core/types.js +28 -24
  115. package/dist/core/validate-event.js +14 -4
  116. package/dist/core/worker-callback.d.ts +17 -0
  117. package/dist/core/worker-callback.js +1 -0
  118. package/dist/debugger/components/event-stream.js +9 -10
  119. package/dist/debugger/events/protocol-summarize.js +26 -0
  120. package/dist/debugger/lib/event-meta.js +1 -2
  121. package/dist/evaluator/definitions-store.d.ts +11 -0
  122. package/dist/evaluator/definitions-store.js +8 -0
  123. package/dist/evaluator/definitions.d.ts +18 -0
  124. package/dist/evaluator/definitions.js +4 -0
  125. package/dist/evaluator/effects.d.ts +226 -0
  126. package/dist/evaluator/effects.js +132 -0
  127. package/dist/evaluator/evaluator.d.ts +24 -0
  128. package/dist/evaluator/evaluator.js +67 -0
  129. package/dist/evaluator/events.d.ts +59 -0
  130. package/dist/evaluator/events.js +21 -0
  131. package/dist/evaluator/index.d.ts +11 -0
  132. package/dist/evaluator/index.js +11 -0
  133. package/dist/evaluator/module.d.ts +606 -0
  134. package/dist/evaluator/module.js +25 -0
  135. package/dist/evaluator/projections.d.ts +50 -0
  136. package/dist/evaluator/projections.js +20 -0
  137. package/dist/evaluator/scope.d.ts +59 -0
  138. package/dist/evaluator/scope.js +7 -0
  139. package/dist/evaluator/signals.d.ts +3 -0
  140. package/dist/evaluator/signals.js +17 -0
  141. package/dist/evaluator/threads.d.ts +62 -0
  142. package/dist/evaluator/threads.js +117 -0
  143. package/dist/evaluator/types.d.ts +125 -0
  144. package/dist/evaluator/types.js +51 -0
  145. package/dist/projectors/delivery.d.ts +83 -0
  146. package/dist/projectors/delivery.js +182 -0
  147. package/dist/projectors/index-model.d.ts +1 -1
  148. package/dist/projectors/index-model.js +4 -6
  149. package/dist/projectors/index.d.ts +2 -0
  150. package/dist/projectors/index.js +2 -0
  151. package/dist/projectors/memory.d.ts +1 -1
  152. package/dist/projectors/memory.js +10 -8
  153. package/dist/projectors/postgres.js +44 -58
  154. package/dist/projectors/projector.d.ts +17 -0
  155. package/dist/projectors/projector.js +26 -1
  156. package/dist/projectors/sqlite-delivery.d.ts +4 -0
  157. package/dist/projectors/sqlite-delivery.js +147 -0
  158. package/dist/projectors/sqlite.js +23 -23
  159. package/dist/projectors/with-projectors.d.ts +7 -2
  160. package/dist/projectors/with-projectors.js +27 -28
  161. package/dist/react/notify.js +11 -8
  162. package/dist/react/react.d.ts +2 -3
  163. package/dist/react/react.js +14 -30
  164. package/dist/react/sse.d.ts +1 -1
  165. package/dist/react/sse.js +1 -1
  166. package/dist/react/store.d.ts +2 -2
  167. package/dist/react/store.js +109 -119
  168. package/dist/runtime/cancellation.d.ts +16 -0
  169. package/dist/runtime/cancellation.js +129 -0
  170. package/dist/runtime/deadline-transition-planner.d.ts +31 -0
  171. package/dist/runtime/deadline-transition-planner.js +139 -0
  172. package/dist/runtime/dispatch-effect.d.ts +3 -2
  173. package/dist/runtime/dispatch-effect.js +7 -13
  174. package/dist/runtime/errors.d.ts +51 -0
  175. package/dist/runtime/errors.js +39 -0
  176. package/dist/runtime/{runtime-helpers.d.ts → helpers.d.ts} +24 -4
  177. package/dist/runtime/{runtime-helpers.js → helpers.js} +56 -19
  178. package/dist/runtime/index.d.ts +3 -3
  179. package/dist/runtime/index.js +3 -3
  180. package/dist/runtime/ingress.d.ts +15 -0
  181. package/dist/runtime/ingress.js +42 -0
  182. package/dist/runtime/local-effect-executor.d.ts +21 -0
  183. package/dist/runtime/local-effect-executor.js +137 -0
  184. package/dist/runtime/looms.d.ts +22 -4
  185. package/dist/runtime/looms.js +54 -36
  186. package/dist/runtime/observer.d.ts +45 -0
  187. package/dist/runtime/observer.js +11 -0
  188. package/dist/runtime/repository.d.ts +55 -0
  189. package/dist/runtime/repository.js +278 -0
  190. package/dist/runtime/retry-policy.d.ts +16 -0
  191. package/dist/runtime/retry-policy.js +29 -0
  192. package/dist/runtime/run-execution-context.d.ts +30 -0
  193. package/dist/runtime/run-execution-context.js +56 -0
  194. package/dist/runtime/run-start.d.ts +4 -0
  195. package/dist/runtime/run-start.js +44 -0
  196. package/dist/runtime/runtime.d.ts +6 -87
  197. package/dist/runtime/runtime.js +233 -470
  198. package/dist/runtime/server/auth.d.ts +40 -0
  199. package/dist/runtime/server/auth.js +40 -0
  200. package/dist/runtime/server/fetch-handler.d.ts +14 -0
  201. package/dist/runtime/server/fetch-handler.js +51 -0
  202. package/dist/runtime/server/http.d.ts +37 -0
  203. package/dist/runtime/server/http.js +62 -0
  204. package/dist/runtime/server/route-table.d.ts +55 -0
  205. package/dist/runtime/server/route-table.js +74 -0
  206. package/dist/runtime/server/routes/effects.d.ts +3 -0
  207. package/dist/runtime/server/routes/effects.js +59 -0
  208. package/dist/runtime/server/routes/operations.d.ts +3 -0
  209. package/dist/runtime/server/routes/operations.js +14 -0
  210. package/dist/runtime/server/routes/runs.d.ts +3 -0
  211. package/dist/runtime/server/routes/runs.js +192 -0
  212. package/dist/runtime/server.d.ts +2 -8
  213. package/dist/runtime/server.js +4 -211
  214. package/dist/runtime/sse.d.ts +2 -1
  215. package/dist/runtime/sse.js +24 -17
  216. package/dist/runtime/types.d.ts +115 -0
  217. package/dist/runtime/types.js +1 -0
  218. package/dist/runtime/wake-engine.d.ts +20 -0
  219. package/dist/runtime/wake-engine.js +311 -0
  220. package/dist/runtime/wake-scheduler.d.ts +3 -4
  221. package/dist/runtime/wake-scheduler.js +25 -31
  222. package/dist/runtime/worker-callbacks.d.ts +16 -0
  223. package/dist/runtime/worker-callbacks.js +167 -0
  224. package/dist/runtime/worker-effect-dispatch.d.ts +36 -0
  225. package/dist/runtime/worker-effect-dispatch.js +126 -0
  226. package/dist/s2/config.d.ts +1 -1
  227. package/dist/s2/config.js +1 -1
  228. package/dist/s2/lite.js +70 -82
  229. package/dist/s2/pagination.d.ts +15 -0
  230. package/dist/s2/pagination.js +36 -0
  231. package/dist/s2/projector.js +6 -5
  232. package/dist/s2/snapshot-store.js +148 -163
  233. package/dist/s2/store.d.ts +3 -21
  234. package/dist/s2/store.js +71 -165
  235. package/dist/testing/index.d.ts +15 -0
  236. package/dist/testing/index.js +53 -16
  237. package/dist/workflow/definitions-store.d.ts +2 -3
  238. package/dist/workflow/definitions-store.js +2 -2
  239. package/dist/workflow/definitions.d.ts +78 -22
  240. package/dist/workflow/definitions.js +80 -14
  241. package/dist/workflow/effects.d.ts +359 -13
  242. package/dist/workflow/effects.js +214 -115
  243. package/dist/workflow/events.d.ts +149 -5
  244. package/dist/workflow/events.js +27 -5
  245. package/dist/workflow/graph.d.ts +56 -0
  246. package/dist/workflow/graph.js +207 -0
  247. package/dist/workflow/index.d.ts +1 -0
  248. package/dist/workflow/index.js +1 -0
  249. package/dist/workflow/module.d.ts +1047 -266
  250. package/dist/workflow/projections.d.ts +14 -12
  251. package/dist/workflow/projections.js +30 -8
  252. package/dist/workflow/scope.d.ts +146 -4
  253. package/dist/workflow/threads.d.ts +383 -155
  254. package/dist/workflow/threads.js +308 -110
  255. package/package.json +8 -2
  256. package/dist/runtime/events-api.d.ts +0 -4
  257. package/dist/runtime/events-api.js +0 -84
@@ -1,11 +1,89 @@
1
+ import { Effect, Schema } from 'effect';
2
+ import { RuntimeEffectSchema, WaitConditionSchema, } from './effects.js';
3
+ import { JsonValueSchema } from './envelope.js';
4
+ import { DEFAULT_DEFINITION_VERSION } from './module.js';
5
+ export const RunStateSchema = Schema.Struct({
6
+ runId: Schema.String,
7
+ status: Schema.Union([
8
+ Schema.Literal('running'),
9
+ Schema.Literal('completed'),
10
+ Schema.Literal('failed'),
11
+ Schema.Literal('cancelled'),
12
+ ]),
13
+ rootThreadId: Schema.NullOr(Schema.String),
14
+ startIdentity: Schema.NullOr(Schema.Struct({
15
+ kind: Schema.String,
16
+ definitionName: Schema.String,
17
+ definitionVersion: Schema.String,
18
+ input: JsonValueSchema,
19
+ requestedThreadId: Schema.NullOr(Schema.String),
20
+ idempotencyKey: Schema.NullOr(Schema.String),
21
+ rootThreadId: Schema.String,
22
+ })).pipe(Schema.withDecodingDefaultKey(Effect.succeed(null))),
23
+ threads: Schema.Record(Schema.String, Schema.Struct({
24
+ threadId: Schema.String,
25
+ kind: Schema.String,
26
+ definitionName: Schema.String,
27
+ definitionVersion: Schema.String.pipe(Schema.withDecodingDefaultKey(Effect.succeed(DEFAULT_DEFINITION_VERSION))),
28
+ parentThreadId: Schema.NullOr(Schema.String),
29
+ status: Schema.Union([
30
+ Schema.Literal('running'),
31
+ Schema.Literal('waiting'),
32
+ Schema.Literal('completed'),
33
+ Schema.Literal('failed'),
34
+ Schema.Literal('cancelled'),
35
+ ]),
36
+ input: JsonValueSchema,
37
+ output: Schema.NullOr(JsonValueSchema),
38
+ error: Schema.NullOr(Schema.String),
39
+ state: JsonValueSchema,
40
+ })),
41
+ waits: Schema.Record(Schema.String, Schema.Struct({
42
+ waitId: Schema.String,
43
+ threadId: Schema.String,
44
+ on: WaitConditionSchema,
45
+ tag: Schema.optional(JsonValueSchema),
46
+ })),
47
+ outstandingEffects: Schema.Array(Schema.Struct({
48
+ effectId: Schema.String,
49
+ threadId: Schema.String,
50
+ causingSeq: Schema.Finite,
51
+ causingEventId: Schema.String,
52
+ effect: RuntimeEffectSchema,
53
+ })),
54
+ effectExecutions: Schema.Record(Schema.String, Schema.Struct({
55
+ effectId: Schema.String,
56
+ attempt: Schema.Finite,
57
+ status: Schema.Union([
58
+ Schema.Literal('queued'),
59
+ Schema.Literal('dispatched'),
60
+ Schema.Literal('started'),
61
+ Schema.Literal('heartbeat'),
62
+ Schema.Literal('retry_wait'),
63
+ Schema.Literal('cancel_requested'),
64
+ Schema.Literal('timed_out'),
65
+ Schema.Literal('ambiguous'),
66
+ Schema.Literal('running'),
67
+ ]),
68
+ nextAttemptAt: Schema.NullOr(Schema.Finite),
69
+ deadlineAt: Schema.NullOr(Schema.Finite).pipe(Schema.withDecodingDefaultKey(Effect.succeed(null))),
70
+ startedAt: Schema.NullOr(Schema.Finite).pipe(Schema.withDecodingDefaultKey(Effect.succeed(null))),
71
+ lastHeartbeatAt: Schema.NullOr(Schema.Finite).pipe(Schema.withDecodingDefaultKey(Effect.succeed(null))),
72
+ lastError: Schema.NullOr(Schema.String),
73
+ })).pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))),
74
+ completedEffectIds: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
75
+ processedIdempotencyKeys: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
76
+ });
1
77
  export function emptyRunState(runId) {
2
78
  return {
3
79
  runId,
4
80
  status: 'running',
5
81
  rootThreadId: null,
82
+ startIdentity: null,
6
83
  threads: {},
7
84
  waits: {},
8
85
  outstandingEffects: [],
86
+ effectExecutions: {},
9
87
  completedEffectIds: [],
10
88
  processedIdempotencyKeys: [],
11
89
  };
@@ -1,5 +1,6 @@
1
1
  import { Context, Effect, Layer, Stream } from 'effect';
2
2
  import { type AppendableEvent, type EventEnvelope } from './envelope';
3
+ import { EventPayloadTooLargeError } from './limits';
3
4
  declare const EventStoreError_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 & {
4
5
  readonly _tag: "EventStoreError";
5
6
  } & Readonly<A>;
@@ -59,8 +60,31 @@ export declare class EventStoreFencedError extends EventStoreFencedError_base<{
59
60
  }> {
60
61
  constructor(runId: string, token: string);
61
62
  }
62
- export type EventStoreAppendError = EventStoreError | EventStoreConflictError | EventStoreFencedError;
63
- export type AnyEventStoreError = EventStoreError | EventStoreConflictError | EventStoreTruncationError | EventStoreTrimmedError | EventStoreFencedError;
63
+ declare const UnsafeEventStoreTrimError_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 & {
64
+ readonly _tag: "UnsafeEventStoreTrimError";
65
+ } & Readonly<A>;
66
+ export declare class UnsafeEventStoreTrimError extends UnsafeEventStoreTrimError_base<{
67
+ readonly runId: string;
68
+ readonly trimThrough: number;
69
+ readonly message: string;
70
+ }> {
71
+ constructor(runId: string, trimThrough: number);
72
+ }
73
+ export type EventStoreTrimCoverage = {
74
+ /** Durable archive contains every event through this sequence. */
75
+ readonly archiveCursor: number;
76
+ readonly requiredProjectors?: never;
77
+ readonly projectorCursors?: never;
78
+ } | {
79
+ readonly archiveCursor?: never;
80
+ /** Authoritative identities of every projector required before trimming. */
81
+ readonly requiredProjectors: readonly string[];
82
+ /** Durable checkpoint by projector identity. Every required projector must be present. */
83
+ readonly projectorCursors: Readonly<Record<string, number>>;
84
+ };
85
+ export declare function trimEventStoreSafely(store: EventStore, runId: string, beforeSeq: number, coverage: EventStoreTrimCoverage): Effect.Effect<void, EventStoreError | UnsafeEventStoreTrimError>;
86
+ export type EventStoreAppendError = EventStoreError | EventStoreConflictError | EventStoreFencedError | EventPayloadTooLargeError;
87
+ export type AnyEventStoreError = EventStoreError | EventStoreConflictError | EventStoreTruncationError | EventStoreTrimmedError | EventStoreFencedError | EventPayloadTooLargeError;
64
88
  export declare function isEventStoreError(err: unknown): err is AnyEventStoreError;
65
89
  export interface StreamBounds {
66
90
  readonly head: number;
@@ -68,6 +92,16 @@ export interface StreamBounds {
68
92
  }
69
93
  export interface AppendOptions {
70
94
  readonly expectedTail?: number;
95
+ /**
96
+ * Atomically claims an ingress command with the append. Reusing the key
97
+ * returns a deduplicated result without appending events. Stores without a
98
+ * command table may ignore this; callers must pair it with `expectedTail`
99
+ * so a retried command conflicts instead of appending twice.
100
+ */
101
+ readonly idempotency?: {
102
+ readonly key: string;
103
+ readonly fingerprint?: string;
104
+ };
71
105
  /** Set the stream fencing token atomically in this batch (`''` clears). */
72
106
  readonly fence?: string;
73
107
  /** Must match the stream's current fencing token, or the append fails. */
@@ -92,6 +126,7 @@ export declare function paginatePages<T, SCursor, E = never, R = never>(options:
92
126
  export interface AppendResult {
93
127
  sequences: number[];
94
128
  tail: number;
129
+ deduplicated?: boolean;
95
130
  }
96
131
  export interface EventStore {
97
132
  readonly append: (runId: string, events: ReadonlyArray<AppendableEvent>, options?: AppendOptions) => Effect.Effect<AppendResult, EventStoreAppendError>;
@@ -128,7 +163,7 @@ export interface EventStore {
128
163
  readonly subscribe: (runId: string, options?: {
129
164
  fromSeq?: number;
130
165
  }) => Stream.Stream<EventEnvelope, EventStoreError>;
131
- readonly listRuns: () => Effect.Effect<string[], EventStoreError>;
166
+ readonly listRuns: Effect.Effect<string[], EventStoreError>;
132
167
  }
133
168
  /**
134
169
  * Utility to read an EventStore as a Stream.Stream, using `store.readStream` if implemented
@@ -1,5 +1,6 @@
1
1
  import { Context, Data, Effect, Layer, Option, Queue, Ref, Stream } from 'effect';
2
2
  import { withAssignedSeq } from './envelope.js';
3
+ import { assertEventByteLimits, EventPayloadTooLargeError } from './limits.js';
3
4
  export class EventStoreError extends Data.TaggedError('EventStoreError') {
4
5
  conflict;
5
6
  cause;
@@ -55,12 +56,34 @@ export class EventStoreFencedError extends Data.TaggedError('EventStoreFencedErr
55
56
  this.name = 'EventStoreFencedError';
56
57
  }
57
58
  }
59
+ export class UnsafeEventStoreTrimError extends Data.TaggedError('UnsafeEventStoreTrimError') {
60
+ constructor(runId, trimThrough) {
61
+ super({
62
+ runId,
63
+ trimThrough,
64
+ message: `UnsafeEventStoreTrim: refusing to trim run "${runId}" through seq ${trimThrough}; no archive or complete projector checkpoint coverage was supplied`,
65
+ });
66
+ this.name = 'UnsafeEventStoreTrimError';
67
+ }
68
+ }
69
+ export function trimEventStoreSafely(store, runId, beforeSeq, coverage) {
70
+ const trimThrough = Math.max(0, beforeSeq - 1);
71
+ const covered = coverage.archiveCursor !== undefined
72
+ ? coverage.archiveCursor >= trimThrough
73
+ : coverage.requiredProjectors.length > 0 &&
74
+ coverage.requiredProjectors.every((projector) => (coverage.projectorCursors[projector] ?? -1) >= trimThrough);
75
+ if (!store.trim || !covered) {
76
+ return Effect.fail(new UnsafeEventStoreTrimError(runId, trimThrough));
77
+ }
78
+ return store.trim(runId, beforeSeq);
79
+ }
58
80
  export function isEventStoreError(err) {
59
81
  return (err instanceof EventStoreError ||
60
82
  err instanceof EventStoreConflictError ||
61
83
  err instanceof EventStoreTruncationError ||
62
84
  err instanceof EventStoreTrimmedError ||
63
- err instanceof EventStoreFencedError);
85
+ err instanceof EventStoreFencedError ||
86
+ err instanceof EventPayloadTooLargeError);
64
87
  }
65
88
  /**
66
89
  * Pure Effect-native stream pagination primitive.
@@ -99,16 +122,41 @@ export const makeMemoryEventStore = Effect.gen(function* () {
99
122
  if (existing) {
100
123
  return existing;
101
124
  }
102
- const created = { events: [], waiters: [], head: 1, tail: 0 };
125
+ const created = {
126
+ events: [],
127
+ waiters: [],
128
+ head: 1,
129
+ tail: 0,
130
+ commands: new Map(),
131
+ };
103
132
  map.set(runId, created);
104
133
  return created;
105
134
  };
106
135
  const service = {
107
136
  append: (runId, events, options) => Effect.gen(function* () {
137
+ yield* Effect.try({
138
+ try: () => assertEventByteLimits(runId, events),
139
+ catch: (cause) => cause instanceof EventPayloadTooLargeError
140
+ ? cause
141
+ : new EventStoreError('event size validation failed', cause),
142
+ });
108
143
  const sequences = [];
144
+ let deduplicated = false;
109
145
  yield* Ref.update(logs, (map) => {
110
146
  const next = new Map(map);
111
147
  const log = getOrCreate(next, runId);
148
+ const command = options?.idempotency
149
+ ? log.commands.get(options.idempotency.key)
150
+ : undefined;
151
+ if (command) {
152
+ if (options?.idempotency?.fingerprint !== undefined &&
153
+ command.fingerprint !== undefined &&
154
+ command.fingerprint !== options.idempotency.fingerprint) {
155
+ throw new EventStoreError(`Idempotency key "${options.idempotency.key}" was reused with different content`);
156
+ }
157
+ deduplicated = true;
158
+ return map;
159
+ }
112
160
  if (options?.expectedTail !== undefined && log.tail !== options.expectedTail) {
113
161
  throw new EventStoreConflictError(runId, options.expectedTail, log.tail);
114
162
  }
@@ -135,7 +183,14 @@ export const makeMemoryEventStore = Effect.gen(function* () {
135
183
  head: log.head,
136
184
  tail: nextTail,
137
185
  fenceToken,
186
+ commands: new Map(log.commands),
138
187
  };
188
+ if (options?.idempotency) {
189
+ updated.commands.set(options.idempotency.key, {
190
+ fingerprint: options.idempotency.fingerprint,
191
+ committedTail: nextTail,
192
+ });
193
+ }
139
194
  next.set(runId, updated);
140
195
  for (const event of appended) {
141
196
  for (const waiter of log.waiters) {
@@ -145,11 +200,16 @@ export const makeMemoryEventStore = Effect.gen(function* () {
145
200
  return next;
146
201
  }).pipe(Effect.catchDefect((cause) => Effect.fail(cause instanceof EventStoreConflictError ||
147
202
  cause instanceof EventStoreFencedError ||
148
- cause instanceof EventStoreError
203
+ cause instanceof EventStoreError ||
204
+ cause instanceof EventPayloadTooLargeError
149
205
  ? cause
150
206
  : new EventStoreError('append failed', cause))));
151
207
  const tail = yield* service.tail(runId);
152
- return { sequences, tail };
208
+ const result = { sequences, tail };
209
+ if (deduplicated) {
210
+ result.deduplicated = true;
211
+ }
212
+ return result;
153
213
  }),
154
214
  read: (runId, options) => Effect.gen(function* () {
155
215
  const map = yield* Ref.get(logs);
@@ -174,21 +234,19 @@ export const makeMemoryEventStore = Effect.gen(function* () {
174
234
  }
175
235
  return { head: log.head, tail: log.tail };
176
236
  }),
177
- trim: (runId, beforeSeq) => Effect.gen(function* () {
178
- yield* Ref.update(logs, (map) => {
179
- const log = map.get(runId);
180
- if (!log) {
181
- return map;
182
- }
183
- const next = new Map(map);
184
- const retained = log.events.filter((e) => e.seq >= beforeSeq);
185
- next.set(runId, {
186
- ...log,
187
- events: retained,
188
- head: Math.max(log.head, beforeSeq),
189
- });
190
- return next;
237
+ trim: (runId, beforeSeq) => Ref.update(logs, (map) => {
238
+ const log = map.get(runId);
239
+ if (!log) {
240
+ return map;
241
+ }
242
+ const next = new Map(map);
243
+ const retained = log.events.filter((e) => e.seq >= beforeSeq);
244
+ next.set(runId, {
245
+ ...log,
246
+ events: retained,
247
+ head: Math.max(log.head, beforeSeq),
191
248
  });
249
+ return next;
192
250
  }),
193
251
  subscribe: (runId, options) => Stream.callback((queue) => Effect.gen(function* () {
194
252
  const fromSeq = options?.fromSeq ?? 1;
@@ -219,10 +277,7 @@ export const makeMemoryEventStore = Effect.gen(function* () {
219
277
  }
220
278
  }));
221
279
  })),
222
- listRuns: () => Effect.gen(function* () {
223
- const map = yield* Ref.get(logs);
224
- return [...map.keys()];
225
- }),
280
+ listRuns: Ref.get(logs).pipe(Effect.map((map) => [...map.keys()])),
226
281
  };
227
282
  return service;
228
283
  });
@@ -7,6 +7,7 @@ export interface StartContext<TInput = JsonValue> {
7
7
  readonly threadId: string;
8
8
  readonly parentThreadId: string | null;
9
9
  readonly definitionName: string;
10
+ readonly definitionVersion: string;
10
11
  readonly input: TInput;
11
12
  }
12
13
  export interface ThreadContext {
@@ -22,20 +23,20 @@ export interface ThreadDefinition<S = any> {
22
23
  step(state: S, event: EventEnvelope, ctx: ThreadContext): S;
23
24
  effects(state: S, ctx: ThreadContext): RuntimeEffect[];
24
25
  }
25
- export declare function defineThread<TShape, TInputSchema extends SchemaInput | undefined = undefined>(def: {
26
- readonly kind: string;
27
- readonly shape: TShape;
28
- readonly input?: TInputSchema;
29
- initialState(ctx: StartContext<InferDefinedSchema<TInputSchema>>): InferSchemaOutput<TShape>;
30
- step(state: InferSchemaOutput<TShape>, event: EventEnvelope, ctx: ThreadContext): InferSchemaOutput<TShape>;
31
- effects?(state: InferSchemaOutput<TShape>, ctx: ThreadContext): RuntimeEffect[];
32
- }): ThreadDefinition<InferSchemaOutput<TShape>>;
33
26
  export declare function defineThread<S = JsonValue, TInputSchema extends SchemaInput | undefined = undefined>(def: {
34
27
  readonly kind: string;
35
- readonly shape?: unknown;
28
+ readonly shape?: undefined;
36
29
  readonly input?: TInputSchema;
37
30
  initialState(ctx: StartContext<InferDefinedSchema<TInputSchema>>): S;
38
31
  step(state: S, event: EventEnvelope, ctx: ThreadContext): S;
39
32
  effects?(state: S, ctx: ThreadContext): RuntimeEffect[];
40
33
  }): ThreadDefinition<S>;
34
+ export declare function defineThread<TShape extends SchemaInput, TInputSchema extends SchemaInput | undefined = undefined>(def: {
35
+ readonly kind: string;
36
+ readonly shape: TShape;
37
+ readonly input?: TInputSchema;
38
+ initialState(ctx: StartContext<InferDefinedSchema<TInputSchema>>): InferSchemaOutput<TShape>;
39
+ step(state: NoInfer<InferSchemaOutput<TShape>>, event: EventEnvelope, ctx: ThreadContext): InferSchemaOutput<TShape>;
40
+ effects?(state: NoInfer<InferSchemaOutput<TShape>>, ctx: ThreadContext): RuntimeEffect[];
41
+ }): ThreadDefinition<InferSchemaOutput<TShape>>;
41
42
  export declare function isTerminalStatus(status: ThreadStatus): boolean;
@@ -9,15 +9,12 @@ export type JsonValue = JsonPrimitive | JsonArray | JsonObject;
9
9
  export interface JsonPayload {
10
10
  readonly [key: string]: JsonValue | JsonPayload | readonly (JsonValue | JsonPayload)[] | undefined;
11
11
  }
12
- /**
13
- * Recursively remove keys with `undefined` values from an object,
14
- * matching wire JSON serialization behavior where undefined properties drop.
15
- */
16
- export declare function cleanUndefined<T>(value: T): T;
12
+ export declare const stringifyJson: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => string;
13
+ export declare const stringifyJsonPretty: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => string;
14
+ /** Convert a value to its JSON wire representation, dropping undefined object properties. */
15
+ export declare function cleanUndefined(value: unknown): JsonValue;
17
16
  /** Mark a JSON-serializable struct as a log payload, stripping undefined fields. */
18
- export declare function asJson<T>(payload: T): JsonValue;
19
- /** Recover a typed struct previously written as JSON. */
20
- export declare function fromJsonStruct<T>(value: JsonValue): T;
17
+ export declare const asJson: typeof cleanUndefined;
21
18
  export declare function isJsonObject(value: unknown): value is {
22
19
  [key: string]: JsonValue;
23
20
  };
@@ -1,36 +1,40 @@
1
- import { Predicate } from 'effect';
2
- /**
3
- * Recursively remove keys with `undefined` values from an object,
4
- * matching wire JSON serialization behavior where undefined properties drop.
5
- */
6
- export function cleanUndefined(value) {
1
+ import { Predicate, Schema } from 'effect';
2
+ const encodeUnknownJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));
3
+ const encodeUnknownJsonPretty = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown, { space: 2 }));
4
+ export const stringifyJson = encodeUnknownJson;
5
+ export const stringifyJsonPretty = encodeUnknownJsonPretty;
6
+ // This recursive serializer is the parser for unknown JSON-like boundary values.
7
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
8
+ function cleanJsonValue(value) {
9
+ if (value === null || Predicate.isString(value) || Predicate.isBoolean(value)) {
10
+ return value;
11
+ }
12
+ if (Predicate.isNumber(value)) {
13
+ return Number.isFinite(value) ? value : null;
14
+ }
7
15
  if (Array.isArray(value)) {
8
- // SAFETY: Mapping recursively over an array preserves array structure.
9
- return value.map(cleanUndefined);
16
+ return value.map((item) => cleanJsonValue(item) ?? null);
10
17
  }
11
- if (value === null || !Predicate.isObject(value)) {
12
- return value;
18
+ if (!Predicate.isReadonlyObject(value)) {
19
+ return undefined;
13
20
  }
14
21
  const result = {};
15
- for (const [k, v] of Object.entries(value)) {
16
- if (v !== undefined) {
17
- // SAFETY: cleanUndefined produces JSON-compatible values from JSON-like inputs.
18
- result[k] = cleanUndefined(v);
22
+ for (const [key, nested] of Object.entries(value)) {
23
+ const cleaned = cleanJsonValue(nested);
24
+ if (cleaned !== undefined) {
25
+ result[key] = cleaned;
19
26
  }
20
27
  }
21
- // SAFETY: Rebuilding an object without undefined keys preserves its record shape.
22
28
  return result;
23
29
  }
24
- /** Mark a JSON-serializable struct as a log payload, stripping undefined fields. */
25
- export function asJson(payload) {
26
- // SAFETY: cleanUndefined ensures undefined keys drop as they would across the wire.
27
- return cleanUndefined(payload);
28
- }
29
- /** Recover a typed struct previously written as JSON. */
30
- export function fromJsonStruct(value) {
31
- // SAFETY: value is a JSON struct previously produced as T.
32
- return value;
30
+ /** Convert a value to its JSON wire representation, dropping undefined object properties. */
31
+ // Public JSON normalization intentionally accepts unknown input and returns the parsed domain type.
32
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
33
+ export function cleanUndefined(value) {
34
+ return cleanJsonValue(value) ?? null;
33
35
  }
36
+ /** Mark a JSON-serializable struct as a log payload, stripping undefined fields. */
37
+ export const asJson = cleanUndefined;
34
38
  export function isJsonObject(value) {
35
39
  return Predicate.isObject(value) && !Array.isArray(value);
36
40
  }
@@ -1,4 +1,4 @@
1
- import { Data, Effect } from 'effect';
1
+ import { Data, Effect, Predicate, Schema } from 'effect';
2
2
  import { validateInputEffect } from './schema.js';
3
3
  import { cleanUndefined } from './types.js';
4
4
  export class InvalidEventError extends Data.TaggedError('InvalidEventError') {
@@ -25,16 +25,26 @@ function findCatalogAndEntry(catalogs, type) {
25
25
  }
26
26
  return undefined;
27
27
  }
28
+ function isStandardSchema(entry) {
29
+ return Predicate.isReadonlyObject(entry) && '~standard' in entry;
30
+ }
31
+ function isEffectSchema(entry) {
32
+ return Schema.isSchema(entry);
33
+ }
28
34
  export function validateEventInput(catalogs, input) {
29
35
  const match = findCatalogAndEntry(catalogs, input.type);
30
36
  if (!match) {
31
37
  return Effect.succeed(input);
32
38
  }
33
39
  const cleanedPayload = cleanUndefined(input.payload);
34
- // SAFETY: match.entry is a catalog entry (Schema, StandardSchema, or payload marker).
35
- return validateInputEffect(match.entry, cleanedPayload).pipe(Effect.map((validatedPayload) => ({
40
+ const validated = isEffectSchema(match.entry)
41
+ ? validateInputEffect(match.entry, cleanedPayload)
42
+ : isStandardSchema(match.entry)
43
+ ? validateInputEffect(match.entry, cleanedPayload)
44
+ : Effect.succeed(cleanedPayload);
45
+ return validated.pipe(Effect.map((validatedPayload) => ({
36
46
  ...input,
37
- payload: validatedPayload,
47
+ payload: cleanUndefined(validatedPayload),
38
48
  })), Effect.mapError((err) => new InvalidEventError(input.type, `Invalid payload for event "${input.type}": ${err.message}`, err.issues)));
39
49
  }
40
50
  export function validateEventsEffect(catalogs, events) {
@@ -0,0 +1,17 @@
1
+ import type { EventInput } from './envelope';
2
+ /** Identifies one attempt of an effect executed by an external worker. */
3
+ export interface WorkerCallback {
4
+ readonly effectId: string;
5
+ readonly attempt: number;
6
+ }
7
+ /** What an external worker reports about an effect attempt. */
8
+ export type WorkerCallbackInput = (WorkerCallback & {
9
+ readonly kind: 'started' | 'heartbeat' | 'cancelled';
10
+ }) | (WorkerCallback & {
11
+ readonly kind: 'complete';
12
+ readonly events: ReadonlyArray<EventInput>;
13
+ }) | (WorkerCallback & {
14
+ readonly kind: 'fail';
15
+ readonly error: string;
16
+ });
17
+ export type WorkerCallbackKind = WorkerCallbackInput['kind'];
@@ -0,0 +1 @@
1
+ export {};
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useVirtualizer } from '@tanstack/react-virtual';
3
+ import { Effect, Fiber } from 'effect';
3
4
  import { Check, Search, SlidersHorizontal } from 'lucide-react';
4
5
  import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui';
5
- import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
7
  import { defaultEventCatalog } from '../events/catalog.js';
7
8
  import { cn, shortId } from '../lib/cn.js';
8
9
  import { getEventMeta } from '../lib/event-meta.js';
@@ -53,17 +54,17 @@ export function EventStream({ events, startedAt, threadId, threadLabel, selected
53
54
  return () => undefined;
54
55
  }
55
56
  followLock.current = true;
56
- let timer;
57
+ let followRelease;
57
58
  const rafId = requestAnimationFrame(() => {
58
59
  virtualizer.scrollToIndex(visible.length - 1, { align: 'end' });
59
- timer = setTimeout(() => {
60
+ followRelease = Effect.runFork(Effect.sleep(50).pipe(Effect.andThen(Effect.sync(() => {
60
61
  followLock.current = false;
61
- }, 50);
62
+ }))));
62
63
  });
63
64
  return () => {
64
65
  cancelAnimationFrame(rafId);
65
- if (timer !== undefined) {
66
- clearTimeout(timer);
66
+ if (followRelease !== undefined) {
67
+ Effect.runFork(Fiber.interrupt(followRelease));
67
68
  }
68
69
  followLock.current = false;
69
70
  };
@@ -107,13 +108,11 @@ export function EventStream({ events, startedAt, threadId, threadLabel, selected
107
108
  ? (renderInspector?.(selected) ?? (_jsx(EventInspector, { event: selected, loadReplayStep: loadReplayStep })))
108
109
  : null] }));
109
110
  }
110
- // SAFETY: memoized EventRow preserves generic EventRowProps contract.
111
- const EventRow = memo(function EventRow({ event, catalog, startedAt, selected, onSelectSeq, }) {
111
+ function EventRow({ event, catalog, startedAt, selected, onSelectSeq, }) {
112
112
  const meta = getEventMeta(event, catalog);
113
113
  const offset = startedAt !== undefined ? Math.max(0, event.ts - startedAt) : undefined;
114
114
  const handleClick = useCallback(() => {
115
115
  onSelectSeq(selected ? undefined : event.seq);
116
116
  }, [onSelectSeq, selected, event.seq]);
117
117
  return (_jsxs("button", { type: "button", onClick: handleClick, title: [meta.summary.title, meta.summary.detail].filter(Boolean).join(' — '), className: cn('flex w-full items-start gap-1.5 rounded-sm px-1.5 py-1 text-left font-mono text-[11px]', selected ? 'bg-accent' : 'hover:bg-accent/40', event.ephemeral && 'opacity-50'), children: [_jsx("span", { className: cn('mt-1.5 size-1.5 shrink-0 rounded-full', catalog.familyClass(meta.family)) }), _jsx("span", { className: "text-muted-foreground w-6 shrink-0 text-right tabular-nums", children: event.seq }), _jsx("span", { className: "debugger-event-offset text-muted-foreground w-10 shrink-0 text-right tabular-nums", children: offset !== undefined ? `+${offset}` : '' }), _jsxs("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: [_jsx("span", { className: "text-foreground", children: meta.summary.title }), meta.summary.detail ? (_jsx("span", { className: "text-muted-foreground ml-1", children: meta.summary.detail })) : null] }), event.causationId ? (_jsxs("span", { className: "debugger-event-causation text-muted-foreground shrink-0", children: ["\u2190 ", shortId(event.causationId)] })) : null, event.threadId ? (_jsx("span", { className: "debugger-event-thread text-muted-foreground shrink-0", children: shortId(event.threadId) })) : null] }));
118
- // SAFETY: memoized EventRow preserves generic EventRowProps contract.
119
- });
118
+ }
@@ -25,6 +25,8 @@ export function summarizeProtocolEvent(event) {
25
25
  title: text(payload.error) ? 'run failed' : 'run completed',
26
26
  detail: text(payload.error) ?? compactJson(payload.output ?? null),
27
27
  };
28
+ case 'runtime.run.cancelled':
29
+ return { title: 'run cancelled', detail: text(payload.reason) };
28
30
  case 'runtime.thread.started':
29
31
  return {
30
32
  title: 'thread started',
@@ -58,10 +60,34 @@ export function summarizeProtocolEvent(event) {
58
60
  return { title: 'timer fired' };
59
61
  case 'runtime.effect.failed':
60
62
  return { title: 'effect failed', detail: text(payload.error) };
63
+ case 'runtime.effect.attempt.started':
64
+ return { title: 'effect attempt started', detail: numeric(payload.attempt) };
65
+ case 'runtime.effect.queued':
66
+ return { title: 'effect queued', detail: numeric(payload.attempt) };
67
+ case 'runtime.effect.dispatched':
68
+ return { title: 'effect dispatched', detail: numeric(payload.attempt) };
69
+ case 'runtime.effect.worker.started':
70
+ return { title: 'worker started effect', detail: numeric(payload.attempt) };
71
+ case 'runtime.effect.heartbeat':
72
+ return { title: 'effect heartbeat', detail: numeric(payload.attempt) };
73
+ case 'runtime.effect.completed':
74
+ return { title: 'effect completed', detail: numeric(payload.attempt) };
75
+ case 'runtime.effect.cancel.requested':
76
+ return { title: 'effect cancellation requested', detail: numeric(payload.attempt) };
77
+ case 'runtime.effect.cancelled':
78
+ return { title: 'effect cancelled', detail: numeric(payload.attempt) };
79
+ case 'runtime.effect.timed_out':
80
+ return { title: 'effect timed out', detail: text(payload.timeout) };
81
+ case 'runtime.effect.ambiguous':
82
+ return { title: 'effect outcome ambiguous', detail: text(payload.error) };
83
+ case 'runtime.effect.retry.scheduled':
84
+ return { title: 'effect retry scheduled', detail: text(payload.error) };
61
85
  case 'runtime.snapshot.taken':
62
86
  return { title: 'snapshot', detail: `seq ${numeric(payload.seq) ?? ''}` };
63
87
  case 'runtime.signal.received':
64
88
  return { title: 'signal', detail: compactJson(event.payload) };
89
+ case 'runtime.thread.cancel.requested':
90
+ return { title: 'thread cancellation requested', detail: text(payload.threadId) };
65
91
  default: {
66
92
  const exhaustiveType = event.type;
67
93
  return { title: 'event', detail: compactJson(obj(exhaustiveType)) };
@@ -12,8 +12,7 @@ export function getEventMeta(event, catalog) {
12
12
  : searchText(event, catalog ? catalog.summarize : summarizeEvent);
13
13
  const meta = { summary, searchText: text, family };
14
14
  metaCache.set(event, {
15
- // SAFETY: EventStreamCatalog is covariant over event for cache identity compares.
16
- catalog: catalog,
15
+ catalog,
17
16
  meta,
18
17
  });
19
18
  return meta;
@@ -0,0 +1,11 @@
1
+ import { Context, Layer } from 'effect';
2
+ import type { EvaluatorDefinition } from './definitions';
3
+ export interface EvaluatorDefinitionStore {
4
+ get(name: string): EvaluatorDefinition | undefined;
5
+ }
6
+ declare const EvaluatorDefinitionsTag_base: Context.ServiceClass<EvaluatorDefinitionsTag, "looms/EvaluatorDefinitions", EvaluatorDefinitionStore>;
7
+ export declare class EvaluatorDefinitionsTag extends EvaluatorDefinitionsTag_base {
8
+ }
9
+ export declare function makeEvaluatorDefinitionStore(definitions: ReadonlyArray<EvaluatorDefinition>): EvaluatorDefinitionStore;
10
+ export declare const EvaluatorDefinitionsLive: (definitions: ReadonlyArray<EvaluatorDefinition>) => Layer.Layer<EvaluatorDefinitionsTag, never, never>;
11
+ export {};
@@ -0,0 +1,8 @@
1
+ import { Context, Layer } from 'effect';
2
+ export class EvaluatorDefinitionsTag extends Context.Service()('looms/EvaluatorDefinitions') {
3
+ }
4
+ export function makeEvaluatorDefinitionStore(definitions) {
5
+ const map = new Map(definitions.map((def) => [def.name, def]));
6
+ return { get: (name) => map.get(name) };
7
+ }
8
+ export const EvaluatorDefinitionsLive = (definitions) => Layer.succeed(EvaluatorDefinitionsTag, makeEvaluatorDefinitionStore(definitions));