@lostgradient/weft 0.2.0 → 0.3.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/README.md +47 -22
- package/dist/cli/generated/operation-client.generated.d.ts +28 -1
- package/dist/cli/generated/operation-client.generated.js +2 -0
- package/dist/cli-main.js +79 -79
- package/dist/client/handle-delegation.d.ts +4 -0
- package/dist/client/handle-delegation.js +6 -0
- package/dist/client/http-client-requests.d.ts +2 -0
- package/dist/client/http-client-requests.js +3 -0
- package/dist/client/http-client.d.ts +4 -1
- package/dist/client/http-client.js +9 -1
- package/dist/client/interface.d.ts +57 -2
- package/dist/client/local.d.ts +4 -1
- package/dist/client/local.js +7 -0
- package/dist/client/start-body.d.ts +7 -1
- package/dist/client/start-body.js +13 -4
- package/dist/core/codec/extension-codec.js +4 -2
- package/dist/core/codec/index.d.ts +1 -0
- package/dist/core/codec/index.js +1 -0
- package/dist/core/codec/serializer-registry.d.ts +122 -0
- package/dist/core/codec/serializer-registry.js +51 -0
- package/dist/core/context/index.d.ts +10 -0
- package/dist/core/context/index.js +3 -0
- package/dist/core/context/internals.d.ts +10 -0
- package/dist/core/context/internals.js +5 -1
- package/dist/core/context/run-operation.d.ts +16 -3
- package/dist/core/context/run-operation.js +16 -7
- package/dist/core/context/speculative-child.js +2 -0
- package/dist/core/context/types.d.ts +6 -0
- package/dist/core/engine/bulk-operations-purge.js +1 -0
- package/dist/core/engine/bulk-operations.js +1 -1
- package/dist/core/engine/callback-creators-bundles.js +2 -1
- package/dist/core/engine/callback-creators-core.js +2 -1
- package/dist/core/engine/construction.d.ts +1 -1
- package/dist/core/engine/construction.js +15 -3
- package/dist/core/engine/disposal.js +12 -0
- package/dist/core/engine/engine-create-types.d.ts +0 -14
- package/dist/core/engine/engine-internal-types.d.ts +17 -0
- package/dist/core/engine/engine-leak-warnings.d.ts +6 -0
- package/dist/core/engine/engine-leak-warnings.js +4 -0
- package/dist/core/engine/engine-runtime-helpers.d.ts +17 -0
- package/dist/core/engine/engine-runtime-helpers.js +26 -5
- package/dist/core/engine/errors.d.ts +74 -0
- package/dist/core/engine/errors.js +25 -1
- package/dist/core/engine/handle-result.js +1 -1
- package/dist/core/engine/handles.d.ts +89 -40
- package/dist/core/engine/handles.js +25 -27
- package/dist/core/engine/index.d.ts +122 -4
- package/dist/core/engine/index.js +82 -5
- package/dist/core/engine/inline-launch-queue.d.ts +14 -0
- package/dist/core/engine/inline-launch-queue.js +32 -7
- package/dist/core/engine/internals.d.ts +26 -10
- package/dist/core/engine/lifecycle/fork-helpers.js +1 -7
- package/dist/core/engine/lifecycle/persist.js +5 -20
- package/dist/core/engine/lifecycle/recovered-services.d.ts +45 -0
- package/dist/core/engine/lifecycle/recovered-services.js +34 -0
- package/dist/core/engine/lifecycle/resume.js +33 -5
- package/dist/core/engine/lifecycle/shared.d.ts +8 -0
- package/dist/core/engine/lifecycle/start-batch.js +23 -12
- package/dist/core/engine/lifecycle/start-commit.d.ts +47 -0
- package/dist/core/engine/lifecycle/start-commit.js +27 -0
- package/dist/core/engine/lifecycle/start-exec.d.ts +30 -2
- package/dist/core/engine/lifecycle/start-exec.js +38 -0
- package/dist/core/engine/lifecycle/start-or-signal-resolution.d.ts +79 -0
- package/dist/core/engine/lifecycle/start-or-signal-resolution.js +60 -0
- package/dist/core/engine/lifecycle/start-or-signal.d.ts +45 -0
- package/dist/core/engine/lifecycle/start-or-signal.js +141 -0
- package/dist/core/engine/lifecycle/start.d.ts +3 -3
- package/dist/core/engine/lifecycle/start.js +42 -37
- package/dist/core/engine/lifecycle.d.ts +3 -2
- package/dist/core/engine/lifecycle.js +9 -2
- package/dist/core/engine/listing.js +1 -1
- package/dist/core/engine/operations-data.d.ts +16 -0
- package/dist/core/engine/operations-data.js +6 -0
- package/dist/core/engine/operations-time.d.ts +3 -2
- package/dist/core/engine/operations-time.js +6 -1
- package/dist/core/engine/persisted-data-version.d.ts +5 -9
- package/dist/core/engine/persisted-data-version.js +4 -5
- package/dist/core/engine/schedule-handle.d.ts +45 -0
- package/dist/core/engine/schedule-handle.js +26 -0
- package/dist/core/engine/schedules.d.ts +1 -1
- package/dist/core/engine/schedules.js +7 -3
- package/dist/core/engine/second-instance-detector.d.ts +96 -0
- package/dist/core/engine/second-instance-detector.js +108 -0
- package/dist/core/engine/signals.d.ts +22 -0
- package/dist/core/engine/signals.js +15 -0
- package/dist/core/engine/termination/cleanup.d.ts +25 -0
- package/dist/core/engine/termination/cleanup.js +21 -1
- package/dist/core/engine/termination/complete.js +4 -3
- package/dist/core/engine/termination/suspend.d.ts +68 -0
- package/dist/core/engine/termination/suspend.js +41 -0
- package/dist/core/engine/termination.d.ts +4 -2
- package/dist/core/engine/termination.js +2 -0
- package/dist/core/engine/validation.js +25 -1
- package/dist/core/engine/workflow-feed.d.ts +5 -3
- package/dist/core/events/event-map.d.ts +2 -1
- package/dist/core/events/workflow-events.d.ts +23 -0
- package/dist/core/events/workflow-events.js +9 -0
- package/dist/core/inline-execution-strategy.d.ts +5 -0
- package/dist/core/inline-execution-strategy.js +2 -1
- package/dist/core/list-filter-validation.js +2 -1
- package/dist/core/start-workflow-validation.d.ts +22 -0
- package/dist/core/start-workflow-validation.js +11 -1
- package/dist/core/step-context.d.ts +10 -6
- package/dist/core/step-context.js +7 -15
- package/dist/core/types/activity.d.ts +6 -3
- package/dist/core/types/identity.d.ts +8 -1
- package/dist/core/types/launch-metadata.d.ts +33 -0
- package/dist/core/types/launch-metadata.js +0 -0
- package/dist/core/types/message-handles.d.ts +25 -0
- package/dist/core/types/options.d.ts +90 -7
- package/dist/core/types/reviews.d.ts +2 -1
- package/dist/core/types/services-resolution.d.ts +47 -0
- package/dist/core/types/services-resolution.js +0 -0
- package/dist/core/types/state.d.ts +11 -11
- package/dist/core/types/workflow-builder.d.ts +5 -4
- package/dist/core/types/workflow-context.d.ts +25 -0
- package/dist/core/types/workflow-function.d.ts +17 -0
- package/dist/core/types/workflow-snapshot.d.ts +29 -0
- package/dist/core/types/workflow-snapshot.js +0 -0
- package/dist/core/types.d.ts +3 -0
- package/dist/core/types.js +3 -0
- package/dist/core/weft-error.d.ts +46 -14
- package/dist/core/weft-error.js +12 -1
- package/dist/diagnostics/doctor.js +6 -3
- package/dist/diagnostics/format.js +2 -2
- package/dist/diagnostics/types.d.ts +1 -0
- package/dist/diagnostics/version-check.js +6 -4
- package/dist/index.d.ts +10 -5
- package/dist/index.js +11 -2
- package/dist/json-schema.js +3 -3
- package/dist/mcp/cli.js +35 -35
- package/dist/mcp/list-filter.js +2 -1
- package/dist/mcp/session.js +1 -0
- package/dist/observability/index.js +2 -2
- package/dist/server/handler.js +30 -30
- package/dist/server/index.js +33 -33
- package/dist/server/interactive-operations.js +1 -0
- package/dist/server/operations/resume-workflow.js +2 -2
- package/dist/server/operations/start-or-signal-workflow.d.ts +39 -0
- package/dist/server/operations/start-or-signal-workflow.js +140 -0
- package/dist/server/operations/start-workflow-options.d.ts +32 -0
- package/dist/server/operations/start-workflow-options.js +63 -0
- package/dist/server/operations/start-workflow.js +7 -69
- package/dist/server/operations/suspend-workflow.d.ts +13 -0
- package/dist/server/operations/suspend-workflow.js +36 -0
- package/dist/server/rest-binding.d.ts +18 -7
- package/dist/server/rest-bindings.js +12 -0
- package/dist/server/runtime/task-dispatch.js +5 -3
- package/dist/server/runtime/task-polling.d.ts +16 -2
- package/dist/server/runtime/task-polling.js +20 -5
- package/dist/server/runtime/websocket-worker.js +8 -0
- package/dist/server/serve-internals.d.ts +8 -0
- package/dist/server/serve-internals.js +4 -2
- package/dist/server/task-state.d.ts +8 -0
- package/dist/service-worker/index.js +28 -28
- package/dist/storage/capabilities.d.ts +10 -2
- package/dist/storage/capabilities.js +2 -2
- package/dist/storage/http.js +2 -2
- package/dist/storage/index.d.ts +7 -1
- package/dist/storage/indexeddb.js +1 -1
- package/dist/storage/interface.d.ts +40 -0
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +3 -0
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon-value-mapping.d.ts +47 -0
- package/dist/storage/neon-value-mapping.js +11 -0
- package/dist/storage/neon.d.ts +108 -0
- package/dist/storage/neon.js +10 -0
- package/dist/storage/node-sqlite-loader.d.ts +71 -0
- package/dist/storage/node-sqlite-loader.js +41 -0
- package/dist/storage/node-sqlite.d.ts +1 -19
- package/dist/storage/node-sqlite.js +38 -32
- package/dist/storage/postgres-key-value-queries.d.ts +79 -0
- package/dist/storage/postgres-key-value-queries.js +63 -0
- package/dist/storage/resolve.d.ts +2 -165
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/storage-configuration.d.ts +209 -0
- package/dist/storage/storage-configuration.js +0 -0
- package/dist/storage/text-value-store.d.ts +13 -10
- package/dist/storage/turso.js +2 -2
- package/dist/storage/typed-storage.js +1 -1
- package/dist/storage/web-extension.js +1 -1
- package/dist/testing/event-loop.d.ts +36 -2
- package/dist/testing/index.d.ts +31 -1
- package/dist/testing/index.js +33 -33
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/index.js +9 -5
- package/dist/worker/long-poll.js +4 -0
- package/dist/worker/protocol-messages.d.ts +20 -0
- package/dist/worker/protocol-schemas.d.ts +32 -0
- package/dist/worker/protocol-schemas.js +8 -4
- package/dist/worker/protocol-task-result.d.ts +28 -0
- package/dist/worker/protocol-task-result.js +76 -0
- package/dist/worker/protocol.d.ts +4 -15
- package/dist/worker/protocol.js +1 -1
- package/dist/worker/registry/fair-share.d.ts +29 -0
- package/dist/worker/registry/fair-share.js +30 -0
- package/dist/worker/registry/routing.d.ts +18 -0
- package/dist/worker/registry/routing.js +14 -0
- package/dist/worker/registry/types.d.ts +7 -0
- package/dist/worker/registry.d.ts +16 -1
- package/dist/worker/registry.js +24 -36
- package/package.json +17 -4
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LaunchMetadata, QueryDefinition, SearchAttributeValue, SignalDefinition, SignalDeliveryOptions, UpdateDefinition, WorkflowState } from '../types.ts';
|
|
2
|
+
import type { WorkflowSnapshot } from '../types/workflow-snapshot.ts';
|
|
2
3
|
export declare function getWorkflowExecutionStartedAt(state: Pick<WorkflowState, 'createdAt' | 'startedAt'>): number;
|
|
3
4
|
export declare const HANDLE_RESULT_PROMISE: unique symbol;
|
|
4
5
|
export interface WorkflowHandleEngine extends EventTarget {
|
|
5
6
|
[HANDLE_RESULT_PROMISE](workflowId: string): Promise<unknown>;
|
|
6
7
|
cancel(workflowId: string): Promise<void>;
|
|
8
|
+
suspend(workflowId: string): Promise<void>;
|
|
9
|
+
resume(workflowId: string): Promise<WorkflowHandle>;
|
|
7
10
|
signal(workflowId: string, name: string, payload?: unknown, options?: SignalDeliveryOptions): Promise<void>;
|
|
8
11
|
update(workflowId: string, name: string, payload?: unknown, options?: {
|
|
9
12
|
timeout?: number;
|
|
@@ -14,13 +17,13 @@ export interface WorkflowHandleEngine extends EventTarget {
|
|
|
14
17
|
addTags(workflowId: string, ...tags: string[]): Promise<void>;
|
|
15
18
|
removeTags(workflowId: string, ...tags: string[]): Promise<void>;
|
|
16
19
|
get(workflowId: string): Promise<WorkflowState | null>;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Current checkpoint step (the run's cursor) for a workflow, or `null` when no
|
|
22
|
+
* checkpoint exists. Reads the in-memory checkpoint when the run is live in
|
|
23
|
+
* this engine, otherwise the durably persisted checkpoint — so it is correct
|
|
24
|
+
* for both an in-flight run and one recovered or inspected in a fresh process.
|
|
25
|
+
*/
|
|
26
|
+
getCurrentCheckpointStep(workflowId: string): Promise<number | null>;
|
|
24
27
|
}
|
|
25
28
|
/**
|
|
26
29
|
* Handle to a running or completed workflow. Returned by {@link Engine.start}
|
|
@@ -72,6 +75,84 @@ export declare class WorkflowHandle<TResult = unknown> extends EventTarget imple
|
|
|
72
75
|
constructor(id: string, engine: WorkflowHandleEngine);
|
|
73
76
|
result(): Promise<TResult>;
|
|
74
77
|
cancel(): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Suspend this workflow without terminating it: it transitions to the
|
|
80
|
+
* non-terminal `'suspended'` status, keeps its durable checkpoint, and is
|
|
81
|
+
* later resumable via {@link WorkflowHandle.resume}. Unlike `cancel()`, this
|
|
82
|
+
* does not run cancel handlers and does not settle `result()` — the result
|
|
83
|
+
* promise stays pending until a later `resume()` completes the run. A
|
|
84
|
+
* suspended workflow is NOT auto-recovered by `engine.recoverAll()`; resume it
|
|
85
|
+
* explicitly. Suspending a workflow that is not running is a no-op.
|
|
86
|
+
*/
|
|
87
|
+
suspend(): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Resume this workflow from its persisted checkpoint after it was suspended
|
|
90
|
+
* (or left `'running'` by a prior process). The run is re-driven on this
|
|
91
|
+
* engine; `result()` on this handle resolves when the resumed run completes.
|
|
92
|
+
* Throws if the workflow is in a status that cannot be resumed (terminal,
|
|
93
|
+
* pending, or not found).
|
|
94
|
+
*/
|
|
95
|
+
resume(): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Reconstruct this workflow's launch context — its original `input` and the
|
|
98
|
+
* launch options recoverable from durable state — from the persisted
|
|
99
|
+
* {@link WorkflowState}. Resolves `null` if the workflow no longer exists
|
|
100
|
+
* (never started, or purged).
|
|
101
|
+
*
|
|
102
|
+
* Designed for the post-`recoverAll()` case: a recovered handle can recover
|
|
103
|
+
* the input a run was started with (and its `id`/`tags`) without the caller
|
|
104
|
+
* keeping a side table correlating recovered workflows back to their launch
|
|
105
|
+
* context. This is an async read (it loads state) so it behaves identically
|
|
106
|
+
* on handles from `start()`, `recoverAll()`, and `getHandle()` — none of which
|
|
107
|
+
* is special-cased — rather than a sync property that would be `undefined` on
|
|
108
|
+
* a handle created without a state load.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* import { Engine } from '@lostgradient/weft';
|
|
113
|
+
*
|
|
114
|
+
* const engine = new Engine();
|
|
115
|
+
* const handles = await engine.recoverAll();
|
|
116
|
+
* for (const handle of handles) {
|
|
117
|
+
* const metadata = await handle.getLaunchMetadata();
|
|
118
|
+
* if (metadata) {
|
|
119
|
+
* // rebuild this run's dependencies from metadata.input
|
|
120
|
+
* void metadata.input;
|
|
121
|
+
* }
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
getLaunchMetadata(): Promise<LaunchMetadata | null>;
|
|
126
|
+
/**
|
|
127
|
+
* A point-in-time view of this workflow's progress: its status and current
|
|
128
|
+
* checkpoint step (cursor). Resolves `null` if the workflow no longer exists.
|
|
129
|
+
* The `status` matches `engine.get(id)` — notably it reports `'pending'` for a
|
|
130
|
+
* run whose inline start is still queued, even though its persisted status is
|
|
131
|
+
* `'running'`.
|
|
132
|
+
*
|
|
133
|
+
* Designed for observing a recovered run: after `engine.recoverAll()`, a
|
|
134
|
+
* caller can read where a resumed run currently is — and rebuild its own
|
|
135
|
+
* progress adapter to re-register the run on a live surface — without waiting
|
|
136
|
+
* for the run's final `result()`. It is an async read (loads state +
|
|
137
|
+
* checkpoint), so it behaves identically on handles from `start()`,
|
|
138
|
+
* `recoverAll()`, and `getHandle()`.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* import { Engine } from '@lostgradient/weft';
|
|
143
|
+
*
|
|
144
|
+
* const engine = new Engine();
|
|
145
|
+
* const handles = await engine.recoverAll();
|
|
146
|
+
* for (const handle of handles) {
|
|
147
|
+
* const snapshot = await handle.snapshot();
|
|
148
|
+
* if (snapshot) {
|
|
149
|
+
* // re-register a progress adapter at snapshot.step
|
|
150
|
+
* void snapshot.step;
|
|
151
|
+
* }
|
|
152
|
+
* }
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
snapshot(): Promise<WorkflowSnapshot | null>;
|
|
75
156
|
signal(name: SignalDefinition): Promise<void>;
|
|
76
157
|
signal<TInput>(name: SignalDefinition<TInput>, payload: TInput, options?: SignalDeliveryOptions): Promise<void>;
|
|
77
158
|
signal(name: string, payload?: unknown, options?: SignalDeliveryOptions): Promise<void>;
|
|
@@ -103,35 +184,3 @@ export declare class WorkflowHandle<TResult = unknown> extends EventTarget imple
|
|
|
103
184
|
};
|
|
104
185
|
[Symbol.asyncDispose](): Promise<void>;
|
|
105
186
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Handle to a recurring schedule created by {@link Engine.schedule}. Use
|
|
108
|
-
* `handle.pause()`, `handle.resume()`, `handle.cancel()`, or
|
|
109
|
-
* `handle.update(cronExpression)` to manage the schedule lifecycle.
|
|
110
|
-
* `handle.describe()` returns the current {@link ScheduleSummary}.
|
|
111
|
-
*
|
|
112
|
-
* @example
|
|
113
|
-
* ```ts
|
|
114
|
-
* import { workflow, Engine, ScheduleHandle } from '@lostgradient/weft';
|
|
115
|
-
*
|
|
116
|
-
* const engine = new Engine();
|
|
117
|
-
* engine.register(workflow({ name: 'daily-report' }).execute(async function* () { return 'ok'; }));
|
|
118
|
-
*
|
|
119
|
-
* const handle = await engine.schedule('daily-report', null, '0 9 * * *');
|
|
120
|
-
* const typedHandle: ScheduleHandle = handle;
|
|
121
|
-
* await handle.pause();
|
|
122
|
-
* const summary = await handle.describe();
|
|
123
|
-
* void typedHandle;
|
|
124
|
-
* console.log(summary.status); // 'paused'
|
|
125
|
-
* await handle.cancel();
|
|
126
|
-
* ```
|
|
127
|
-
*/
|
|
128
|
-
export declare class ScheduleHandle {
|
|
129
|
-
#private;
|
|
130
|
-
readonly id: string;
|
|
131
|
-
constructor(id: string, engine: ScheduleHandleEngine);
|
|
132
|
-
pause(): Promise<void>;
|
|
133
|
-
resume(): Promise<void>;
|
|
134
|
-
cancel(): Promise<void>;
|
|
135
|
-
update(newSpec: string | ScheduleSpec): Promise<void>;
|
|
136
|
-
describe(): Promise<ScheduleSummary>;
|
|
137
|
-
}
|
|
@@ -51,6 +51,31 @@ export class WorkflowHandle extends EventTarget {
|
|
|
51
51
|
async cancel() {
|
|
52
52
|
return this.#engine.cancel(this.id);
|
|
53
53
|
}
|
|
54
|
+
async suspend() {
|
|
55
|
+
return this.#engine.suspend(this.id);
|
|
56
|
+
}
|
|
57
|
+
async resume() {
|
|
58
|
+
await this.#engine.resume(this.id);
|
|
59
|
+
}
|
|
60
|
+
async getLaunchMetadata() {
|
|
61
|
+
const state = await this.#engine.get(this.id);
|
|
62
|
+
if (state === null)
|
|
63
|
+
return null;
|
|
64
|
+
return {
|
|
65
|
+
input: state.input,
|
|
66
|
+
launchOptions: {
|
|
67
|
+
id: state.id,
|
|
68
|
+
...state.tags !== void 0 && state.tags.length > 0 && { tags: state.tags }
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async snapshot() {
|
|
73
|
+
const state = await this.#engine.get(this.id);
|
|
74
|
+
if (state === null)
|
|
75
|
+
return null;
|
|
76
|
+
const step = await this.#engine.getCurrentCheckpointStep(this.id);
|
|
77
|
+
return { status: state.status, step: step ?? 0 };
|
|
78
|
+
}
|
|
54
79
|
async signal(nameOrDefinition, payload, options) {
|
|
55
80
|
return this.#engine.signal(this.id, messageName(nameOrDefinition), payload, options);
|
|
56
81
|
}
|
|
@@ -144,30 +169,3 @@ export class WorkflowHandle extends EventTarget {
|
|
|
144
169
|
}
|
|
145
170
|
async[Symbol.asyncDispose]() {}
|
|
146
171
|
}
|
|
147
|
-
|
|
148
|
-
export class ScheduleHandle {
|
|
149
|
-
id;
|
|
150
|
-
#engine;
|
|
151
|
-
constructor(id, engine) {
|
|
152
|
-
this.id = id;
|
|
153
|
-
this.#engine = engine;
|
|
154
|
-
}
|
|
155
|
-
async pause() {
|
|
156
|
-
await this.#engine.pauseSchedule(this.id);
|
|
157
|
-
}
|
|
158
|
-
async resume() {
|
|
159
|
-
await this.#engine.resumeSchedule(this.id);
|
|
160
|
-
}
|
|
161
|
-
async cancel() {
|
|
162
|
-
await this.#engine.cancelSchedule(this.id);
|
|
163
|
-
}
|
|
164
|
-
async update(newSpec) {
|
|
165
|
-
await this.#engine.updateSchedule(this.id, newSpec);
|
|
166
|
-
}
|
|
167
|
-
async describe() {
|
|
168
|
-
const schedule = await this.#engine.getSchedule(this.id);
|
|
169
|
-
if (!schedule)
|
|
170
|
-
throw Error(`Schedule "${this.id}" not found`);
|
|
171
|
-
return schedule;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
@@ -4,7 +4,7 @@ import type { StoredStreamChunk } from '../context.ts';
|
|
|
4
4
|
import type { Interceptor } from '../interceptor.ts';
|
|
5
5
|
import { type ReviewRequest } from '../review/index.ts';
|
|
6
6
|
import { Scheduler } from '../scheduler.ts';
|
|
7
|
-
import { type AnyActivityDefinition, type AnyWorkflowDefinition, type AttributeFilterKey, type BulkCancelResult, type BulkDeleteResult, type BulkOperationCommitOptions, type BulkOperationDryRunOptions, type BulkOperationDryRunResult, type BulkSignalAllCommitOptions, type BulkSignalAllDryRunOptions, type BulkSignalResult, type BulkTagResult, type CheckpointState, type CheckpointSummary, type CoordinatedUpdateResult, type DefaultActivityTypes, type DefaultWorkflowRegistry, type ForkOptions, type InferActivityEntries, type InferActivityEntry, type InferWorkflowEntries, type InferWorkflowEntry, type ListFilter, type ListOptions, type PaginatedResult, type PurgeResult, type QueryDefinition, type RegisteredWorkflowDefinition, type RetentionOverview, type ReviewListEntry, type ReviewListFilter, type ScheduleDefinition, type ScheduleFilter, type ScheduleOptions, type ScheduleSpec, type ScheduleSummary, type SearchAttributeValue, type SignalDefinition, type SignalDeliveryOptions, type StartOptions, type SubmitReviewOptions, type TypedListFilter, type UpdateDefinition, type WorkflowEvent, type WorkflowInput, type WorkflowOutput, type WorkflowReplay, type WorkflowState, type WorkflowSummary, type WorkflowTimelineEntry } from '../types.ts';
|
|
7
|
+
import { type AnyActivityDefinition, type AnyWorkflowDefinition, type AttributeFilterKey, type BulkCancelResult, type BulkDeleteResult, type BulkOperationCommitOptions, type BulkOperationDryRunOptions, type BulkOperationDryRunResult, type BulkSignalAllCommitOptions, type BulkSignalAllDryRunOptions, type BulkSignalResult, type BulkTagResult, type CheckpointState, type CheckpointSummary, type CoordinatedUpdateResult, type DefaultActivityTypes, type DefaultWorkflowRegistry, type ForkOptions, type InferActivityEntries, type InferActivityEntry, type InferWorkflowEntries, type InferWorkflowEntry, type ListFilter, type ListOptions, type PaginatedResult, type PurgeResult, type QueryDefinition, type RegisteredWorkflowDefinition, type RetentionOverview, type ReviewListEntry, type ReviewListFilter, type ScheduleDefinition, type ScheduleFilter, type ScheduleOptions, type ScheduleSpec, type ScheduleSummary, type SearchAttributeValue, type SignalDefinition, type SignalDeliveryOptions, type StartOptions, type StartOrSignalSignal, type SubmitReviewOptions, type TypedListFilter, type UpdateDefinition, type WorkflowEvent, type WorkflowInput, type WorkflowOutput, type WorkflowReplay, type WorkflowState, type WorkflowSummary, type WorkflowTimelineEntry } from '../types.ts';
|
|
8
8
|
import type { TimerEntry } from '../types/checkpoint.ts';
|
|
9
9
|
import type { WorkflowAlreadyRegistered } from '../types/workflow-builder.ts';
|
|
10
10
|
import { type AggregateOptions, type AggregateResult } from './aggregate.ts';
|
|
@@ -12,17 +12,19 @@ import { type EmptyActivityDefinitions, type EmptyWorkflowDefinitions, type Know
|
|
|
12
12
|
import { type ActivityDefinitionName, type EngineCreateOptions, type RegisteredActivityDefinitionExecute, type UnknownWorkflowNameWhenDefaultRegistryIsEmpty } from './engine-create-types.ts';
|
|
13
13
|
import type { EngineConstructorOptions } from './engine-internal-types.ts';
|
|
14
14
|
import type { EngineStateNamespace } from './engine-state-namespace.ts';
|
|
15
|
-
import { HANDLE_RESULT_PROMISE,
|
|
15
|
+
import { HANDLE_RESULT_PROMISE, WorkflowHandle } from './handles.ts';
|
|
16
16
|
import { type RecoverAllOptions } from './lifecycle.ts';
|
|
17
17
|
import { assertCompatiblePersistedDataVersion } from './persisted-data-version.ts';
|
|
18
|
+
import { ScheduleHandle } from './schedule-handle.ts';
|
|
18
19
|
import { type WorkflowFeedListener, type WorkflowFeedRecord, type WorkflowFeedSelector } from './workflow-feed.ts';
|
|
19
20
|
export { ActivityReconciliationCapabilityError, ActivityReconciliationConflictError, ActivityReconciliationIndeterminateError, } from './activity-reconciliation.ts';
|
|
20
21
|
export { AsyncActivityTokenNotFoundError } from './async-activity-completion.ts';
|
|
21
22
|
export type { PendingAsyncActivity } from './async-activity-completion.ts';
|
|
22
23
|
export type { PendingTimelineEntry, RegistrationEntry, ResolvedOptions, TrackedWaiterKeys, WorkflowResultWaiter, } from './engine-internal-types.ts';
|
|
23
|
-
export { ActivityResolutionError, BulkDeleteRequiresTerminalWorkflowsError, BulkOperationConfirmationError, EngineCreateNameMismatchError, EngineDisposedError, PersistedDataIncompatibleError, WorkflowAlreadyExistsError, WorkflowNotFoundError, WorkflowNotRegisteredError, WorkflowTypeNotRegisteredForRecoveryError, } from './errors.ts';
|
|
24
|
-
export { HANDLE_RESULT_PROMISE,
|
|
24
|
+
export { ActivityResolutionError, BulkDeleteRequiresTerminalWorkflowsError, BulkOperationConfirmationError, EngineCreateNameMismatchError, EngineDisposedError, IdempotencyKeyPurgedError, PersistedDataIncompatibleError, StartOrSignalConflictError, WorkflowAlreadyExistsError, WorkflowNotFoundError, WorkflowNotRegisteredError, WorkflowSuspendNotSupportedError, WorkflowTypeNotRegisteredForRecoveryError, } from './errors.ts';
|
|
25
|
+
export { HANDLE_RESULT_PROMISE, WorkflowHandle } from './handles.ts';
|
|
25
26
|
export type { RecoverAllOptions } from './lifecycle.ts';
|
|
27
|
+
export { ScheduleHandle } from './schedule-handle.ts';
|
|
26
28
|
export type { WorkflowFeedListener, WorkflowFeedRecord, WorkflowFeedSelector, } from './workflow-feed.ts';
|
|
27
29
|
export type { EngineCreateOptions } from './engine-create-types.ts';
|
|
28
30
|
export { clearEngineLeakWarningTokenForTesting, getEngineLeakCollectionCountForTesting, hasEngineLeakWarningTokenForTesting, setEngineLeakWarningOverrideForTesting, setNextEngineLeakWarningTokenForTesting, shouldEmitEngineLeakWarningForTesting, } from './engine-leak-warnings.ts';
|
|
@@ -30,6 +32,7 @@ export type { EngineStateNamespace } from './engine-state-namespace.ts';
|
|
|
30
32
|
export { assertCompatiblePersistedDataVersion };
|
|
31
33
|
export declare const ENGINE_PARKED_WORKFLOW_COUNT_FOR_TESTING: unique symbol;
|
|
32
34
|
export declare const ENGINE_SIGNAL_WAITER_COUNT_FOR_TESTING: unique symbol;
|
|
35
|
+
export declare const ENGINE_SLEEP_RESOLVER_COUNT_FOR_TESTING: unique symbol;
|
|
33
36
|
/**
|
|
34
37
|
* Durable execution engine.
|
|
35
38
|
*
|
|
@@ -185,6 +188,25 @@ export declare class Engine<TWorkflows extends object = DefaultWorkflowRegistry,
|
|
|
185
188
|
listActivityDefinitions(): ActivityMetadata[];
|
|
186
189
|
start<TName extends KnownWorkflowNames<TWorkflows>>(type: TName, input: WorkflowInput<TWorkflows, TName>, options?: StartOptions): Promise<WorkflowHandle<WorkflowOutput<TWorkflows, TName>>>;
|
|
187
190
|
start<TName extends string>(type: UnknownWorkflowNameWhenDefaultRegistryIsEmpty<TWorkflows, TName>, input: unknown, options?: StartOptions): Promise<WorkflowHandle>;
|
|
191
|
+
/**
|
|
192
|
+
* Atomically start a workflow or signal it if it already exists
|
|
193
|
+
* (signal-with-start). With an absent target, the workflow record and the
|
|
194
|
+
* first signal commit in one batch and the freshly-launched run consumes the
|
|
195
|
+
* signal on its first drive. A non-terminal target (running, pending, or
|
|
196
|
+
* suspended) is signalled through the normal signal path; a terminal target
|
|
197
|
+
* throws {@link StartOrSignalConflictError} rather than starting a new run or
|
|
198
|
+
* dropping the signal.
|
|
199
|
+
*
|
|
200
|
+
* Concurrent callers converge on one workflow and one delivered signal. Pass
|
|
201
|
+
* `options.idempotencyKey` to dedup independent callers (e.g. retried
|
|
202
|
+
* webhooks); the signal id derives from the key when `signal.signalId` is
|
|
203
|
+
* omitted, so callers that share only the key still converge. `signal.signalId`
|
|
204
|
+
* and `options.idempotencyKey` are mutually exclusive (provide exactly one), as
|
|
205
|
+
* are `options.id` and `options.idempotencyKey`. Requires a storage backend
|
|
206
|
+
* with `conditionalBatch`.
|
|
207
|
+
*/
|
|
208
|
+
startOrSignal<TName extends KnownWorkflowNames<TWorkflows>>(type: TName, input: WorkflowInput<TWorkflows, TName>, signal: StartOrSignalSignal, options?: StartOptions): Promise<WorkflowHandle<WorkflowOutput<TWorkflows, TName>>>;
|
|
209
|
+
startOrSignal<TName extends string>(type: UnknownWorkflowNameWhenDefaultRegistryIsEmpty<TWorkflows, TName>, input: unknown, signal: StartOrSignalSignal, options?: StartOptions): Promise<WorkflowHandle>;
|
|
188
210
|
getHandle(workflowId: string): WorkflowHandle;
|
|
189
211
|
list<const TAttributeKeys extends readonly AttributeFilterKey[] = readonly AttributeFilterKey[]>(filter?: TypedListFilter<TAttributeKeys>, options?: ListOptions): Promise<PaginatedResult<WorkflowSummary>>;
|
|
190
212
|
aggregate(filter: ListFilter | undefined, options: AggregateOptions): Promise<AggregateResult>;
|
|
@@ -201,6 +223,37 @@ export declare class Engine<TWorkflows extends object = DefaultWorkflowRegistry,
|
|
|
201
223
|
tagAll(filter: ListFilter, tags: string[], options?: BulkOperationCommitOptions): Promise<BulkTagResult>;
|
|
202
224
|
untagAll(filter: ListFilter, tags: string[], options: BulkOperationDryRunOptions): Promise<BulkOperationDryRunResult>;
|
|
203
225
|
untagAll(filter: ListFilter, tags: string[], options?: BulkOperationCommitOptions): Promise<BulkTagResult>;
|
|
226
|
+
/**
|
|
227
|
+
* Register a recurring schedule that starts a workflow on a cron expression or
|
|
228
|
+
* fixed interval. Returns a {@link ScheduleHandle} for pausing, resuming,
|
|
229
|
+
* updating, or cancelling the schedule.
|
|
230
|
+
*
|
|
231
|
+
* Two call forms:
|
|
232
|
+
* - A {@link ScheduleDefinition} object: `engine.schedule({ workflow, cron, input })`.
|
|
233
|
+
* Carries the workflow (definition or type name), the `cron`/`every` spec,
|
|
234
|
+
* optional `input`, `id`, `overlapPolicy`, and `backfill`.
|
|
235
|
+
* - Positional: `engine.schedule(type, input, spec, options?)` where `spec` is
|
|
236
|
+
* a cron string or a {@link ScheduleSpec} (`{ cron }` or `{ every }`).
|
|
237
|
+
*
|
|
238
|
+
* The {@link ScheduleOptions.overlap} policy governs what happens when a tick
|
|
239
|
+
* fires while the previous run is still in flight.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```ts
|
|
243
|
+
* import { workflow, Engine } from '@lostgradient/weft';
|
|
244
|
+
*
|
|
245
|
+
* const engine = new Engine();
|
|
246
|
+
* engine.register(workflow({ name: 'sweep' }).execute(async function* () { return 'ok'; }));
|
|
247
|
+
*
|
|
248
|
+
* // Definition form: every day at 09:00, skip a tick if the prior run is still running.
|
|
249
|
+
* const handle = await engine.schedule({
|
|
250
|
+
* workflow: 'sweep',
|
|
251
|
+
* cron: '0 9 * * *',
|
|
252
|
+
* overlapPolicy: 'skip',
|
|
253
|
+
* });
|
|
254
|
+
* await handle.pause();
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
204
257
|
schedule<TInput>(definition: ScheduleDefinition<TInput>): Promise<ScheduleHandle>;
|
|
205
258
|
schedule(type: string, input: unknown, spec: string | ScheduleSpec, options?: ScheduleOptions): Promise<ScheduleHandle>;
|
|
206
259
|
getSchedule(scheduleId: string): Promise<ScheduleSummary | null>;
|
|
@@ -212,6 +265,7 @@ export declare class Engine<TWorkflows extends object = DefaultWorkflowRegistry,
|
|
|
212
265
|
[HANDLE_RESULT_PROMISE](workflowId: string): Promise<unknown>;
|
|
213
266
|
[ENGINE_PARKED_WORKFLOW_COUNT_FOR_TESTING](): number;
|
|
214
267
|
[ENGINE_SIGNAL_WAITER_COUNT_FOR_TESTING](): number;
|
|
268
|
+
[ENGINE_SLEEP_RESOLVER_COUNT_FOR_TESTING](): number;
|
|
215
269
|
signal(workflowId: string, name: SignalDefinition): Promise<void>;
|
|
216
270
|
signal<TInput>(workflowId: string, name: SignalDefinition<TInput>, payload: TInput, options?: SignalDeliveryOptions): Promise<void>;
|
|
217
271
|
signal(workflowId: string, name: string, payload?: unknown, options?: SignalDeliveryOptions): Promise<void>;
|
|
@@ -230,7 +284,40 @@ export declare class Engine<TWorkflows extends object = DefaultWorkflowRegistry,
|
|
|
230
284
|
getStreamChunks(workflowId: string, key: string, options?: {
|
|
231
285
|
after?: number;
|
|
232
286
|
}): Promise<StoredStreamChunk[]>;
|
|
287
|
+
/**
|
|
288
|
+
* Read a value a workflow offloaded with `ctx.offload(key, ...)` back out of
|
|
289
|
+
* storage by `workflowId` + `key`.
|
|
290
|
+
*
|
|
291
|
+
* This is the external, post-completion reader for offloaded artifacts — the
|
|
292
|
+
* missing sibling of {@link getStreamChunks} and {@link getEvents}. Offloaded
|
|
293
|
+
* values survive normal completion (`completeWorkflow`/`failWorkflow` preserve
|
|
294
|
+
* them) so a consumer can read a finished workflow's offloaded output after
|
|
295
|
+
* `handle.result()` resolves. They are swept only when a workflow is
|
|
296
|
+
* terminated, cancelled, or times out.
|
|
297
|
+
*
|
|
298
|
+
* @returns The decoded offload value, or `null` when no value is stored under
|
|
299
|
+
* that key (key was never written, workflow ID unknown, or artifact swept).
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```ts
|
|
303
|
+
* import { Engine } from '@lostgradient/weft';
|
|
304
|
+
*
|
|
305
|
+
* async function readReport(engine: Engine, workflowId: string): Promise<unknown> {
|
|
306
|
+
* // `null` when the workflow offloaded nothing under this key, or after a
|
|
307
|
+
* // terminated workflow swept its output artifacts.
|
|
308
|
+
* return engine.getOffload(workflowId, 'report');
|
|
309
|
+
* }
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
getOffload(workflowId: string, key: string): Promise<unknown>;
|
|
233
313
|
fork(sourceWorkflowId: string, options?: ForkOptions): Promise<WorkflowHandle>;
|
|
314
|
+
/**
|
|
315
|
+
* Re-drive a workflow from its persisted checkpoint and return a live handle.
|
|
316
|
+
* Accepts a workflow left `'running'` (e.g. recovered after a process restart)
|
|
317
|
+
* or one explicitly `'suspended'` via {@link Engine.suspend} — a suspended
|
|
318
|
+
* workflow is durably flipped back to `'running'` as part of resuming. Throws
|
|
319
|
+
* if the workflow is in any other status (terminal, pending) or not found.
|
|
320
|
+
*/
|
|
234
321
|
resume(workflowId: string): Promise<WorkflowHandle>;
|
|
235
322
|
/**
|
|
236
323
|
* Recover every running workflow found in storage. By default, recovery
|
|
@@ -265,8 +352,23 @@ export declare class Engine<TWorkflows extends object = DefaultWorkflowRegistry,
|
|
|
265
352
|
*/
|
|
266
353
|
failAsyncActivity(token: string, error: unknown): Promise<void>;
|
|
267
354
|
cancel(workflowId: string): Promise<void>;
|
|
355
|
+
/**
|
|
356
|
+
* Suspend a running workflow without terminating it. The workflow transitions
|
|
357
|
+
* to the non-terminal `'suspended'` status, keeps its durable checkpoint, and
|
|
358
|
+
* is later resumable via {@link Engine.resume} (or `handle.resume()`). Unlike
|
|
359
|
+
* {@link Engine.cancel}, this does not run cancel handlers and does not settle
|
|
360
|
+
* the result promise — `handle.result()` stays pending until a later `resume()`
|
|
361
|
+
* drives the run to completion.
|
|
362
|
+
*
|
|
363
|
+
* Suspension is client-driven preemption, so a suspended workflow is NOT
|
|
364
|
+
* auto-recovered by {@link Engine.recoverAll}; resume it explicitly. Calling
|
|
365
|
+
* `suspend` on a workflow that is not running (already terminal, or never
|
|
366
|
+
* started) is a no-op.
|
|
367
|
+
*/
|
|
368
|
+
suspend(workflowId: string): Promise<void>;
|
|
268
369
|
timeout(workflowId: string): Promise<void>;
|
|
269
370
|
get(workflowId: string): Promise<WorkflowState | null>;
|
|
371
|
+
getCurrentCheckpointStep(workflowId: string): Promise<number | null>;
|
|
270
372
|
getAttributes(workflowId: string): Promise<Record<string, SearchAttributeValue> | null>;
|
|
271
373
|
setAttributes(workflowId: string, attributes: Record<string, SearchAttributeValue>): Promise<void>;
|
|
272
374
|
addTags(workflowId: string, ...tags: string[]): Promise<void>;
|
|
@@ -287,7 +389,23 @@ export declare class Engine<TWorkflows extends object = DefaultWorkflowRegistry,
|
|
|
287
389
|
timeout?: number;
|
|
288
390
|
idempotencyKey?: string;
|
|
289
391
|
}): Promise<CoordinatedUpdateResult>;
|
|
392
|
+
/**
|
|
393
|
+
* Synchronous teardown (`using engine = ...`). Pending inline launches that
|
|
394
|
+
* have not yet run are **discarded**, not executed. When you need queued
|
|
395
|
+
* starts to complete before teardown — or want a clean event loop with no
|
|
396
|
+
* dangling deferred-launch macrotask — prefer {@link Engine[Symbol.asyncDispose]}
|
|
397
|
+
* via `await using`.
|
|
398
|
+
*/
|
|
290
399
|
[Symbol.dispose](): void;
|
|
400
|
+
/**
|
|
401
|
+
* Async teardown (`await using engine = ...`). Drains pending inline launches
|
|
402
|
+
* so each queued workflow completes its first turn before disposal, leaving no
|
|
403
|
+
* deferred-launch macrotask to fire against torn-down state. The drain is
|
|
404
|
+
* bounded (a pass cap and the abort signal); in the pathological case where it
|
|
405
|
+
* cannot converge, anything still queued is discarded by the synchronous
|
|
406
|
+
* teardown that always follows. Prefer this over the synchronous
|
|
407
|
+
* {@link Engine[Symbol.dispose]} in async contexts and tests.
|
|
408
|
+
*/
|
|
291
409
|
[Symbol.asyncDispose](): Promise<void>;
|
|
292
410
|
get storage(): WeftStorage;
|
|
293
411
|
get scheduler(): Scheduler;
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ActivityRegistry
|
|
4
4
|
} from "../activity-registry.js";
|
|
5
5
|
import { AtomicState } from "../atomic-state.js";
|
|
6
|
+
import { deserializeCheckpoint } from "../checkpoint.js";
|
|
6
7
|
import { createHandleCacheFinalizer } from "../engine-helpers.js";
|
|
7
8
|
import { ReviewCoordinator } from "../review/index.js";
|
|
8
9
|
import { Scheduler } from "../scheduler.js";
|
|
@@ -63,6 +64,8 @@ import {
|
|
|
63
64
|
import {
|
|
64
65
|
createCleanupIntervalTick,
|
|
65
66
|
createQueuedInlineWorkflowStartHandler,
|
|
67
|
+
createSecondInstanceDetectorResolver,
|
|
68
|
+
drainQueuedInlineWorkflowStartsForEngine,
|
|
66
69
|
isActivityDefinition
|
|
67
70
|
} from "./engine-runtime-helpers.js";
|
|
68
71
|
import { EngineCreateNameMismatchError } from "./errors.js";
|
|
@@ -70,7 +73,7 @@ import {
|
|
|
70
73
|
createWorkflowHandleWithResultPromise as createWorkflowHandleWithResultPromiseFromInternals,
|
|
71
74
|
getWorkflowResultPromise as getWorkflowResultPromiseFromInternals
|
|
72
75
|
} from "./handle-result.js";
|
|
73
|
-
import { HANDLE_RESULT_PROMISE,
|
|
76
|
+
import { HANDLE_RESULT_PROMISE, WorkflowHandle } from "./handles.js";
|
|
74
77
|
import { hasQueuedInlineWorkflowStart } from "./inline-launch-queue.js";
|
|
75
78
|
import {
|
|
76
79
|
handleStrategyMessage as handleStrategyMessageFromInternals,
|
|
@@ -81,6 +84,8 @@ import {
|
|
|
81
84
|
fork as forkFromLifecycle,
|
|
82
85
|
recoverAll as recoverAllFromLifecycle,
|
|
83
86
|
resume as resumeFromLifecycle,
|
|
87
|
+
startOrSignal as startOrSignalFromLifecycle,
|
|
88
|
+
startWithIdempotency as startWithIdempotencyFromLifecycle,
|
|
84
89
|
startWorkflow as startWorkflowFromLifecycle
|
|
85
90
|
} from "./lifecycle.js";
|
|
86
91
|
import {
|
|
@@ -90,6 +95,7 @@ import {
|
|
|
90
95
|
removeTags as removeWorkflowTags,
|
|
91
96
|
setAttributes as setWorkflowAttributes
|
|
92
97
|
} from "./listing.js";
|
|
98
|
+
import { getOffloadFromInternals } from "./operations-data.js";
|
|
93
99
|
import { getStreamChunksFromInternals } from "./operations-stream.js";
|
|
94
100
|
import {
|
|
95
101
|
handleTimerFired as handleTimerFiredFromInternals
|
|
@@ -113,6 +119,7 @@ import {
|
|
|
113
119
|
listReviews as listReviewsFromInternals,
|
|
114
120
|
submitReview as submitReviewFromInternals
|
|
115
121
|
} from "./reviews.js";
|
|
122
|
+
import { ScheduleHandle } from "./schedule-handle.js";
|
|
116
123
|
import {
|
|
117
124
|
cancelSchedule as cancelScheduleFromInternals,
|
|
118
125
|
listSchedules as listSchedulesFromInternals,
|
|
@@ -122,6 +129,10 @@ import {
|
|
|
122
129
|
toScheduleSummary,
|
|
123
130
|
updateSchedule as updateScheduleFromInternals
|
|
124
131
|
} from "./schedules.js";
|
|
132
|
+
import {
|
|
133
|
+
createSecondInstanceDetectionTick,
|
|
134
|
+
createSecondInstanceDetector
|
|
135
|
+
} from "./second-instance-detector.js";
|
|
125
136
|
import { signal as signalWorkflow } from "./signals.js";
|
|
126
137
|
import { loadScheduleState, loadWorkflowState } from "./storage-io.js";
|
|
127
138
|
import {
|
|
@@ -133,6 +144,7 @@ import {
|
|
|
133
144
|
cancelWorkflow as cancelWorkflowFromTermination,
|
|
134
145
|
cleanupWaiters as cleanupWaitersFromTermination,
|
|
135
146
|
finalizePendingTimelineEntry,
|
|
147
|
+
suspendWorkflow as suspendWorkflowFromTermination,
|
|
136
148
|
timeoutWorkflow as timeoutWorkflowFromTermination
|
|
137
149
|
} from "./termination.js";
|
|
138
150
|
import {
|
|
@@ -158,13 +170,17 @@ export {
|
|
|
158
170
|
BulkOperationConfirmationError,
|
|
159
171
|
EngineCreateNameMismatchError,
|
|
160
172
|
EngineDisposedError,
|
|
173
|
+
IdempotencyKeyPurgedError,
|
|
161
174
|
PersistedDataIncompatibleError,
|
|
175
|
+
StartOrSignalConflictError,
|
|
162
176
|
WorkflowAlreadyExistsError,
|
|
163
177
|
WorkflowNotFoundError,
|
|
164
178
|
WorkflowNotRegisteredError,
|
|
179
|
+
WorkflowSuspendNotSupportedError,
|
|
165
180
|
WorkflowTypeNotRegisteredForRecoveryError
|
|
166
181
|
} from "./errors.js";
|
|
167
|
-
export { HANDLE_RESULT_PROMISE,
|
|
182
|
+
export { HANDLE_RESULT_PROMISE, WorkflowHandle } from "./handles.js";
|
|
183
|
+
export { ScheduleHandle } from "./schedule-handle.js";
|
|
168
184
|
export {
|
|
169
185
|
clearEngineLeakWarningTokenForTesting,
|
|
170
186
|
getEngineLeakCollectionCountForTesting,
|
|
@@ -174,13 +190,13 @@ export {
|
|
|
174
190
|
shouldEmitEngineLeakWarningForTesting
|
|
175
191
|
} from "./engine-leak-warnings.js";
|
|
176
192
|
export { assertCompatiblePersistedDataVersion };
|
|
177
|
-
export const ENGINE_PARKED_WORKFLOW_COUNT_FOR_TESTING = Symbol("engineParkedWorkflowCountForTesting"), ENGINE_SIGNAL_WAITER_COUNT_FOR_TESTING = Symbol("engineSignalWaiterCountForTesting");
|
|
193
|
+
export const ENGINE_PARKED_WORKFLOW_COUNT_FOR_TESTING = Symbol("engineParkedWorkflowCountForTesting"), ENGINE_SIGNAL_WAITER_COUNT_FOR_TESTING = Symbol("engineSignalWaiterCountForTesting"), ENGINE_SLEEP_RESOLVER_COUNT_FOR_TESTING = Symbol("engineSleepResolverCountForTesting");
|
|
178
194
|
|
|
179
195
|
export class Engine extends EventTarget {
|
|
180
196
|
static async create(options) {
|
|
181
197
|
const engine = new Engine(options);
|
|
182
198
|
try {
|
|
183
|
-
await assertCompatiblePersistedDataVersion(getInternals(engine).storage
|
|
199
|
+
await assertCompatiblePersistedDataVersion(getInternals(engine).storage);
|
|
184
200
|
for (const [name, definition] of definitionEntries(options.activities)) {
|
|
185
201
|
if (name !== definition.name)
|
|
186
202
|
throw new EngineCreateNameMismatchError("activity", name, definition.name);
|
|
@@ -213,7 +229,8 @@ export class Engine extends EventTarget {
|
|
|
213
229
|
getRegistration: getInternals(this).registrations.get.bind(getInternals(this).registrations),
|
|
214
230
|
getComposedWorkflowInterceptor: () => getComposedWorkflowInterceptor(getInternals(this)),
|
|
215
231
|
resolveWorkflowType: this.#resolveWorkflowTypeTarget.bind(this),
|
|
216
|
-
registerCancelHandler: (workflowId, handler) => registerCancelHandler(getInternals(this), workflowId, handler)
|
|
232
|
+
registerCancelHandler: (workflowId, handler) => registerCancelHandler(getInternals(this), workflowId, handler),
|
|
233
|
+
getWorkflowServices: (workflowId) => getInternals(this).workflowServices.get(workflowId)
|
|
217
234
|
});
|
|
218
235
|
getInternals(this).storage = storage;
|
|
219
236
|
getInternals(this).abortController = new AbortController;
|
|
@@ -261,6 +278,7 @@ export class Engine extends EventTarget {
|
|
|
261
278
|
if (queuedInlineWorkflowStartChannel !== null)
|
|
262
279
|
queuedInlineWorkflowStartChannel.port1.onmessage = createQueuedInlineWorkflowStartHandler(weakEngine, queuedInlineWorkflowStartChannel);
|
|
263
280
|
getInternals(this).heartbeatDetails = new Map;
|
|
281
|
+
getInternals(this).workflowServices = new Map;
|
|
264
282
|
getInternals(this).pendingAsyncActivities = new Map;
|
|
265
283
|
getInternals(this).pendingStarts = new Set;
|
|
266
284
|
getInternals(this).pendingScheduleCreations = new Set;
|
|
@@ -280,6 +298,7 @@ export class Engine extends EventTarget {
|
|
|
280
298
|
const cleanupIntervalDisposalTracker = {
|
|
281
299
|
disposed: !1,
|
|
282
300
|
cleanupInterval: null,
|
|
301
|
+
secondInstanceDetectionInterval: null,
|
|
283
302
|
testToken: consumeNextEngineLeakWarningTokenForTesting()
|
|
284
303
|
}, cleanupInterval = setInterval(createCleanupIntervalTick(weakEngine, cleanupIntervalDisposalTracker), 60000);
|
|
285
304
|
cleanupIntervalDisposalTracker.cleanupInterval = cleanupInterval;
|
|
@@ -289,6 +308,8 @@ export class Engine extends EventTarget {
|
|
|
289
308
|
getInternals(this).retentionSweepInterval = null;
|
|
290
309
|
getInternals(this).retentionSweepInFlight = null;
|
|
291
310
|
getInternals(this).nextRetentionSweepAt = null;
|
|
311
|
+
getInternals(this).secondInstanceDetectionInterval = null;
|
|
312
|
+
getInternals(this).secondInstanceDetector = null;
|
|
292
313
|
getInternals(this).eventLogHeads = new Map;
|
|
293
314
|
getInternals(this).workflowFeedListeners = new Map;
|
|
294
315
|
getInternals(this).workflowVersionTuples = new Map;
|
|
@@ -296,6 +317,25 @@ export class Engine extends EventTarget {
|
|
|
296
317
|
getInternals(this).strategy.onMessage(this.#handleStrategyMessage.bind(this));
|
|
297
318
|
getInternals(this).alertManager = createAlertManagerForEngine(this, options?.alerts, getNow);
|
|
298
319
|
this.#ensureRetentionSweepInterval();
|
|
320
|
+
this.#startSecondInstanceDetection();
|
|
321
|
+
}
|
|
322
|
+
#startSecondInstanceDetection() {
|
|
323
|
+
const internals = getInternals(this);
|
|
324
|
+
if (!internals.options.secondInstanceDetectionEnabled)
|
|
325
|
+
return;
|
|
326
|
+
const detector = createSecondInstanceDetector({
|
|
327
|
+
storage: internals.storage,
|
|
328
|
+
instanceId: crypto.randomUUID(),
|
|
329
|
+
getNow: internals.options.getNow,
|
|
330
|
+
intervalMs: internals.options.secondInstanceHeartbeatIntervalMs
|
|
331
|
+
});
|
|
332
|
+
internals.secondInstanceDetector = detector;
|
|
333
|
+
const tracker = internals.cleanupIntervalDisposalTracker;
|
|
334
|
+
if (tracker === null)
|
|
335
|
+
return;
|
|
336
|
+
const weakEngine = new WeakRef(this), detectionInterval = setInterval(createSecondInstanceDetectionTick(createSecondInstanceDetectorResolver(weakEngine), tracker), internals.options.secondInstanceHeartbeatIntervalMs);
|
|
337
|
+
internals.secondInstanceDetectionInterval = detectionInterval;
|
|
338
|
+
tracker.secondInstanceDetectionInterval = detectionInterval;
|
|
299
339
|
}
|
|
300
340
|
#hasConfiguredRetention() {
|
|
301
341
|
return hasConfiguredRetention(getInternals(this));
|
|
@@ -397,8 +437,19 @@ export class Engine extends EventTarget {
|
|
|
397
437
|
return getInternals(this).activityRegistry.listDefinitions();
|
|
398
438
|
}
|
|
399
439
|
async start(type, input, options) {
|
|
440
|
+
if (options?.idempotencyKey !== void 0)
|
|
441
|
+
return startWithIdempotencyFromLifecycle(getInternals(this), type, input, options, this.#createLifecycleCallbacks());
|
|
400
442
|
return startWorkflowFromLifecycle(getInternals(this), type, input, options, void 0, this.#createLifecycleCallbacks());
|
|
401
443
|
}
|
|
444
|
+
async startOrSignal(type, input, signal, options) {
|
|
445
|
+
return startOrSignalFromLifecycle(getInternals(this), type, input, signal, options, this.#createStartOrSignalCallbacks());
|
|
446
|
+
}
|
|
447
|
+
#createStartOrSignalCallbacks() {
|
|
448
|
+
return {
|
|
449
|
+
...this.#createLifecycleCallbacks(),
|
|
450
|
+
signalExistingWorkflow: (workflowId, signalName, payload, signalId) => this.signal(workflowId, signalName, payload, { signalId })
|
|
451
|
+
};
|
|
452
|
+
}
|
|
402
453
|
getHandle(workflowId) {
|
|
403
454
|
const entry = getInternals(this).handleCache.get(workflowId);
|
|
404
455
|
if (entry) {
|
|
@@ -478,6 +529,9 @@ export class Engine extends EventTarget {
|
|
|
478
529
|
[ENGINE_SIGNAL_WAITER_COUNT_FOR_TESTING]() {
|
|
479
530
|
return getInternals(this).signalWaiters.size;
|
|
480
531
|
}
|
|
532
|
+
[ENGINE_SLEEP_RESOLVER_COUNT_FOR_TESTING]() {
|
|
533
|
+
return getInternals(this).sleepResolvers.size;
|
|
534
|
+
}
|
|
481
535
|
async signal(workflowId, nameOrDefinition, payload, options) {
|
|
482
536
|
return signalWorkflow(getInternals(this), workflowId, messageName(nameOrDefinition), payload, {
|
|
483
537
|
loadWorkflowState: (id) => loadWorkflowState(getInternals(this), id),
|
|
@@ -496,6 +550,9 @@ export class Engine extends EventTarget {
|
|
|
496
550
|
async getStreamChunks(workflowId, key, options) {
|
|
497
551
|
return getStreamChunksFromInternals(getInternals(this), workflowId, key, options);
|
|
498
552
|
}
|
|
553
|
+
async getOffload(workflowId, key) {
|
|
554
|
+
return getOffloadFromInternals(getInternals(this), workflowId, key);
|
|
555
|
+
}
|
|
499
556
|
async fork(sourceWorkflowId, options) {
|
|
500
557
|
return forkFromLifecycle(getInternals(this), sourceWorkflowId, options, this.#createLifecycleCallbacks());
|
|
501
558
|
}
|
|
@@ -515,6 +572,9 @@ export class Engine extends EventTarget {
|
|
|
515
572
|
async cancel(workflowId) {
|
|
516
573
|
await cancelWorkflowFromTermination(getInternals(this), workflowId, this.#createTerminationCallbacks());
|
|
517
574
|
}
|
|
575
|
+
async suspend(workflowId) {
|
|
576
|
+
await suspendWorkflowFromTermination(getInternals(this), workflowId, this.#createTerminationCallbacks());
|
|
577
|
+
}
|
|
518
578
|
async timeout(workflowId) {
|
|
519
579
|
await timeoutWorkflowFromTermination(getInternals(this), workflowId, this.#createTerminationCallbacks());
|
|
520
580
|
}
|
|
@@ -524,6 +584,15 @@ export class Engine extends EventTarget {
|
|
|
524
584
|
return { ...state, status: "pending" };
|
|
525
585
|
return state;
|
|
526
586
|
}
|
|
587
|
+
async getCurrentCheckpointStep(workflowId) {
|
|
588
|
+
const inMemory = getInternals(this).checkpoints.get(workflowId);
|
|
589
|
+
if (inMemory !== void 0)
|
|
590
|
+
return inMemory.step;
|
|
591
|
+
const bytes = await getInternals(this).storage.get(KEYS.checkpoint(workflowId));
|
|
592
|
+
if (bytes === null)
|
|
593
|
+
return null;
|
|
594
|
+
return deserializeCheckpoint(bytes).step;
|
|
595
|
+
}
|
|
527
596
|
async getAttributes(workflowId) {
|
|
528
597
|
return getWorkflowAttributes(getInternals(this), workflowId);
|
|
529
598
|
}
|
|
@@ -581,6 +650,14 @@ export class Engine extends EventTarget {
|
|
|
581
650
|
disposeEngine(getInternals(this));
|
|
582
651
|
}
|
|
583
652
|
async[Symbol.asyncDispose]() {
|
|
653
|
+
if (!getInternals(this).disposed) {
|
|
654
|
+
try {
|
|
655
|
+
await drainQueuedInlineWorkflowStartsForEngine(this);
|
|
656
|
+
} finally {
|
|
657
|
+
this[Symbol.dispose]();
|
|
658
|
+
}
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
584
661
|
this[Symbol.dispose]();
|
|
585
662
|
}
|
|
586
663
|
get storage() {
|