@swirls/looms 0.0.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -2
- package/dist/actor/cell.d.ts +9 -4
- package/dist/actor/cell.js +7 -11
- package/dist/actor/host.d.ts +7 -3
- package/dist/actor/host.js +57 -33
- package/dist/actor/index.d.ts +1 -1
- package/dist/actor/index.js +1 -1
- package/dist/actor/routing.d.ts +12 -1
- package/dist/actor/routing.js +57 -51
- package/dist/agent/agent-session.d.ts +5 -0
- package/dist/agent/agent-session.js +270 -0
- package/dist/agent/debugger/agent-chat.d.ts +2 -0
- package/dist/agent/debugger/agent-chat.js +176 -0
- package/dist/agent/debugger/index.d.ts +5 -0
- package/dist/agent/debugger/index.js +19 -0
- package/dist/agent/debugger/summarize.d.ts +2 -0
- package/dist/agent/debugger/summarize.js +51 -0
- package/dist/agent/debugger/token-usage.d.ts +3 -0
- package/dist/agent/debugger/token-usage.js +11 -0
- package/dist/agent/definitions-store.d.ts +2 -3
- package/dist/agent/definitions-store.js +2 -4
- package/dist/agent/definitions.d.ts +47 -29
- package/dist/agent/definitions.js +39 -12
- package/dist/agent/effects.d.ts +359 -2
- package/dist/agent/effects.js +60 -34
- package/dist/agent/events.d.ts +175 -19
- package/dist/agent/events.js +45 -10
- package/dist/agent/index.d.ts +1 -0
- package/dist/agent/index.js +1 -0
- package/dist/agent/llm.d.ts +11 -2
- package/dist/agent/llm.js +13 -3
- package/dist/agent/module.d.ts +812 -78
- package/dist/agent/module.js +16 -4
- package/dist/agent/scope.d.ts +174 -18
- package/dist/agent/signals.d.ts +26 -1
- package/dist/agent/signals.js +79 -0
- package/dist/agent/threads.d.ts +3 -33
- package/dist/agent/threads.js +20 -25
- package/dist/agent/tool-schema.d.ts +1 -17
- package/dist/agent/tool-schema.js +3 -43
- package/dist/agent/types.d.ts +49 -4
- package/dist/agent/types.js +5 -5
- package/dist/ai-vercel/index.d.ts +1 -0
- package/dist/ai-vercel/index.js +1 -0
- package/dist/ai-vercel/messages.js +22 -7
- package/dist/ai-vercel/vercel-evaluator.d.ts +36 -0
- package/dist/ai-vercel/vercel-evaluator.js +73 -0
- package/dist/ai-vercel/vercel-llm.js +64 -44
- package/dist/approval/debugger/approvals-view.d.ts +3 -0
- package/dist/approval/debugger/approvals-view.js +16 -0
- package/dist/approval/debugger/index.d.ts +4 -0
- package/dist/approval/debugger/index.js +11 -0
- package/dist/approval/debugger/summarize.d.ts +2 -0
- package/dist/approval/debugger/summarize.js +17 -0
- package/dist/approval/effects.d.ts +89 -0
- package/dist/approval/module.d.ts +179 -29
- package/dist/approval/projections.d.ts +0 -14
- package/dist/approval/projections.js +20 -3
- package/dist/approval/signals.d.ts +2 -0
- package/dist/approval/signals.js +18 -2
- package/dist/cli/cli.js +2 -0
- package/dist/cli/commands.js +12 -16
- package/dist/client/client.d.ts +1213 -18
- package/dist/client/client.js +120 -120
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/sse.d.ts +13 -0
- package/dist/client/sse.js +38 -44
- package/dist/cloudflare/alarm-scheduler.d.ts +12 -2
- package/dist/cloudflare/alarm-scheduler.js +34 -7
- package/dist/cloudflare/durable-object.d.ts +10 -2
- package/dist/cloudflare/durable-object.js +59 -28
- package/dist/cloudflare/event-store.d.ts +7 -2
- package/dist/cloudflare/event-store.js +8 -7
- package/dist/cloudflare/index.d.ts +2 -2
- package/dist/cloudflare/index.js +2 -2
- package/dist/cloudflare/router.d.ts +1 -1
- package/dist/cloudflare/router.js +15 -16
- package/dist/core/catalog.d.ts +3 -3
- package/dist/core/catalog.js +2 -5
- package/dist/core/coalesce.js +5 -3
- package/dist/core/compose.d.ts +2 -1
- package/dist/core/compose.js +19 -2
- package/dist/core/debugger-host.d.ts +16 -0
- package/dist/core/debugger-host.js +1 -0
- package/dist/core/definition-store.d.ts +11 -0
- package/dist/core/definition-store.js +6 -0
- package/dist/core/effects.d.ts +127 -15
- package/dist/core/effects.js +89 -17
- package/dist/core/envelope.d.ts +27 -7
- package/dist/core/envelope.js +20 -6
- package/dist/core/event-codec.d.ts +4 -2
- package/dist/core/event-codec.js +10 -5
- package/dist/core/event-index.js +1 -3
- package/dist/core/fold.d.ts +3 -0
- package/dist/core/fold.js +171 -24
- package/dist/core/ids.d.ts +5 -0
- package/dist/core/ids.js +35 -2
- package/dist/core/index.d.ts +7 -1
- package/dist/core/index.js +6 -1
- package/dist/core/kind.d.ts +22 -0
- package/dist/core/kind.js +17 -0
- package/dist/core/limits.d.ts +34 -0
- package/dist/core/limits.js +46 -0
- package/dist/core/match.d.ts +1 -0
- package/dist/core/match.js +10 -1
- package/dist/core/module-scope.d.ts +22 -20
- package/dist/core/module-scope.js +38 -28
- package/dist/core/module.d.ts +18 -5
- package/dist/core/module.js +4 -0
- package/dist/core/projection.d.ts +13 -13
- package/dist/core/projection.js +9 -5
- package/dist/core/protocol.d.ts +68 -11
- package/dist/core/protocol.js +82 -11
- package/dist/core/published.d.ts +43 -0
- package/dist/core/published.js +44 -0
- package/dist/core/replay.d.ts +312 -3
- package/dist/core/replay.js +11 -1
- package/dist/core/schema.d.ts +11 -20
- package/dist/core/schema.js +63 -80
- package/dist/core/serialize.js +2 -4
- package/dist/core/snapshot-store.d.ts +6 -1
- package/dist/core/snapshot-store.js +15 -2
- package/dist/core/snapshots.d.ts +5 -1
- package/dist/core/snapshots.js +10 -15
- package/dist/core/sqlite-store.js +95 -11
- package/dist/core/state.d.ts +142 -4
- package/dist/core/state.js +78 -0
- package/dist/core/store.d.ts +38 -3
- package/dist/core/store.js +77 -22
- package/dist/core/thread.d.ts +10 -9
- package/dist/core/types.d.ts +5 -8
- package/dist/core/types.js +28 -24
- package/dist/core/validate-event.js +14 -4
- package/dist/core/worker-callback.d.ts +17 -0
- package/dist/core/worker-callback.js +1 -0
- package/dist/debugger/app/assets/index-B3Ah8WAE.css +2 -0
- package/dist/debugger/app/assets/index-Qb371t_O.js +22 -0
- package/dist/debugger/app/assets/rolldown-runtime-CbXtAM7H.js +1 -0
- package/dist/debugger/app/assets/routes-CjtEHgPQ.js +47 -0
- package/dist/debugger/app/index.html +14 -0
- package/dist/debugger/components/composer.d.ts +17 -0
- package/dist/debugger/components/composer.js +25 -0
- package/dist/debugger/components/debugger-split.d.ts +2 -1
- package/dist/debugger/components/debugger-split.js +2 -2
- package/dist/debugger/components/event-inspector.d.ts +2 -1
- package/dist/debugger/components/event-inspector.js +25 -16
- package/dist/debugger/components/event-stream.d.ts +2 -1
- package/dist/debugger/components/event-stream.js +14 -74
- package/dist/debugger/components/follow-list.d.ts +19 -0
- package/dist/debugger/components/follow-list.js +96 -0
- package/dist/debugger/components/json-tree.d.ts +11 -0
- package/dist/debugger/components/json-tree.js +120 -0
- package/dist/debugger/components/json-view.js +2 -2
- package/dist/debugger/components/run-tree.js +53 -20
- package/dist/debugger/components/status-dot.d.ts +4 -0
- package/dist/debugger/components/status-dot.js +6 -2
- package/dist/debugger/context.d.ts +14 -0
- package/dist/debugger/context.js +17 -0
- package/dist/debugger/contracts.d.ts +7 -4
- package/dist/debugger/events/builtin-families.d.ts +3 -0
- package/dist/debugger/events/builtin-families.js +9 -0
- package/dist/debugger/events/catalog.d.ts +3 -4
- package/dist/debugger/events/catalog.js +27 -17
- package/dist/debugger/events/protocol-summarize.d.ts +1 -2
- package/dist/debugger/events/protocol-summarize.js +42 -24
- package/dist/debugger/index.d.ts +20 -6
- package/dist/debugger/index.js +19 -5
- package/dist/debugger/lib/cn.d.ts +1 -0
- package/dist/debugger/lib/cn.js +3 -0
- package/dist/debugger/lib/event-meta.d.ts +2 -3
- package/dist/debugger/lib/event-meta.js +6 -12
- package/dist/debugger/lib/json.d.ts +25 -0
- package/dist/debugger/lib/json.js +87 -0
- package/dist/debugger/lib/payload.d.ts +7 -0
- package/dist/debugger/lib/payload.js +11 -0
- package/dist/debugger/lib/time.d.ts +3 -0
- package/dist/debugger/lib/time.js +24 -0
- package/dist/debugger/plugin.d.ts +36 -0
- package/dist/debugger/plugin.js +14 -0
- package/dist/debugger/server/index.d.ts +14 -0
- package/dist/debugger/server/index.js +114 -0
- package/dist/debugger/shell/debugger-shell.d.ts +15 -0
- package/dist/debugger/shell/debugger-shell.js +43 -0
- package/dist/debugger/shell/definition-key.d.ts +3 -0
- package/dist/debugger/shell/definition-key.js +4 -0
- package/dist/debugger/shell/events-pane.d.ts +12 -0
- package/dist/debugger/shell/events-pane.js +25 -0
- package/dist/debugger/shell/projections-panel.d.ts +4 -0
- package/dist/debugger/shell/projections-panel.js +59 -0
- package/dist/debugger/shell/run-controls.d.ts +4 -0
- package/dist/debugger/shell/run-controls.js +72 -0
- package/dist/debugger/shell/run-search.d.ts +6 -0
- package/dist/debugger/shell/run-search.js +102 -0
- package/dist/debugger/shell/run-type-menu.d.ts +6 -0
- package/dist/debugger/shell/run-type-menu.js +11 -0
- package/dist/debugger/shell/schema-form.d.ts +31 -0
- package/dist/debugger/shell/schema-form.js +117 -0
- package/dist/debugger/shell/start-form.d.ts +3 -0
- package/dist/debugger/shell/start-form.js +75 -0
- package/dist/debugger/shell/use-start-run.d.ts +14 -0
- package/dist/debugger/shell/use-start-run.js +32 -0
- package/dist/debugger/shell/workspace.d.ts +6 -0
- package/dist/debugger/shell/workspace.js +15 -0
- package/dist/debugger/styles.css +111 -27
- package/dist/debugger/ui/button.d.ts +24 -0
- package/dist/debugger/ui/button.js +24 -0
- package/dist/debugger/ui/command.d.ts +9 -0
- package/dist/debugger/ui/command.js +24 -0
- package/dist/debugger/ui/dropdown-menu.d.ts +13 -0
- package/dist/debugger/ui/dropdown-menu.js +37 -0
- package/dist/debugger/ui/panel.d.ts +14 -0
- package/dist/debugger/ui/panel.js +16 -0
- package/dist/debugger/ui/popover.d.ts +7 -0
- package/dist/debugger/ui/popover.js +17 -0
- package/dist/debugger/ui/resizable.d.ts +7 -0
- package/dist/debugger/ui/resizable.js +14 -0
- package/dist/debugger/ui/scroll-area.d.ts +2 -1
- package/dist/debugger/ui/scroll-area.js +5 -3
- package/dist/debugger/ui/select.d.ts +10 -0
- package/dist/debugger/ui/select.js +21 -0
- package/dist/debugger/ui/tabs.js +2 -2
- package/dist/debugger/ui/textarea.d.ts +3 -0
- package/dist/debugger/ui/textarea.js +7 -0
- package/dist/evaluator/definitions-store.d.ts +11 -0
- package/dist/evaluator/definitions-store.js +8 -0
- package/dist/evaluator/definitions.d.ts +18 -0
- package/dist/evaluator/definitions.js +4 -0
- package/dist/evaluator/effects.d.ts +226 -0
- package/dist/evaluator/effects.js +132 -0
- package/dist/evaluator/evaluator.d.ts +24 -0
- package/dist/evaluator/evaluator.js +67 -0
- package/dist/evaluator/events.d.ts +59 -0
- package/dist/evaluator/events.js +21 -0
- package/dist/evaluator/index.d.ts +11 -0
- package/dist/evaluator/index.js +11 -0
- package/dist/evaluator/module.d.ts +606 -0
- package/dist/evaluator/module.js +25 -0
- package/dist/evaluator/projections.d.ts +50 -0
- package/dist/evaluator/projections.js +20 -0
- package/dist/evaluator/scope.d.ts +59 -0
- package/dist/evaluator/scope.js +7 -0
- package/dist/evaluator/signals.d.ts +3 -0
- package/dist/evaluator/signals.js +17 -0
- package/dist/evaluator/threads.d.ts +62 -0
- package/dist/evaluator/threads.js +117 -0
- package/dist/evaluator/types.d.ts +125 -0
- package/dist/evaluator/types.js +51 -0
- package/dist/projectors/delivery.d.ts +83 -0
- package/dist/projectors/delivery.js +182 -0
- package/dist/projectors/index-model.d.ts +1 -1
- package/dist/projectors/index-model.js +4 -6
- package/dist/projectors/index.d.ts +2 -0
- package/dist/projectors/index.js +2 -0
- package/dist/projectors/memory.d.ts +1 -1
- package/dist/projectors/memory.js +10 -8
- package/dist/projectors/postgres.js +44 -58
- package/dist/projectors/projector.d.ts +17 -0
- package/dist/projectors/projector.js +26 -1
- package/dist/projectors/sqlite-delivery.d.ts +4 -0
- package/dist/projectors/sqlite-delivery.js +147 -0
- package/dist/projectors/sqlite.js +23 -23
- package/dist/projectors/with-projectors.d.ts +7 -2
- package/dist/projectors/with-projectors.js +27 -28
- package/dist/react/notify.js +11 -8
- package/dist/react/react.d.ts +2 -3
- package/dist/react/react.js +14 -30
- package/dist/react/sse.d.ts +1 -1
- package/dist/react/sse.js +1 -1
- package/dist/react/store.d.ts +3 -3
- package/dist/react/store.js +109 -119
- package/dist/runtime/cancellation.d.ts +16 -0
- package/dist/runtime/cancellation.js +129 -0
- package/dist/runtime/deadline-transition-planner.d.ts +31 -0
- package/dist/runtime/deadline-transition-planner.js +139 -0
- package/dist/runtime/dispatch-effect.d.ts +3 -2
- package/dist/runtime/dispatch-effect.js +7 -13
- package/dist/runtime/errors.d.ts +51 -0
- package/dist/runtime/errors.js +39 -0
- package/dist/runtime/{runtime-helpers.d.ts → helpers.d.ts} +24 -4
- package/dist/runtime/{runtime-helpers.js → helpers.js} +56 -19
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +3 -3
- package/dist/runtime/ingress.d.ts +15 -0
- package/dist/runtime/ingress.js +42 -0
- package/dist/runtime/local-effect-executor.d.ts +21 -0
- package/dist/runtime/local-effect-executor.js +137 -0
- package/dist/runtime/looms.d.ts +30 -4
- package/dist/runtime/looms.js +57 -37
- package/dist/runtime/observer.d.ts +45 -0
- package/dist/runtime/observer.js +11 -0
- package/dist/runtime/repository.d.ts +55 -0
- package/dist/runtime/repository.js +293 -0
- package/dist/runtime/retry-policy.d.ts +16 -0
- package/dist/runtime/retry-policy.js +29 -0
- package/dist/runtime/run-execution-context.d.ts +30 -0
- package/dist/runtime/run-execution-context.js +56 -0
- package/dist/runtime/run-start.d.ts +4 -0
- package/dist/runtime/run-start.js +44 -0
- package/dist/runtime/runtime.d.ts +6 -87
- package/dist/runtime/runtime.js +248 -470
- package/dist/runtime/server/auth.d.ts +41 -0
- package/dist/runtime/server/auth.js +45 -0
- package/dist/runtime/server/debugger-host.d.ts +11 -0
- package/dist/runtime/server/debugger-host.js +32 -0
- package/dist/runtime/server/fetch-handler.d.ts +18 -0
- package/dist/runtime/server/fetch-handler.js +62 -0
- package/dist/runtime/server/http.d.ts +38 -0
- package/dist/runtime/server/http.js +66 -0
- package/dist/runtime/server/route-table.d.ts +55 -0
- package/dist/runtime/server/route-table.js +74 -0
- package/dist/runtime/server/routes/definitions.d.ts +3 -0
- package/dist/runtime/server/routes/definitions.js +20 -0
- package/dist/runtime/server/routes/effects.d.ts +3 -0
- package/dist/runtime/server/routes/effects.js +59 -0
- package/dist/runtime/server/routes/operations.d.ts +3 -0
- package/dist/runtime/server/routes/operations.js +14 -0
- package/dist/runtime/server/routes/runs.d.ts +3 -0
- package/dist/runtime/server/routes/runs.js +199 -0
- package/dist/runtime/server.d.ts +4 -8
- package/dist/runtime/server.js +6 -211
- package/dist/runtime/sse.d.ts +2 -1
- package/dist/runtime/sse.js +24 -17
- package/dist/runtime/types.d.ts +117 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/wake-engine.d.ts +20 -0
- package/dist/runtime/wake-engine.js +311 -0
- package/dist/runtime/wake-scheduler.d.ts +3 -4
- package/dist/runtime/wake-scheduler.js +25 -31
- package/dist/runtime/worker-callbacks.d.ts +16 -0
- package/dist/runtime/worker-callbacks.js +167 -0
- package/dist/runtime/worker-effect-dispatch.d.ts +36 -0
- package/dist/runtime/worker-effect-dispatch.js +126 -0
- package/dist/s2/config.d.ts +1 -1
- package/dist/s2/config.js +1 -1
- package/dist/s2/lite.js +70 -82
- package/dist/s2/pagination.d.ts +15 -0
- package/dist/s2/pagination.js +36 -0
- package/dist/s2/projector.js +6 -5
- package/dist/s2/snapshot-store.js +188 -161
- package/dist/s2/store.d.ts +3 -21
- package/dist/s2/store.js +71 -165
- package/dist/testing/index.d.ts +15 -0
- package/dist/testing/index.js +53 -16
- package/dist/workflow/debugger/index.d.ts +4 -0
- package/dist/workflow/debugger/index.js +11 -0
- package/dist/workflow/debugger/nodes-view.d.ts +3 -0
- package/dist/workflow/debugger/nodes-view.js +13 -0
- package/dist/workflow/debugger/summarize.d.ts +2 -0
- package/dist/workflow/debugger/summarize.js +30 -0
- package/dist/workflow/definitions-store.d.ts +2 -3
- package/dist/workflow/definitions-store.js +2 -2
- package/dist/workflow/definitions.d.ts +78 -22
- package/dist/workflow/definitions.js +80 -14
- package/dist/workflow/effects.d.ts +453 -107
- package/dist/workflow/effects.js +214 -115
- package/dist/workflow/events.d.ts +149 -5
- package/dist/workflow/events.js +27 -5
- package/dist/workflow/graph.d.ts +56 -0
- package/dist/workflow/graph.js +207 -0
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.js +1 -0
- package/dist/workflow/module.d.ts +1047 -266
- package/dist/workflow/projections.d.ts +14 -12
- package/dist/workflow/projections.js +30 -8
- package/dist/workflow/scope.d.ts +146 -4
- package/dist/workflow/threads.d.ts +383 -155
- package/dist/workflow/threads.js +308 -110
- package/package.json +26 -2
- package/dist/debugger/events/domain-summarize.d.ts +0 -3
- package/dist/debugger/events/domain-summarize.js +0 -107
- package/dist/debugger/events/family.d.ts +0 -8
- package/dist/debugger/events/family.js +0 -44
- package/dist/runtime/events-api.d.ts +0 -4
- package/dist/runtime/events-api.js +0 -84
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Effect, Predicate } from 'effect';
|
|
2
|
+
import { createEvent, isPrimitiveEffect, } from '@swirls/looms/core';
|
|
3
|
+
import { RuntimeExecutionError } from './errors.js';
|
|
4
|
+
import { workerTask } from './helpers.js';
|
|
5
|
+
function workerClaims(worker, runId, item) {
|
|
6
|
+
if (!worker.handles && !worker.canDispatch) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const { handles } = worker;
|
|
10
|
+
const handled = Predicate.isFunction(handles)
|
|
11
|
+
? handles(item.effect.type)
|
|
12
|
+
: (handles?.includes(item.effect.type) ?? false);
|
|
13
|
+
return handled || (worker.canDispatch?.(workerTask(runId, item, 0)) ?? false);
|
|
14
|
+
}
|
|
15
|
+
const WORKER_ATTEMPT_STATUSES = new Set([
|
|
16
|
+
'queued',
|
|
17
|
+
'dispatched',
|
|
18
|
+
'started',
|
|
19
|
+
'heartbeat',
|
|
20
|
+
'cancel_requested',
|
|
21
|
+
'timed_out',
|
|
22
|
+
'ambiguous',
|
|
23
|
+
]);
|
|
24
|
+
/**
|
|
25
|
+
* Decides where an outstanding effect runs. An attempt already in flight keeps
|
|
26
|
+
* its durable placement. Otherwise an explicit `execution` wins, then a worker
|
|
27
|
+
* claim; effects with a local handler run in process and handler-less effects
|
|
28
|
+
* go to the worker.
|
|
29
|
+
*/
|
|
30
|
+
export function isWorkerEffect(input) {
|
|
31
|
+
const { item, execution, worker } = input;
|
|
32
|
+
if (isPrimitiveEffect(item.effect)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (execution?.status === 'running') {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if (execution && WORKER_ATTEMPT_STATUSES.has(execution.status)) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
const definition = input.registry.effects.get(item.effect.type);
|
|
42
|
+
if (definition?.execution) {
|
|
43
|
+
return definition.execution === 'worker';
|
|
44
|
+
}
|
|
45
|
+
const claim = worker ? workerClaims(worker, input.runId, item) : undefined;
|
|
46
|
+
if (claim) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
if (definition) {
|
|
50
|
+
return !definition.hasHandler;
|
|
51
|
+
}
|
|
52
|
+
return worker !== undefined && claim !== false;
|
|
53
|
+
}
|
|
54
|
+
export function planWorkerEffects(input) {
|
|
55
|
+
return input.state.outstandingEffects
|
|
56
|
+
.filter((item) => {
|
|
57
|
+
const execution = input.state.effectExecutions[item.effectId];
|
|
58
|
+
if (!isWorkerEffect({ ...input, item, execution })) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return (!execution ||
|
|
62
|
+
execution.status === 'queued' ||
|
|
63
|
+
(execution.status === 'retry_wait' &&
|
|
64
|
+
execution.nextAttemptAt !== null &&
|
|
65
|
+
execution.nextAttemptAt <= input.now + 5));
|
|
66
|
+
})
|
|
67
|
+
.map((item) => {
|
|
68
|
+
const existing = input.state.effectExecutions[item.effectId];
|
|
69
|
+
const attempt = existing?.status === 'queued' ? existing.attempt : (existing?.attempt ?? 0) + 1;
|
|
70
|
+
const retry = input.registry.effects.get(item.effect.type)?.retry;
|
|
71
|
+
return {
|
|
72
|
+
item,
|
|
73
|
+
attempt,
|
|
74
|
+
queuedEvent: existing?.status === 'queued'
|
|
75
|
+
? undefined
|
|
76
|
+
: createEvent(input.runId, {
|
|
77
|
+
type: 'runtime.effect.queued',
|
|
78
|
+
payload: { effectId: item.effectId, attempt, deadlineAt: null },
|
|
79
|
+
threadId: item.threadId,
|
|
80
|
+
causationId: item.causingEventId,
|
|
81
|
+
origin: { type: 'system' },
|
|
82
|
+
}),
|
|
83
|
+
task: workerTask(input.runId, item, attempt),
|
|
84
|
+
scheduleToStartDeadlineAt: retry?.scheduleToStartTimeoutMs
|
|
85
|
+
? input.now + retry.scheduleToStartTimeoutMs
|
|
86
|
+
: null,
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Hand a planned attempt to the worker. A rejected dispatch may still have reached the worker, so
|
|
92
|
+
* with a schedule-to-start deadline it is recorded as dispatched and recovered by that timeout;
|
|
93
|
+
* without one it is parked as ambiguous for an operator.
|
|
94
|
+
*/
|
|
95
|
+
export function dispatchWorkerEffect(runId, plan, worker) {
|
|
96
|
+
const dispatched = createEvent(runId, {
|
|
97
|
+
type: 'runtime.effect.dispatched',
|
|
98
|
+
payload: {
|
|
99
|
+
effectId: plan.item.effectId,
|
|
100
|
+
attempt: plan.attempt,
|
|
101
|
+
deadlineAt: plan.scheduleToStartDeadlineAt,
|
|
102
|
+
},
|
|
103
|
+
threadId: plan.item.threadId,
|
|
104
|
+
causationId: plan.item.causingEventId,
|
|
105
|
+
origin: { type: 'system' },
|
|
106
|
+
});
|
|
107
|
+
const ambiguous = (error) => createEvent(runId, {
|
|
108
|
+
type: 'runtime.effect.ambiguous',
|
|
109
|
+
payload: { effectId: plan.item.effectId, attempt: plan.attempt, error },
|
|
110
|
+
threadId: plan.item.threadId,
|
|
111
|
+
causationId: plan.item.causingEventId,
|
|
112
|
+
origin: { type: 'system' },
|
|
113
|
+
});
|
|
114
|
+
if (!worker) {
|
|
115
|
+
return Effect.succeed(ambiguous(`no effect worker is configured for "${plan.item.effect.type}"`));
|
|
116
|
+
}
|
|
117
|
+
return Effect.tryPromise({
|
|
118
|
+
try: () => Promise.resolve(worker.dispatch(plan.task)),
|
|
119
|
+
catch: (cause) => new RuntimeExecutionError(cause instanceof Error ? cause.message : String(cause)),
|
|
120
|
+
}).pipe(Effect.match({
|
|
121
|
+
onFailure: (error) => plan.scheduleToStartDeadlineAt === null
|
|
122
|
+
? ambiguous(`worker dispatch outcome is ambiguous: ${error.message}`)
|
|
123
|
+
: dispatched,
|
|
124
|
+
onSuccess: () => dispatched,
|
|
125
|
+
}));
|
|
126
|
+
}
|
package/dist/s2/config.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare const S2ConfigSchema: Schema.Struct<{
|
|
|
14
14
|
}>]>>;
|
|
15
15
|
readonly streamConfig: Schema.optional<Schema.Struct<{
|
|
16
16
|
readonly retentionPolicy: Schema.optional<Schema.Union<readonly [Schema.Struct<{
|
|
17
|
-
readonly ageSecs: Schema.
|
|
17
|
+
readonly ageSecs: Schema.Finite;
|
|
18
18
|
}>, Schema.Struct<{
|
|
19
19
|
readonly infinite: Schema.Struct<{}>;
|
|
20
20
|
}>]>>;
|
package/dist/s2/config.js
CHANGED
|
@@ -17,7 +17,7 @@ export const S2ConfigSchema = Schema.Struct({
|
|
|
17
17
|
])),
|
|
18
18
|
streamConfig: Schema.optional(Schema.Struct({
|
|
19
19
|
retentionPolicy: Schema.optional(Schema.Union([
|
|
20
|
-
Schema.Struct({ ageSecs: Schema.
|
|
20
|
+
Schema.Struct({ ageSecs: Schema.Finite }),
|
|
21
21
|
Schema.Struct({ infinite: Schema.Struct({}) }),
|
|
22
22
|
])),
|
|
23
23
|
storageClass: Schema.optional(Schema.Literals(['standard', 'express'])),
|
package/dist/s2/lite.js
CHANGED
|
@@ -3,7 +3,14 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as net from 'node:net';
|
|
4
4
|
import * as os from 'node:os';
|
|
5
5
|
import * as path from 'node:path';
|
|
6
|
+
import { Clock, Data, Effect } from 'effect';
|
|
6
7
|
import { s2 } from './store.js';
|
|
8
|
+
class S2LiteError extends Data.TaggedError('S2LiteError') {
|
|
9
|
+
constructor(message, cause) {
|
|
10
|
+
super({ cause, message });
|
|
11
|
+
this.name = 'S2LiteError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
7
14
|
export function findS2Binary(env) {
|
|
8
15
|
// 1. Check PATH
|
|
9
16
|
const pathDirs = (env.PATH ?? '').split(path.delimiter);
|
|
@@ -48,103 +55,84 @@ export function findS2Binary(env) {
|
|
|
48
55
|
return undefined;
|
|
49
56
|
}
|
|
50
57
|
export function isPortOpen(port, host = '127.0.0.1') {
|
|
51
|
-
return
|
|
58
|
+
return Effect.runPromise(Effect.callback((resume) => {
|
|
52
59
|
const socket = new net.Socket();
|
|
53
|
-
|
|
54
|
-
socket.once('connect', () => {
|
|
55
|
-
socket.destroy();
|
|
56
|
-
resolve(true);
|
|
57
|
-
});
|
|
58
|
-
socket.once('timeout', () => {
|
|
59
|
-
socket.destroy();
|
|
60
|
-
resolve(false);
|
|
61
|
-
});
|
|
62
|
-
socket.once('error', () => {
|
|
60
|
+
const complete = (open) => {
|
|
63
61
|
socket.destroy();
|
|
64
|
-
|
|
65
|
-
}
|
|
62
|
+
resume(Effect.succeed(open));
|
|
63
|
+
};
|
|
64
|
+
socket.setTimeout(200);
|
|
65
|
+
socket.once('connect', () => complete(true));
|
|
66
|
+
socket.once('timeout', () => complete(false));
|
|
67
|
+
socket.once('error', () => complete(false));
|
|
66
68
|
socket.connect(port, host);
|
|
67
|
-
|
|
69
|
+
return Effect.sync(() => socket.destroy());
|
|
70
|
+
}));
|
|
68
71
|
}
|
|
69
72
|
let activeProcess = null;
|
|
70
|
-
export
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// Did not spawn this process, no-op stop.
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
// 2. Locate the s2 binary.
|
|
85
|
-
const binaryPath = options.binaryPath ?? findS2Binary(options.env ?? {});
|
|
86
|
-
if (!binaryPath) {
|
|
87
|
-
throw new Error(`s2 CLI binary not found on PATH or in standard locations.
|
|
73
|
+
export function startS2Lite(options = {}) {
|
|
74
|
+
return Effect.runPromise(Effect.gen(function* () {
|
|
75
|
+
const port = options.port ?? 8080;
|
|
76
|
+
const endpoint = `http://127.0.0.1:${port}`;
|
|
77
|
+
if (yield* Effect.promise(() => isPortOpen(port))) {
|
|
78
|
+
return { port, endpoint, stop: () => undefined };
|
|
79
|
+
}
|
|
80
|
+
const binaryPath = options.binaryPath ?? findS2Binary(options.env ?? {});
|
|
81
|
+
if (!binaryPath) {
|
|
82
|
+
return yield* new S2LiteError(`s2 CLI binary not found on PATH or in standard locations.
|
|
88
83
|
To install s2-lite for local development:
|
|
89
84
|
flox activate # installs into $FLOX_ENV_CACHE/s2 (preferred)
|
|
90
85
|
bun run setup:s2 # scripts/install-s2-cli.sh
|
|
91
86
|
https://s2.dev/docs/cli/installation`);
|
|
92
|
-
}
|
|
93
|
-
// 3. Spawn s2 lite.
|
|
94
|
-
// SAFETY: child_process.spawn returns child process matching S2ChildProcess.
|
|
95
|
-
const child = childProcess.spawn(binaryPath, ['lite', '--port', String(port)], {
|
|
96
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
97
|
-
detached: false,
|
|
98
|
-
});
|
|
99
|
-
activeProcess = child;
|
|
100
|
-
const cleanup = () => {
|
|
101
|
-
if (activeProcess === child) {
|
|
102
|
-
activeProcess = null;
|
|
103
87
|
}
|
|
104
|
-
try
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
88
|
+
const child = yield* Effect.try({
|
|
89
|
+
try: () => childProcess.spawn(binaryPath, ['lite', '--port', String(port)], {
|
|
90
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
91
|
+
detached: false,
|
|
92
|
+
}),
|
|
93
|
+
catch: (cause) => new S2LiteError('Failed to start s2 lite', cause),
|
|
94
|
+
});
|
|
95
|
+
activeProcess = child;
|
|
96
|
+
const cleanup = () => {
|
|
97
|
+
if (activeProcess === child) {
|
|
98
|
+
activeProcess = null;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
child.kill('SIGTERM');
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// The process may already have exited.
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
process.once('exit', cleanup);
|
|
108
|
+
process.once('SIGINT', cleanup);
|
|
109
|
+
process.once('SIGTERM', cleanup);
|
|
110
|
+
let stderrOutput = '';
|
|
111
|
+
child.stderr?.on('data', (chunk) => {
|
|
112
|
+
stderrOutput += chunk.toString();
|
|
113
|
+
});
|
|
114
|
+
const deadline = (yield* Clock.currentTimeMillis) + 5_000;
|
|
115
|
+
while ((yield* Clock.currentTimeMillis) < deadline) {
|
|
116
|
+
if (child.exitCode !== null) {
|
|
117
|
+
return yield* new S2LiteError(`s2 lite process exited with code ${child.exitCode} before becoming ready: ${stderrOutput}`);
|
|
118
|
+
}
|
|
119
|
+
if (yield* Effect.promise(() => isPortOpen(port))) {
|
|
120
|
+
return { port, endpoint, stop: cleanup };
|
|
121
|
+
}
|
|
122
|
+
yield* Effect.sleep(50);
|
|
131
123
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
throw new Error(`Timed out waiting for s2 lite to start on port ${port}: ${stderrOutput}`);
|
|
124
|
+
cleanup();
|
|
125
|
+
return yield* new S2LiteError(`Timed out waiting for s2 lite to start on port ${port}: ${stderrOutput}`);
|
|
126
|
+
}));
|
|
136
127
|
}
|
|
137
128
|
/**
|
|
138
129
|
* Returns a factory function for createLooms that automatically starts local s2-lite
|
|
139
130
|
* and connects an S2 EventStore to it.
|
|
140
131
|
*/
|
|
141
132
|
export function s2Lite(options = {}) {
|
|
142
|
-
return
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
endpoint: started.endpoint,
|
|
148
|
-
});
|
|
149
|
-
};
|
|
133
|
+
return () => startS2Lite(options).then((started) => s2({
|
|
134
|
+
basin: options.basin ?? 'looms-demo',
|
|
135
|
+
accessToken: options.accessToken ?? 's2_local',
|
|
136
|
+
endpoint: started.endpoint,
|
|
137
|
+
}));
|
|
150
138
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Effect, Stream } from 'effect';
|
|
2
|
+
export interface S2ReadBatchLike<T> {
|
|
3
|
+
readonly records: ReadonlyArray<T>;
|
|
4
|
+
readonly tail?: {
|
|
5
|
+
readonly seqNum: number;
|
|
6
|
+
} | null;
|
|
7
|
+
}
|
|
8
|
+
export interface ReadAllPagesOptions {
|
|
9
|
+
readonly fromSeq?: number;
|
|
10
|
+
readonly limit?: number;
|
|
11
|
+
readonly maxPageSize?: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function paginateS2Stream<T extends {
|
|
14
|
+
readonly seqNum: number;
|
|
15
|
+
}, E>(fetchPage: (cursor: number, count: number) => Effect.Effect<S2ReadBatchLike<T> | null, E>, options?: ReadAllPagesOptions): Stream.Stream<T, E>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Effect, Option, Stream } from 'effect';
|
|
2
|
+
export function paginateS2Stream(fetchPage, options) {
|
|
3
|
+
const fromSeq = options?.fromSeq ?? 0;
|
|
4
|
+
const userLimit = options?.limit;
|
|
5
|
+
const maxPageSize = options?.maxPageSize ?? 1000;
|
|
6
|
+
if (userLimit !== undefined && userLimit <= 0) {
|
|
7
|
+
return Stream.empty;
|
|
8
|
+
}
|
|
9
|
+
const baseStream = Stream.paginate({ cursor: fromSeq, remaining: userLimit }, (state) => Effect.gen(function* () {
|
|
10
|
+
if (state.remaining !== undefined && state.remaining <= 0) {
|
|
11
|
+
return [[], Option.none()];
|
|
12
|
+
}
|
|
13
|
+
const count = state.remaining !== undefined ? Math.min(maxPageSize, state.remaining) : maxPageSize;
|
|
14
|
+
const page = yield* fetchPage(state.cursor, count);
|
|
15
|
+
if (!page || page.records.length === 0) {
|
|
16
|
+
return [[], Option.none()];
|
|
17
|
+
}
|
|
18
|
+
const records = page.records.filter((record) => record.seqNum >= fromSeq);
|
|
19
|
+
const nextRemaining = state.remaining !== undefined ? state.remaining - records.length : undefined;
|
|
20
|
+
const lastRecord = page.records[page.records.length - 1];
|
|
21
|
+
if (!lastRecord || (nextRemaining !== undefined && nextRemaining <= 0)) {
|
|
22
|
+
return [records, Option.none()];
|
|
23
|
+
}
|
|
24
|
+
const nextCursor = Math.max(state.cursor + 1, lastRecord.seqNum + 1);
|
|
25
|
+
if (nextCursor <= state.cursor) {
|
|
26
|
+
return [records, Option.none()];
|
|
27
|
+
}
|
|
28
|
+
// Byte-capped pages can be shorter than requested while more records remain.
|
|
29
|
+
const shortPage = page.records.length < count;
|
|
30
|
+
const reachedTail = !shortPage && page.tail?.seqNum !== undefined && nextCursor >= page.tail.seqNum;
|
|
31
|
+
return reachedTail
|
|
32
|
+
? [records, Option.none()]
|
|
33
|
+
: [records, Option.some({ cursor: nextCursor, remaining: nextRemaining })];
|
|
34
|
+
}));
|
|
35
|
+
return userLimit !== undefined ? baseStream.pipe(Stream.take(userLimit)) : baseStream;
|
|
36
|
+
}
|
package/dist/s2/projector.js
CHANGED
|
@@ -10,9 +10,10 @@ export function s2Projector(config) {
|
|
|
10
10
|
const store = s2(config);
|
|
11
11
|
return {
|
|
12
12
|
name: 's2',
|
|
13
|
-
|
|
13
|
+
version: '1',
|
|
14
|
+
project: (events) => {
|
|
14
15
|
if (events.length === 0) {
|
|
15
|
-
return;
|
|
16
|
+
return Promise.resolve();
|
|
16
17
|
}
|
|
17
18
|
const byRun = new Map();
|
|
18
19
|
for (const event of events) {
|
|
@@ -20,9 +21,9 @@ export function s2Projector(config) {
|
|
|
20
21
|
batch.push(event);
|
|
21
22
|
byRun.set(event.runId, batch);
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
24
|
+
return Effect.runPromise(Effect.forEach(byRun, ([runId, batch]) => store.append(runId, batch), {
|
|
25
|
+
discard: true,
|
|
26
|
+
}));
|
|
26
27
|
},
|
|
27
28
|
};
|
|
28
29
|
}
|