@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.
- package/CHANGELOG.md +53 -0
- package/README.md +2 -0
- 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 +55 -32
- 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/definitions-store.d.ts +2 -3
- package/dist/agent/definitions-store.js +2 -4
- package/dist/agent/definitions.d.ts +46 -20
- package/dist/agent/definitions.js +36 -8
- 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 +24 -1
- package/dist/agent/signals.js +77 -0
- package/dist/agent/threads.d.ts +3 -33
- package/dist/agent/threads.js +20 -25
- package/dist/agent/tool-schema.js +0 -1
- 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 +23 -0
- package/dist/ai-vercel/vercel-evaluator.js +68 -0
- package/dist/ai-vercel/vercel-llm.js +64 -44
- 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 +1195 -18
- package/dist/client/client.js +118 -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.js +13 -14
- 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 +18 -2
- 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 +5 -1
- package/dist/core/index.js +5 -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 +16 -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/replay.d.ts +312 -3
- package/dist/core/replay.js +11 -1
- package/dist/core/schema.d.ts +8 -19
- package/dist/core/schema.js +25 -80
- package/dist/core/serialize.js +2 -4
- package/dist/core/snapshot-store.d.ts +2 -1
- package/dist/core/snapshot-store.js +8 -2
- package/dist/core/snapshots.d.ts +5 -1
- package/dist/core/snapshots.js +10 -15
- package/dist/core/sqlite-store.js +56 -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/components/event-stream.js +9 -10
- package/dist/debugger/events/protocol-summarize.js +26 -0
- package/dist/debugger/lib/event-meta.js +1 -2
- 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 +2 -2
- 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 +22 -4
- package/dist/runtime/looms.js +54 -36
- 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 +278 -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 +233 -470
- package/dist/runtime/server/auth.d.ts +40 -0
- package/dist/runtime/server/auth.js +40 -0
- package/dist/runtime/server/fetch-handler.d.ts +14 -0
- package/dist/runtime/server/fetch-handler.js +51 -0
- package/dist/runtime/server/http.d.ts +37 -0
- package/dist/runtime/server/http.js +62 -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/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 +192 -0
- package/dist/runtime/server.d.ts +2 -8
- package/dist/runtime/server.js +4 -211
- package/dist/runtime/sse.d.ts +2 -1
- package/dist/runtime/sse.js +24 -17
- package/dist/runtime/types.d.ts +115 -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 +148 -163
- 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/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 +359 -13
- 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 +8 -2
- package/dist/runtime/events-api.d.ts +0 -4
- package/dist/runtime/events-api.js +0 -84
package/dist/runtime/runtime.js
CHANGED
|
@@ -1,213 +1,138 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { Clock, Effect, ManagedRuntime, Predicate } from 'effect';
|
|
2
|
+
import { composeModules, createEvent, createThreadId, createRunId, DEFAULT_DEFINITION_VERSION, DEFAULT_SNAPSHOT_EVERY, definitionKey, EventStoreTag, foldRun, project, replayTo, } from '@swirls/looms/core';
|
|
3
|
+
import { createCancellation } from './cancellation.js';
|
|
4
|
+
import { nextRuntimeDeadline } from './deadline-transition-planner.js';
|
|
5
|
+
import { RuntimeExecutionError } from './errors.js';
|
|
6
|
+
import { admitExternalEvents, moduleServices, resolveSnapshotStore, RunCursorCache, threadStartedEvents, UnknownDefinitionError, validateAndCreateEvents, waitSatisfiedEvents, } from './helpers.js';
|
|
7
|
+
import { createRunIngress } from './ingress.js';
|
|
8
|
+
import { notifyObserver } from './observer.js';
|
|
9
|
+
import { createRuntimeRepository } from './repository.js';
|
|
10
|
+
import { RunExecutionContexts } from './run-execution-context.js';
|
|
11
|
+
import { assertSameDurableStart } from './run-start.js';
|
|
12
|
+
import { createWakeEngine } from './wake-engine.js';
|
|
13
|
+
import { createTimeoutScheduler } from './wake-scheduler.js';
|
|
14
|
+
import { createWorkerCallbacks } from './worker-callbacks.js';
|
|
6
15
|
export { createTimeoutScheduler } from './wake-scheduler.js';
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
effectId,
|
|
12
|
-
threadId,
|
|
13
|
-
message: `Duplicate effect dispatch detected for effectId "${effectId}" on thread "${threadId}"; refusing to re-dispatch already executed effect`,
|
|
14
|
-
});
|
|
15
|
-
this.name = 'DuplicateEffectDispatchError';
|
|
16
|
-
}
|
|
16
|
+
export { DuplicateEffectDispatchError, MaxWakeIterationsError, StartRunConflictError, } from './errors.js';
|
|
17
|
+
export { RunOverloadedError } from './ingress.js';
|
|
18
|
+
function currentTimeMillis() {
|
|
19
|
+
return Math.floor(performance.timeOrigin + performance.now());
|
|
17
20
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
/** Threads whose in-process effects an admitted signal interrupts. */
|
|
22
|
+
function interruptedThreadIds(events, rules) {
|
|
23
|
+
const threadIds = new Set();
|
|
24
|
+
for (const event of events) {
|
|
25
|
+
const payload = Predicate.isObject(event.payload) ? event.payload : {};
|
|
26
|
+
if (event.type === 'runtime.thread.cancel.requested' && Predicate.isString(payload.threadId)) {
|
|
27
|
+
threadIds.add(payload.threadId);
|
|
28
|
+
}
|
|
29
|
+
for (const rule of rules) {
|
|
30
|
+
for (const threadId of rule(event)) {
|
|
31
|
+
threadIds.add(threadId);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
26
34
|
}
|
|
35
|
+
return threadIds;
|
|
27
36
|
}
|
|
28
37
|
const DEFAULT_RUN_CACHE_SIZE = 0;
|
|
38
|
+
const RESCAN_CONCURRENCY = 16;
|
|
29
39
|
const DEFAULT_KEEP_SNAPSHOTS = 1;
|
|
40
|
+
const DEFAULT_MAX_PENDING_RUN_OPERATIONS = 256;
|
|
30
41
|
export function createRuntime(options) {
|
|
31
42
|
const registry = composeModules(options.modules);
|
|
32
43
|
const registeredDefinitions = registry.definitions;
|
|
33
|
-
const definitions = new Map(registeredDefinitions.map((def) => [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
44
|
+
const definitions = new Map(registeredDefinitions.map((def) => [definitionKey(def.kind, def.name, def.version), def]));
|
|
45
|
+
// Module composition erases the heterogeneous service identifier union at this host boundary.
|
|
46
|
+
// oxlint-disable-next-line effecttsgo/any-unknown-in-error-context
|
|
47
|
+
const services = ManagedRuntime.make(moduleServices(options.modules));
|
|
48
|
+
const executionContexts = new RunExecutionContexts();
|
|
49
|
+
const maxPendingRunOperations = options.maxPendingRunOperations ?? DEFAULT_MAX_PENDING_RUN_OPERATIONS;
|
|
38
50
|
const snapshotEvery = options.snapshotEvery ?? DEFAULT_SNAPSHOT_EVERY;
|
|
39
51
|
const maxWakeIterations = options.maxWakeIterations ?? 100;
|
|
40
52
|
const snapshotStore = resolveSnapshotStore(options.snapshotStore, options.store);
|
|
41
53
|
const trimAfterSnapshot = options.trimAfterSnapshot;
|
|
42
54
|
const keepSnapshots = trimAfterSnapshot?.keepSnapshots ?? DEFAULT_KEEP_SNAPSHOTS;
|
|
43
55
|
const runCache = new RunCursorCache(options.runCacheSize ?? DEFAULT_RUN_CACHE_SIZE);
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
});
|
|
55
|
-
const readRemaining = (store, runId, events, expectedTail) => Effect.gen(function* () {
|
|
56
|
-
let collected = events;
|
|
57
|
-
while (collected.length > 0) {
|
|
58
|
-
const lastSeq = collected[collected.length - 1].seq;
|
|
59
|
-
const target = expectedTail ?? (yield* store.tail(runId));
|
|
60
|
-
if (lastSeq >= target) {
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
const more = yield* store.read(runId, {
|
|
64
|
-
fromSeq: lastSeq + 1,
|
|
65
|
-
limit: expectedTail !== undefined ? Math.max(0, target - lastSeq) : undefined,
|
|
66
|
-
});
|
|
67
|
-
if (more.length === 0 || more[0].seq <= lastSeq) {
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
collected = [...collected, ...more];
|
|
71
|
-
}
|
|
72
|
-
return collected;
|
|
56
|
+
const observe = (event) => notifyObserver(options.observer, event);
|
|
57
|
+
const withRunIngress = createRunIngress(executionContexts, maxPendingRunOperations);
|
|
58
|
+
const repository = createRuntimeRepository({
|
|
59
|
+
registry,
|
|
60
|
+
snapshotStore,
|
|
61
|
+
trimAfterSnapshot,
|
|
62
|
+
keepSnapshots,
|
|
63
|
+
runCache,
|
|
64
|
+
executionContexts,
|
|
65
|
+
observer: options.observer,
|
|
73
66
|
});
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
if (bounds.head > fromSeq) {
|
|
89
|
-
return yield* Effect.fail(new EventStoreTrimmedError(runId, bounds.head, fromSeq));
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
return yield* Effect.fail(new EventStoreTrimmedError(runId, firstSeq, fromSeq));
|
|
67
|
+
const { commit, loadCursor } = repository;
|
|
68
|
+
const signalAdmitted = (runId, events, signalOpts) => Effect.gen(function* () {
|
|
69
|
+
const store = yield* EventStoreTag;
|
|
70
|
+
const signalKey = signalOpts?.idempotencyKey;
|
|
71
|
+
const committed = yield* commit(store, runId, (current) => Effect.gen(function* () {
|
|
72
|
+
const processed = current.state.processedIdempotencyKeys;
|
|
73
|
+
const filteredEvents = signalKey && processed.includes(signalKey)
|
|
74
|
+
? []
|
|
75
|
+
: events.filter((input) => {
|
|
76
|
+
const key = input.idempotencyKey ?? signalKey;
|
|
77
|
+
return !key || !processed.includes(key);
|
|
78
|
+
});
|
|
79
|
+
if (filteredEvents.length === 0) {
|
|
80
|
+
return { events: [], value: current.state };
|
|
94
81
|
}
|
|
82
|
+
const batch = yield* validateAndCreateEvents(registry.catalogs, runId, filteredEvents.map((input) => ({
|
|
83
|
+
...input,
|
|
84
|
+
idempotencyKey: input.idempotencyKey ?? signalKey,
|
|
85
|
+
})), { origin: { type: 'external' } });
|
|
86
|
+
const folded = foldRun(batch, registry, { runId, initial: current.state });
|
|
87
|
+
const satisfiedEvents = waitSatisfiedEvents(folded, batch).map((input) => createEvent(runId, input));
|
|
88
|
+
return {
|
|
89
|
+
events: [...batch, ...satisfiedEvents],
|
|
90
|
+
value: current.state,
|
|
91
|
+
idempotencyKey: signalKey ? `signal:${signalKey}` : undefined,
|
|
92
|
+
};
|
|
93
|
+
}));
|
|
94
|
+
if (committed.append) {
|
|
95
|
+
executionContexts.abort(runId, interruptedThreadIds(events, registry.interruptRules), 'Effect interrupted');
|
|
95
96
|
}
|
|
96
|
-
|
|
97
|
-
return advanceCursor(cursor, delta, registry);
|
|
97
|
+
return committed.value;
|
|
98
98
|
});
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
const fromSeq = cursor.seq + 1;
|
|
114
|
-
let delta = yield* store.read(runId, { fromSeq });
|
|
115
|
-
if (cursor.seq === 0 && delta[0] && delta[0].seq > 1) {
|
|
116
|
-
if (store.bounds) {
|
|
117
|
-
const bounds = yield* store.bounds(runId);
|
|
118
|
-
if (bounds.head > 1) {
|
|
119
|
-
return yield* Effect.fail(new EventStoreTrimmedError(runId, bounds.head, 1));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
return yield* Effect.fail(new EventStoreTrimmedError(runId, delta[0].seq, 1));
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
if (delta.length > 0) {
|
|
127
|
-
delta = yield* readRemaining(store, runId, delta);
|
|
128
|
-
}
|
|
129
|
-
return advanceCursor(cursor, delta, registry);
|
|
99
|
+
let scheduler;
|
|
100
|
+
let runtime;
|
|
101
|
+
const wakeEngine = createWakeEngine({
|
|
102
|
+
registry,
|
|
103
|
+
services,
|
|
104
|
+
definitions,
|
|
105
|
+
executionContexts,
|
|
106
|
+
repository,
|
|
107
|
+
snapshotEvery,
|
|
108
|
+
maxWakeIterations,
|
|
109
|
+
worker: options.worker,
|
|
110
|
+
observer: options.observer,
|
|
111
|
+
scheduler: () => scheduler,
|
|
130
112
|
});
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
runCache.delete(runId);
|
|
138
|
-
return yield* loadCursorCold(store, runId);
|
|
139
|
-
}
|
|
140
|
-
if (tail === cached.seq) {
|
|
141
|
-
return cached;
|
|
142
|
-
}
|
|
143
|
-
const next = yield* catchUp(store, runId, cached, tail).pipe(Effect.catchTag('EventStoreTrimmedError', () => {
|
|
144
|
-
runCache.delete(runId);
|
|
145
|
-
return loadCursorCold(store, runId);
|
|
146
|
-
}));
|
|
147
|
-
runCache.set(runId, next);
|
|
148
|
-
return next;
|
|
149
|
-
}
|
|
150
|
-
const next = yield* loadCursorCold(store, runId);
|
|
151
|
-
runCache.set(runId, next);
|
|
152
|
-
return next;
|
|
113
|
+
const workerCallbacks = createWorkerCallbacks({
|
|
114
|
+
registry,
|
|
115
|
+
repository,
|
|
116
|
+
withRunIngress,
|
|
117
|
+
observer: options.observer,
|
|
118
|
+
wake: wakeEngine,
|
|
153
119
|
});
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
yield* snapshotStore.save({
|
|
164
|
-
runId,
|
|
165
|
-
cursor: cursor.seq,
|
|
166
|
-
stateHash: hashRunState(cursor.state),
|
|
167
|
-
takenAt: Date.now(),
|
|
168
|
-
state: cursor.state,
|
|
169
|
-
});
|
|
170
|
-
if (snapshotStore.prune) {
|
|
171
|
-
yield* snapshotStore.prune(runId, keepSnapshots);
|
|
172
|
-
}
|
|
173
|
-
if (trimAfterSnapshot && store.trim && snapshotStore.listCursors) {
|
|
174
|
-
const cursors = yield* snapshotStore.listCursors(runId);
|
|
175
|
-
const oldestKept = cursors[Math.max(0, cursors.length - keepSnapshots)];
|
|
176
|
-
if (oldestKept !== undefined) {
|
|
177
|
-
yield* store.trim(runId, oldestKept + 1);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return { ...cursor, durableSinceSnapshot: 0 };
|
|
181
|
-
}).pipe(Effect.catch(() => Effect.succeed(cursor)));
|
|
182
|
-
const applyAppend = (store, runId, cursor, batch, appendResult, commandRecords = 0) => Effect.gen(function* () {
|
|
183
|
-
const echo = assignSequences(batch, appendResult);
|
|
184
|
-
const first = echo[0];
|
|
185
|
-
let next = cursor;
|
|
186
|
-
if (first && first.seq !== cursor.seq + 1 + commandRecords) {
|
|
187
|
-
const gap = first.seq - 1 - cursor.seq;
|
|
188
|
-
const live = liveCount.get(runId) ?? 0;
|
|
189
|
-
if (live === gap) {
|
|
190
|
-
next = { ...cursor, seq: first.seq - 1 };
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
next = yield* catchUp(store, runId, cursor, first.seq - 1);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
next = advanceCursor(next, echo, registry);
|
|
197
|
-
liveCount.set(runId, 0);
|
|
198
|
-
if (next.seq !== appendResult.tail) {
|
|
199
|
-
next = yield* catchUp(store, runId, next, appendResult.tail);
|
|
200
|
-
}
|
|
201
|
-
runCache.set(runId, next);
|
|
202
|
-
return next;
|
|
120
|
+
const cancellation = createCancellation({
|
|
121
|
+
registry,
|
|
122
|
+
repository,
|
|
123
|
+
executionContexts,
|
|
124
|
+
worker: options.worker,
|
|
125
|
+
observer: options.observer,
|
|
126
|
+
wake: wakeEngine,
|
|
127
|
+
withRunIngress,
|
|
203
128
|
});
|
|
204
|
-
|
|
205
|
-
const runtime = {
|
|
129
|
+
runtime = {
|
|
206
130
|
modules: options.modules,
|
|
207
131
|
registry,
|
|
208
|
-
|
|
132
|
+
...workerCallbacks,
|
|
133
|
+
listRuns: Effect.gen(function* () {
|
|
209
134
|
const store = yield* EventStoreTag;
|
|
210
|
-
return yield* store.listRuns
|
|
135
|
+
return yield* store.listRuns;
|
|
211
136
|
}),
|
|
212
137
|
getEvents: (runId, readOptions) => Effect.gen(function* () {
|
|
213
138
|
const store = yield* EventStoreTag;
|
|
@@ -226,306 +151,144 @@ export function createRuntime(options) {
|
|
|
226
151
|
const events = yield* runtime.getEvents(runId);
|
|
227
152
|
return replayTo(events, registry, seq);
|
|
228
153
|
}),
|
|
229
|
-
|
|
154
|
+
inspectRun: (runId) => Effect.gen(function* () {
|
|
230
155
|
const store = yield* EventStoreTag;
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
yield* toEffectVoid(scheduler.schedule(runId, earliestTimerAt));
|
|
248
|
-
count += 1;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return count;
|
|
156
|
+
const state = yield* runtime.getRun(runId);
|
|
157
|
+
const tail = yield* store.tail(runId);
|
|
158
|
+
const bounds = store.bounds ? yield* store.bounds(runId) : { head: 1, tail };
|
|
159
|
+
const effects = state.outstandingEffects.map((item) => {
|
|
160
|
+
const execution = state.effectExecutions[item.effectId];
|
|
161
|
+
return {
|
|
162
|
+
effectId: item.effectId,
|
|
163
|
+
threadId: item.threadId,
|
|
164
|
+
type: item.effect.type,
|
|
165
|
+
attempt: execution?.attempt ?? 0,
|
|
166
|
+
status: execution?.status ?? 'pending',
|
|
167
|
+
deadlineAt: execution?.deadlineAt ?? null,
|
|
168
|
+
lastError: execution?.lastError ?? null,
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
return { runId, state, head: bounds.head, tail, effects };
|
|
252
172
|
}),
|
|
253
|
-
|
|
254
|
-
if (!definitions.get(`${args.kind}:${args.definitionName}`)) {
|
|
255
|
-
return yield* Effect.fail(new Error(`Unknown definition ${args.kind}:${args.definitionName}`));
|
|
256
|
-
}
|
|
173
|
+
retryEffect: (runId, effectId) => withRunIngress(runId, Effect.gen(function* () {
|
|
257
174
|
const store = yield* EventStoreTag;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (
|
|
262
|
-
|
|
263
|
-
existing.state.rootThreadId) {
|
|
264
|
-
return {
|
|
265
|
-
runId,
|
|
266
|
-
threadId: existing.state.rootThreadId ?? '',
|
|
267
|
-
state: existing.state,
|
|
268
|
-
};
|
|
175
|
+
yield* commit(store, runId, (cursor) => Effect.gen(function* () {
|
|
176
|
+
const execution = cursor.state.effectExecutions[effectId];
|
|
177
|
+
const item = cursor.state.outstandingEffects.find((candidate) => candidate.effectId === effectId);
|
|
178
|
+
if (!execution || !item) {
|
|
179
|
+
return yield* new RuntimeExecutionError(`Unknown outstanding effect "${effectId}"`);
|
|
269
180
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
});
|
|
279
|
-
const started = startedEvents[0];
|
|
280
|
-
const startedInput = started && Predicate.isObject(started.payload)
|
|
281
|
-
? (started.payload.input ?? null)
|
|
282
|
-
: (args.input ?? null);
|
|
283
|
-
const batch = yield* validateAndCreateEvents(registry.catalogs, runId, [
|
|
284
|
-
{
|
|
285
|
-
type: 'runtime.run.started',
|
|
181
|
+
if (execution.status !== 'ambiguous' &&
|
|
182
|
+
execution.status !== 'timed_out' &&
|
|
183
|
+
execution.status !== 'cancel_requested') {
|
|
184
|
+
return yield* new RuntimeExecutionError(`Effect "${effectId}" cannot be retried from status "${execution.status}"`);
|
|
185
|
+
}
|
|
186
|
+
const now = yield* Clock.currentTimeMillis;
|
|
187
|
+
const retryEvent = createEvent(runId, {
|
|
188
|
+
type: 'runtime.effect.retry.scheduled',
|
|
286
189
|
payload: {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
190
|
+
effectId,
|
|
191
|
+
attempt: execution.attempt,
|
|
192
|
+
nextAttemptAt: now,
|
|
193
|
+
error: `operator retry from ${execution.status}`,
|
|
291
194
|
},
|
|
292
|
-
threadId:
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}),
|
|
301
|
-
signal: (runId, events, signalOpts) => Effect.gen(function* () {
|
|
302
|
-
const store = yield* EventStoreTag;
|
|
303
|
-
const current = yield* loadCursor(store, runId);
|
|
304
|
-
const currentState = current.state;
|
|
305
|
-
if (signalOpts?.idempotencyKey &&
|
|
306
|
-
currentState.processedIdempotencyKeys?.includes(signalOpts.idempotencyKey)) {
|
|
307
|
-
return currentState;
|
|
308
|
-
}
|
|
309
|
-
const filteredEvents = events.filter((input) => {
|
|
310
|
-
const key = input.idempotencyKey ?? signalOpts?.idempotencyKey;
|
|
311
|
-
return !key || !currentState.processedIdempotencyKeys?.includes(key);
|
|
312
|
-
});
|
|
313
|
-
if (filteredEvents.length === 0) {
|
|
314
|
-
return currentState;
|
|
315
|
-
}
|
|
316
|
-
const batch = yield* validateAndCreateEvents(registry.catalogs, runId, filteredEvents.map((input) => ({
|
|
317
|
-
...input,
|
|
318
|
-
idempotencyKey: input.idempotencyKey ?? signalOpts?.idempotencyKey,
|
|
319
|
-
})), { origin: { type: 'external' } });
|
|
320
|
-
const folded = foldRun(batch, registry, { runId, initial: currentState });
|
|
321
|
-
const satisfied = waitSatisfiedEvents(folded, batch);
|
|
322
|
-
const satisfiedEvents = satisfied.map((input) => createEvent(runId, input));
|
|
323
|
-
yield* store.append(runId, stripSeq([...batch, ...satisfiedEvents]));
|
|
324
|
-
return yield* runtime.wake(runId);
|
|
325
|
-
}),
|
|
326
|
-
cancel: (runId, threadId) => Effect.gen(function* () {
|
|
195
|
+
threadId: item.threadId,
|
|
196
|
+
causationId: item.causingEventId,
|
|
197
|
+
origin: { type: 'external', actorId: 'operator' },
|
|
198
|
+
});
|
|
199
|
+
return { events: [retryEvent], value: undefined };
|
|
200
|
+
}));
|
|
201
|
+
})).pipe(Effect.andThen(runtime.wake(runId))),
|
|
202
|
+
rescanTimers: Effect.gen(function* () {
|
|
327
203
|
const store = yield* EventStoreTag;
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
204
|
+
const runIds = yield* store.listRuns;
|
|
205
|
+
const scheduled = yield* Effect.forEach(runIds, (runId) => Effect.gen(function* () {
|
|
206
|
+
const deadline = nextRuntimeDeadline(yield* runtime.getRun(runId));
|
|
207
|
+
if (deadline === undefined) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
yield* scheduler.schedule(runId, deadline);
|
|
211
|
+
return true;
|
|
212
|
+
}), { concurrency: RESCAN_CONCURRENCY });
|
|
213
|
+
return scheduled.filter(Boolean).length;
|
|
337
214
|
}),
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
payload: { timerId: record.waitId, waitId: record.waitId },
|
|
375
|
-
threadId: record.threadId,
|
|
376
|
-
origin: { type: 'system' },
|
|
377
|
-
}));
|
|
378
|
-
}
|
|
379
|
-
const stateAfterFired = foldRun(batch, registry, { runId, initial: state });
|
|
380
|
-
const satisfied = waitSatisfiedEvents(stateAfterFired, batch);
|
|
381
|
-
const satisfiedEvents = satisfied.map((input) => createEvent(runId, input));
|
|
382
|
-
const fullTimerBatch = [...batch, ...satisfiedEvents];
|
|
383
|
-
const appendResult = yield* store.append(runId, stripSeq(fullTimerBatch));
|
|
384
|
-
cursor = yield* applyAppend(store, runId, cursor, stripSeq(fullTimerBatch), appendResult);
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
const outstanding = state.outstandingEffects;
|
|
388
|
-
if (outstanding.length === 0) {
|
|
389
|
-
break;
|
|
390
|
-
}
|
|
391
|
-
for (const item of outstanding) {
|
|
392
|
-
if (dispatchedEffectIds.has(item.effectId)) {
|
|
393
|
-
return yield* Effect.fail(new DuplicateEffectDispatchError(runId, item.effectId, item.threadId));
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
for (const item of outstanding) {
|
|
397
|
-
dispatchedEffectIds.add(item.effectId);
|
|
398
|
-
}
|
|
399
|
-
const groupKeys = groupOutstanding(outstanding);
|
|
400
|
-
const groupResults = yield* Effect.forEach(groupKeys, (groupKey) => Effect.gen(function* () {
|
|
401
|
-
const group = outstanding.filter((item) => `${item.threadId}:${item.causingSeq}` === groupKey);
|
|
402
|
-
const groupProduced = [];
|
|
403
|
-
let failedEffectId;
|
|
404
|
-
for (const item of group) {
|
|
405
|
-
if (failedEffectId && item.effect.type === 'runtime.wait') {
|
|
406
|
-
groupProduced.push(createEffectFailedEvent(runId, item, withdrawnError(failedEffectId)));
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
|
-
const appendLive = async (input) => {
|
|
410
|
-
const validated = await Effect.runPromise(validateEventInput(registry.catalogs, input));
|
|
411
|
-
const liveEvent = createLiveEvent(runId, item, validated);
|
|
412
|
-
if (liveEvent) {
|
|
413
|
-
await Effect.runPromise(liveAppends.push(runId, liveEvent));
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
const outcomes = yield* dispatchEffect(registry, services, definitions, item.effect, {
|
|
417
|
-
effectId: item.effectId,
|
|
418
|
-
runId,
|
|
419
|
-
threadId: item.threadId,
|
|
420
|
-
causingEventId: item.causingEventId,
|
|
421
|
-
emit: appendLive,
|
|
422
|
-
});
|
|
423
|
-
const before = groupProduced.length;
|
|
424
|
-
if (outcomes.length === 0) {
|
|
425
|
-
groupProduced.push(createEffectFailedEvent(runId, item, 'empty-outcome'));
|
|
426
|
-
}
|
|
427
|
-
for (const input of outcomes) {
|
|
428
|
-
groupProduced.push(yield* materializeEffectOutcome(registry.catalogs, runId, item, input));
|
|
429
|
-
}
|
|
430
|
-
const groupOutcomes = groupProduced.slice(before);
|
|
431
|
-
if (item.effect.type !== 'runtime.wait' &&
|
|
432
|
-
groupOutcomes.some((event) => event.type === 'runtime.effect.failed')) {
|
|
433
|
-
failedEffectId = item.effectId;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
return groupProduced;
|
|
437
|
-
}), { concurrency: 'unbounded' });
|
|
438
|
-
const produced = groupResults.flat();
|
|
439
|
-
if (produced.length === 0) {
|
|
440
|
-
break;
|
|
441
|
-
}
|
|
442
|
-
yield* liveAppends.drain(runId);
|
|
443
|
-
const beforeFold = cursor.state;
|
|
444
|
-
const intermediateState = foldRun(produced, registry, { runId, initial: beforeFold });
|
|
445
|
-
const synthesized = synthesizedThreadFailed(beforeFold, intermediateState, produced);
|
|
446
|
-
const synthesizedEvents = synthesized.map((input) => createEvent(runId, input));
|
|
447
|
-
const stateAfterSynth = synthesizedEvents.length > 0
|
|
448
|
-
? foldRun(synthesizedEvents, registry, { runId, initial: intermediateState })
|
|
449
|
-
: intermediateState;
|
|
450
|
-
const allNewEvents = [...produced, ...synthesizedEvents];
|
|
451
|
-
const satisfied = waitSatisfiedEvents(stateAfterSynth, allNewEvents);
|
|
452
|
-
const satisfiedEvents = satisfied.map((input) => createEvent(runId, input));
|
|
453
|
-
const fullBatch = [...allNewEvents, ...satisfiedEvents];
|
|
454
|
-
const appendResult = yield* store.append(runId, stripSeq(fullBatch));
|
|
455
|
-
cursor = yield* applyAppend(store, runId, cursor, stripSeq(fullBatch), appendResult);
|
|
456
|
-
// Bound replay after a crash inside a long wake.
|
|
457
|
-
if (snapshotEvery > 0 && cursor.durableSinceSnapshot >= snapshotEvery) {
|
|
458
|
-
cursor = yield* persistSnapshot(store, runId, cursor);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
const root = cursor.state.rootThreadId
|
|
462
|
-
? cursor.state.threads[cursor.state.rootThreadId]
|
|
463
|
-
: undefined;
|
|
464
|
-
if (root &&
|
|
465
|
-
(root.status === 'completed' ||
|
|
466
|
-
root.status === 'failed' ||
|
|
467
|
-
root.status === 'cancelled') &&
|
|
468
|
-
!isRunTerminal(cursor.state)) {
|
|
469
|
-
const completed = createEvent(runId, {
|
|
470
|
-
type: 'runtime.run.completed',
|
|
215
|
+
recoverDeadlines: Effect.suspend(() => runtime.rescanTimers),
|
|
216
|
+
startRun: (args) => {
|
|
217
|
+
const runId = args.runId ?? createRunId();
|
|
218
|
+
return withRunIngress(runId, Effect.gen(function* () {
|
|
219
|
+
const definitionVersion = args.definitionVersion ?? DEFAULT_DEFINITION_VERSION;
|
|
220
|
+
if (!definitions.get(definitionKey(args.kind, args.definitionName, definitionVersion))) {
|
|
221
|
+
return yield* new UnknownDefinitionError(args.kind, args.definitionName, definitionVersion);
|
|
222
|
+
}
|
|
223
|
+
const store = yield* EventStoreTag;
|
|
224
|
+
observe({ type: 'run.start', runId, at: currentTimeMillis() });
|
|
225
|
+
const threadId = args.threadId ?? createThreadId();
|
|
226
|
+
const startedEvents = yield* threadStartedEvents(definitions, {
|
|
227
|
+
kind: args.kind,
|
|
228
|
+
definitionName: args.definitionName,
|
|
229
|
+
definitionVersion,
|
|
230
|
+
input: args.input ?? null,
|
|
231
|
+
threadId,
|
|
232
|
+
parentThreadId: null,
|
|
233
|
+
});
|
|
234
|
+
const started = startedEvents[0];
|
|
235
|
+
const startedInput = started && Predicate.isObject(started.payload)
|
|
236
|
+
? (started.payload.input ?? null)
|
|
237
|
+
: (args.input ?? null);
|
|
238
|
+
const requestedIdentity = {
|
|
239
|
+
kind: args.kind,
|
|
240
|
+
definitionName: args.definitionName,
|
|
241
|
+
definitionVersion,
|
|
242
|
+
input: startedInput,
|
|
243
|
+
requestedThreadId: args.threadId ?? null,
|
|
244
|
+
idempotencyKey: args.idempotencyKey ?? null,
|
|
245
|
+
};
|
|
246
|
+
const committed = yield* commit(store, runId, (existing) => existing.seq > 0
|
|
247
|
+
? assertSameDurableStart(runId, existing.state, requestedIdentity).pipe(Effect.map((rootThreadId) => ({ events: [], value: rootThreadId })))
|
|
248
|
+
: validateAndCreateEvents(registry.catalogs, runId, [
|
|
249
|
+
{
|
|
250
|
+
type: 'runtime.run.started',
|
|
471
251
|
payload: {
|
|
472
|
-
|
|
473
|
-
|
|
252
|
+
rootThreadId: threadId,
|
|
253
|
+
kind: args.kind,
|
|
254
|
+
definitionName: args.definitionName,
|
|
255
|
+
definitionVersion,
|
|
256
|
+
input: startedInput,
|
|
257
|
+
requestedThreadId: args.threadId ?? null,
|
|
474
258
|
},
|
|
475
259
|
threadId: null,
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
else {
|
|
490
|
-
yield* toEffectVoid(scheduler.cancel(runId));
|
|
491
|
-
}
|
|
492
|
-
if (isRunTerminal(cursor.state)) {
|
|
493
|
-
runCache.delete(runId);
|
|
494
|
-
}
|
|
495
|
-
else {
|
|
496
|
-
runCache.set(runId, cursor);
|
|
497
|
-
}
|
|
498
|
-
finalState = cursor.state;
|
|
499
|
-
} while (wakeAgain.delete(runId));
|
|
500
|
-
// Lost race: a waiter queued after the drain loop but before unlock.
|
|
501
|
-
if (wakeAgain.has(runId)) {
|
|
502
|
-
waking.delete(runId);
|
|
503
|
-
liveAppends.clear(runId);
|
|
504
|
-
liveCount.delete(runId);
|
|
505
|
-
return yield* runtime.wake(runId);
|
|
506
|
-
}
|
|
507
|
-
return finalState;
|
|
508
|
-
}
|
|
509
|
-
finally {
|
|
510
|
-
if (waking.has(runId)) {
|
|
511
|
-
waking.delete(runId);
|
|
260
|
+
idempotencyKey: args.idempotencyKey,
|
|
261
|
+
},
|
|
262
|
+
...startedEvents,
|
|
263
|
+
], { origin: { type: 'system' } }).pipe(Effect.map((batch) => ({
|
|
264
|
+
events: batch,
|
|
265
|
+
value: threadId,
|
|
266
|
+
idempotencyKey: args.idempotencyKey
|
|
267
|
+
? `start:${args.idempotencyKey}`
|
|
268
|
+
: undefined,
|
|
269
|
+
}))));
|
|
270
|
+
if (!committed.append?.deduplicated) {
|
|
271
|
+
return committed.value;
|
|
512
272
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
273
|
+
const durable = yield* loadCursor(store, runId);
|
|
274
|
+
return yield* assertSameDurableStart(runId, durable.state, requestedIdentity);
|
|
275
|
+
})).pipe(Effect.flatMap((threadId) => runtime.wake(runId).pipe(Effect.map((state) => ({
|
|
276
|
+
runId,
|
|
277
|
+
threadId: state.rootThreadId ?? threadId,
|
|
278
|
+
state,
|
|
279
|
+
})))));
|
|
519
280
|
},
|
|
281
|
+
signal: (runId, events, signalOpts) => admitExternalEvents(events).pipe(Effect.flatMap((admitted) => withRunIngress(runId, signalAdmitted(runId, admitted, signalOpts))), Effect.andThen(runtime.wake(runId))),
|
|
282
|
+
cancel: cancellation,
|
|
283
|
+
wake: wakeEngine,
|
|
284
|
+
dispose: Effect.sync(() => scheduler.dispose?.()).pipe(Effect.andThen(services.disposeEffect)),
|
|
520
285
|
};
|
|
521
286
|
scheduler =
|
|
522
287
|
options.scheduler ??
|
|
523
|
-
createTimeoutScheduler((runId) =>
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
});
|
|
288
|
+
createTimeoutScheduler((runId) => options.store
|
|
289
|
+
? runtime
|
|
290
|
+
.wake(runId)
|
|
291
|
+
.pipe(Effect.provideService(EventStoreTag, options.store), Effect.tapError(Effect.logError), Effect.ignore)
|
|
292
|
+
: Effect.void);
|
|
530
293
|
return runtime;
|
|
531
294
|
}
|