@lostgradient/weft 0.24.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cli/conformance.js +5 -2
- package/dist/cli/generated/operation-catalog.snapshot.json +95 -1
- package/dist/cli/generated/operation-client.generated.d.ts +28 -2
- package/dist/cli/generated/operation-client.generated.js +2 -0
- package/dist/cli/parse-schedule-arguments.js +15 -3
- package/dist/cli/schedule.js +2 -1
- package/dist/cli/types.d.ts +2 -1
- package/dist/client/http-client-requests.js +6 -9
- package/dist/client/start-body.js +1 -0
- package/dist/core/catalog/index.d.ts +2 -2
- package/dist/core/catalog/index.js +9 -2
- package/dist/core/catalog/reference-counts.d.ts +29 -14
- package/dist/core/catalog/removal.d.ts +63 -10
- package/dist/core/catalog/removal.js +18 -2
- package/dist/core/catalog/storage-io.d.ts +23 -0
- package/dist/core/catalog/storage-io.js +1 -1
- package/dist/core/engine/activity-resolution.js +17 -17
- package/dist/core/engine/bulk-operations-purge.js +14 -20
- package/dist/core/engine/bulk-operations-retry.d.ts +9 -0
- package/dist/core/engine/bulk-operations-retry.js +177 -0
- package/dist/core/engine/bulk-operations-shared.d.ts +18 -0
- package/dist/core/engine/bulk-operations-shared.js +14 -0
- package/dist/core/engine/bulk-operations.js +8 -167
- package/dist/core/engine/callback-creators-bundles.js +4 -2
- package/dist/core/engine/callback-creators-core.js +9 -1
- package/dist/core/engine/callback-creators-schedule.js +2 -2
- package/dist/core/engine/catalog-readiness.js +2 -0
- package/dist/core/engine/catalog-removal.d.ts +107 -3
- package/dist/core/engine/catalog-removal.js +66 -13
- package/dist/core/engine/catalog-tombstone-recovery.d.ts +56 -0
- package/dist/core/engine/catalog-tombstone-recovery.js +39 -0
- package/dist/core/engine/constraints.js +5 -1
- package/dist/core/engine/construction.d.ts +1 -1
- package/dist/core/engine/decode-revision.d.ts +32 -0
- package/dist/core/engine/decode-revision.js +9 -0
- package/dist/core/engine/disposal.d.ts +6 -2
- package/dist/core/engine/disposal.js +24 -10
- package/dist/core/engine/dynamic-source-errors.d.ts +81 -0
- package/dist/core/engine/dynamic-source-errors.js +25 -0
- package/dist/core/engine/dynamic-source-execution.d.ts +173 -0
- package/dist/core/engine/dynamic-source-execution.js +108 -0
- package/dist/core/engine/engine-internal-types.d.ts +16 -0
- package/dist/core/engine/engine-workflows-namespace.d.ts +10 -0
- package/dist/core/engine/engine-workflows-namespace.js +3 -1
- package/dist/core/engine/index.d.ts +18 -10
- package/dist/core/engine/index.js +21 -10
- package/dist/core/engine/inline-launch-queue.js +1 -1
- package/dist/core/engine/internals.d.ts +26 -30
- package/dist/core/engine/lifecycle/checkpoint-launch.d.ts +29 -0
- package/dist/core/engine/lifecycle/checkpoint-launch.js +75 -0
- package/dist/core/engine/lifecycle/delayed-start-registration.d.ts +30 -0
- package/dist/core/engine/lifecycle/delayed-start-registration.js +11 -0
- package/dist/core/engine/lifecycle/fork-helpers.d.ts +33 -1
- package/dist/core/engine/lifecycle/fork-helpers.js +3 -1
- package/dist/core/engine/lifecycle/recovery-revision-groups.d.ts +75 -0
- package/dist/core/engine/lifecycle/recovery-revision-groups.js +64 -0
- package/dist/core/engine/lifecycle/resume-generation-guard.d.ts +58 -0
- package/dist/core/engine/lifecycle/resume-generation-guard.js +11 -0
- package/dist/core/engine/lifecycle/resume.js +18 -9
- package/dist/core/engine/lifecycle/shared.d.ts +43 -0
- package/dist/core/engine/lifecycle/standalone-claim-acquire.d.ts +12 -0
- package/dist/core/engine/lifecycle/standalone-claim-acquire.js +6 -0
- package/dist/core/engine/lifecycle/start-commit-errors.d.ts +12 -0
- package/dist/core/engine/lifecycle/start-commit-errors.js +6 -0
- package/dist/core/engine/lifecycle/start-commit.d.ts +22 -7
- package/dist/core/engine/lifecycle/start-commit.js +49 -15
- package/dist/core/engine/lifecycle/start-exec.d.ts +18 -2
- package/dist/core/engine/lifecycle/start-exec.js +7 -5
- package/dist/core/engine/lifecycle/start-or-signal-create.js +1 -3
- package/dist/core/engine/lifecycle/start-or-signal.js +1 -1
- package/dist/core/engine/lifecycle/start-precondition-attribution.d.ts +49 -0
- package/dist/core/engine/lifecycle/start-precondition-attribution.js +13 -0
- package/dist/core/engine/lifecycle/start-revision-resolution.d.ts +34 -0
- package/dist/core/engine/lifecycle/start-revision-resolution.js +11 -0
- package/dist/core/engine/lifecycle/start-state.d.ts +16 -0
- package/dist/core/engine/lifecycle/start-state.js +60 -0
- package/dist/core/engine/lifecycle/start-terminal-conflict-purge.d.ts +66 -3
- package/dist/core/engine/lifecycle/start-terminal-conflict-purge.js +13 -3
- package/dist/core/engine/lifecycle/start.d.ts +16 -6
- package/dist/core/engine/lifecycle/start.js +38 -68
- package/dist/core/engine/lifecycle/transition.d.ts +3 -4
- package/dist/core/engine/lifecycle/transition.js +30 -80
- package/dist/core/engine/lifecycle.d.ts +4 -2
- package/dist/core/engine/lifecycle.js +6 -6
- package/dist/core/engine/listing.js +15 -1
- package/dist/core/engine/memo-durable-activity.js +4 -1
- package/dist/core/engine/nonterminal-revision-count.d.ts +25 -0
- package/dist/core/engine/nonterminal-revision-count.js +16 -0
- package/dist/core/engine/operations-time.d.ts +5 -23
- package/dist/core/engine/operations-time.js +13 -11
- package/dist/core/engine/ownership-bootstrap.d.ts +1 -1
- package/dist/core/engine/pinned-schedule-revision-count.d.ts +26 -0
- package/dist/core/engine/pinned-schedule-revision-count.js +19 -0
- package/dist/core/engine/pinned-schedule-revision.d.ts +76 -0
- package/dist/core/engine/pinned-schedule-revision.js +53 -0
- package/dist/core/engine/registration.d.ts +30 -0
- package/dist/core/engine/registration.js +17 -2
- package/dist/core/engine/retention.js +9 -2
- package/dist/core/engine/revision-errors.d.ts +71 -0
- package/dist/core/engine/revision-errors.js +13 -0
- package/dist/core/engine/schedule-revision-fire.d.ts +17 -0
- package/dist/core/engine/schedule-revision-fire.js +3 -0
- package/dist/core/engine/schedule-run.js +2 -1
- package/dist/core/engine/schedules.d.ts +2 -1
- package/dist/core/engine/schedules.js +28 -9
- package/dist/core/engine/source-diagnostics.d.ts +76 -0
- package/dist/core/engine/source-diagnostics.js +105 -0
- package/dist/core/engine/source-registration.js +4 -2
- package/dist/core/engine/source-resolution.d.ts +25 -9
- package/dist/core/engine/source-resolution.js +49 -14
- package/dist/core/engine/source-runtime-state.d.ts +92 -0
- package/dist/core/engine/source-runtime-state.js +36 -0
- package/dist/core/engine/storage-io.d.ts +19 -2
- package/dist/core/engine/storage-io.js +21 -2
- package/dist/core/engine/termination/finalizer-registration.d.ts +18 -0
- package/dist/core/engine/termination/finalizer-registration.js +21 -0
- package/dist/core/engine/termination/finalizer.d.ts +16 -19
- package/dist/core/engine/termination/finalizer.js +3 -3
- package/dist/core/engine/time-operation-callbacks.d.ts +32 -0
- package/dist/core/engine/time-operation-callbacks.js +0 -0
- package/dist/core/engine/validation/schedule-cadence.d.ts +16 -0
- package/dist/core/engine/validation/schedule-cadence.js +20 -0
- package/dist/core/engine/validation/schedule-options.d.ts +4 -1
- package/dist/core/engine/validation/schedule-options.js +14 -0
- package/dist/core/engine/validation/schedule-revision.d.ts +24 -0
- package/dist/core/engine/validation/schedule-revision.js +21 -0
- package/dist/core/engine/validation/schedule-warnings.d.ts +11 -0
- package/dist/core/engine/validation/schedule-warnings.js +5 -0
- package/dist/core/engine/validation/schedule.d.ts +4 -3
- package/dist/core/engine/validation/schedule.js +15 -26
- package/dist/core/engine/validation.js +3 -0
- package/dist/core/engine/workflow-claim-reclaim-target.d.ts +1 -1
- package/dist/core/engine/workflow-claim-reclaim-target.js +16 -2
- package/dist/core/engine/workflow-retention-deadline.d.ts +16 -0
- package/dist/core/engine/workflow-retention-deadline.js +33 -0
- package/dist/core/events/event-map.d.ts +5 -0
- package/dist/core/events/index.d.ts +1 -0
- package/dist/core/events/index.js +1 -0
- package/dist/core/events/workflow-source-events.d.ts +102 -0
- package/dist/core/events/workflow-source-events.js +57 -0
- package/dist/core/execution-strategy.d.ts +7 -0
- package/dist/core/inline-execution-strategy.context-options.d.ts +1 -1
- package/dist/core/inline-execution-strategy.js +1 -1
- package/dist/core/types/checkpoint.d.ts +14 -0
- package/dist/core/types/schedules.d.ts +52 -1
- package/dist/core/types/state.d.ts +16 -0
- package/dist/core/weft-error.d.ts +1 -1
- package/dist/core/weft-error.js +4 -1
- package/dist/core/worker-execution-strategy.d.ts +1 -0
- package/dist/core/worker-execution-strategy.js +13 -1
- package/dist/core/worker-fault-handling.d.ts +10 -0
- package/dist/core/worker-fault-handling.js +1 -0
- package/dist/core/worker-inbound-message.d.ts +1 -0
- package/dist/core/worker-inbound-message.js +1 -0
- package/dist/core/worker-protocol-guard.js +4 -0
- package/dist/core/worker-protocol.d.ts +7 -0
- package/dist/core/worker-protocol.js +2 -1
- package/dist/core/worker-turn-watchdog.d.ts +8 -1
- package/dist/core/worker-turn-watchdog.js +3 -2
- package/dist/diagnostics/format.js +5 -0
- package/dist/diagnostics/types.d.ts +24 -0
- package/dist/diagnostics/version-check.js +8 -2
- package/dist/http.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +7 -0
- package/dist/indexeddb.js +1 -1
- package/dist/json-schema.js +3 -3
- package/dist/server/fault-to-json-rpc.js +2 -1
- package/dist/server/index.d.ts +11 -0
- package/dist/server/operation-fault.d.ts +2 -0
- package/dist/server/operation-fault.js +2 -1
- package/dist/server/operations/create-schedule.d.ts +2 -0
- package/dist/server/operations/create-schedule.js +10 -6
- package/dist/server/operations/get-catalog-diagnostics.d.ts +28 -0
- package/dist/server/operations/get-catalog-diagnostics.js +10 -2
- package/dist/server/operations/get-task-detail-schema.d.ts +8 -0
- package/dist/server/operations/get-task-detail-schema.js +1 -0
- package/dist/server/operations/get-task-detail.d.ts +8 -0
- package/dist/server/operations/get-task-detail.js +1 -0
- package/dist/server/operations/get-task-diagnostics.d.ts +4 -4
- package/dist/server/operations/preload-workflow-revision.d.ts +28 -0
- package/dist/server/operations/preload-workflow-revision.js +60 -0
- package/dist/server/operations/schedule-faults.d.ts +1 -0
- package/dist/server/operations/schedule-faults.js +32 -4
- package/dist/server/operations/schedule-rest-body.d.ts +1 -0
- package/dist/server/operations/schedule-rest-body.js +2 -1
- package/dist/server/operations/static-registrations.js +6 -0
- package/dist/server/operations/update-schedule.d.ts +2 -0
- package/dist/server/operations/update-schedule.js +4 -2
- package/dist/server/operations/workflow-catalog-operation-helpers.js +27 -0
- package/dist/server/runtime/task-dispatch-envelope.d.ts +14 -0
- package/dist/server/runtime/task-dispatch-envelope.js +32 -0
- package/dist/server/runtime/task-dispatch-revision.d.ts +29 -0
- package/dist/server/runtime/task-dispatch-revision.js +13 -0
- package/dist/server/runtime/task-dispatch.js +12 -37
- package/dist/server/runtime/task-ledger-recovery.js +1 -1
- package/dist/server/runtime/task-ledger-runtime.d.ts +14 -2
- package/dist/server/runtime/task-ledger-runtime.js +5 -3
- package/dist/server/runtime/task-polling.js +9 -3
- package/dist/server/runtime/task-reconciliation.js +2 -1
- package/dist/server/runtime/websocket-worker.js +9 -0
- package/dist/server/task-ledger-codec.d.ts +9 -0
- package/dist/server/task-ledger-codec.js +4 -1
- package/dist/server/task-ledger-transition-helpers.js +1 -0
- package/dist/server/task-ledger-types.d.ts +9 -0
- package/dist/server/task-ledger.d.ts +1 -1
- package/dist/server/task-ledger.js +2 -1
- package/dist/server/task-queue-types.d.ts +2 -0
- package/dist/storage/bun-sql.js +3 -1
- package/dist/storage/catalog-keys.d.ts +19 -0
- package/dist/storage/catalog-keys.js +3 -1
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/index.d.ts +3 -1
- package/dist/storage/interface.d.ts +2 -0
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +1 -0
- package/dist/storage/lmdb.d.ts +30 -2
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.js +2 -2
- package/dist/storage/node-sqlite.js +3 -1
- package/dist/storage/postgres.js +2 -2
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/testing.js +1 -1
- package/dist/storage/turso.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web-extension.js +1 -1
- package/dist/worker/protocol-messages.d.ts +17 -0
- package/dist/worker/protocol-schemas.d.ts +32 -0
- package/dist/worker/protocol-schemas.js +7 -3
- package/dist/worker/protocol-task-result.js +35 -3
- package/dist/worker/protocol.js +1 -1
- package/dist/worker/registry/types.d.ts +2 -0
- package/dist/worker/registry.d.ts +1 -1
- package/dist/worker/registry.js +3 -1
- package/dist/workers/workflow-runner.d.ts +9 -0
- package/dist/workers/workflow-runner.js +9 -2
- package/dist/workers/workflow-worker-entry.js +9 -4
- package/package.json +2 -2
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { FailureCategory } from '../types/identity.ts';
|
|
2
|
+
type WorkflowSourceKind = import('../source/index.ts').WorkflowSourceKind;
|
|
3
|
+
/**
|
|
4
|
+
* Fired on the {@link Engine} when a dynamic workflow source's single-flight
|
|
5
|
+
* load for `(workflowType, revision)` starts — the loader is about to be
|
|
6
|
+
* invoked. Never fired for a cache hit (an already-installed revision
|
|
7
|
+
* `resolveWorkflowSource()` returns without invoking the loader).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { Engine, WorkflowSourceLoadStartedEvent } from '@lostgradient/weft';
|
|
12
|
+
*
|
|
13
|
+
* const engine = new Engine();
|
|
14
|
+
* engine.addEventListener(WorkflowSourceLoadStartedEvent.type, (event) => {
|
|
15
|
+
* console.log('loading:', event.workflowType, event.revision);
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare class WorkflowSourceLoadStartedEvent extends Event {
|
|
20
|
+
static readonly type: "workflow-source:load-started";
|
|
21
|
+
readonly workflowType: string;
|
|
22
|
+
readonly revision: string;
|
|
23
|
+
readonly kind: WorkflowSourceKind;
|
|
24
|
+
constructor(workflowType: string, revision: string, kind: WorkflowSourceKind);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Fired on the {@link Engine} when a dynamic workflow source's load for
|
|
28
|
+
* `(workflowType, revision)` completes successfully — the loader ran,
|
|
29
|
+
* validation passed, and the manifest is durably installed.
|
|
30
|
+
* `loadDurationMs` is measured via the engine's own injected clock.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* import { Engine, WorkflowSourceLoadReadyEvent } from '@lostgradient/weft';
|
|
35
|
+
*
|
|
36
|
+
* const engine = new Engine();
|
|
37
|
+
* engine.addEventListener(WorkflowSourceLoadReadyEvent.type, (event) => {
|
|
38
|
+
* console.log('ready:', event.workflowType, event.revision, event.loadDurationMs);
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare class WorkflowSourceLoadReadyEvent extends Event {
|
|
43
|
+
static readonly type: "workflow-source:load-ready";
|
|
44
|
+
readonly workflowType: string;
|
|
45
|
+
readonly revision: string;
|
|
46
|
+
readonly kind: WorkflowSourceKind;
|
|
47
|
+
readonly loadDurationMs: number;
|
|
48
|
+
constructor(workflowType: string, revision: string, kind: WorkflowSourceKind, loadDurationMs: number);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Fired on the {@link Engine} when a dynamic workflow source's load for
|
|
52
|
+
* `(workflowType, revision)` fails — the loader threw, validation rejected
|
|
53
|
+
* the loaded module, or the durable install failed. `failureCategory` is
|
|
54
|
+
* the closed, low-cardinality {@link FailureCategory} classification, safe
|
|
55
|
+
* to use as a metric label; the underlying error itself is never included.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* import { Engine, WorkflowSourceLoadFailedEvent } from '@lostgradient/weft';
|
|
60
|
+
*
|
|
61
|
+
* const engine = new Engine();
|
|
62
|
+
* engine.addEventListener(WorkflowSourceLoadFailedEvent.type, (event) => {
|
|
63
|
+
* console.log('failed:', event.workflowType, event.revision, event.failureCategory);
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare class WorkflowSourceLoadFailedEvent extends Event {
|
|
68
|
+
static readonly type: "workflow-source:load-failed";
|
|
69
|
+
readonly workflowType: string;
|
|
70
|
+
readonly revision: string;
|
|
71
|
+
readonly kind: WorkflowSourceKind;
|
|
72
|
+
readonly loadDurationMs: number;
|
|
73
|
+
readonly failureCategory: FailureCategory;
|
|
74
|
+
constructor(workflowType: string, revision: string, kind: WorkflowSourceKind, loadDurationMs: number, failureCategory: FailureCategory);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Fired on the {@link Engine} when the LAST outstanding
|
|
78
|
+
* `resolveWorkflowSource()` waiter for `(workflowType, revision)` releases
|
|
79
|
+
* (its own abort, or engine disposal) while the shared load is still
|
|
80
|
+
* unsettled. The shared load itself is never aborted — a fresh caller
|
|
81
|
+
* starting a new attempt for the same key re-fires
|
|
82
|
+
* {@link WorkflowSourceLoadStartedEvent}, not this event again for the
|
|
83
|
+
* orphaned attempt.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* import { Engine, WorkflowSourceLoadCancelledEvent } from '@lostgradient/weft';
|
|
88
|
+
*
|
|
89
|
+
* const engine = new Engine();
|
|
90
|
+
* engine.addEventListener(WorkflowSourceLoadCancelledEvent.type, (event) => {
|
|
91
|
+
* console.log('cancelled:', event.workflowType, event.revision);
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare class WorkflowSourceLoadCancelledEvent extends Event {
|
|
96
|
+
static readonly type: "workflow-source:load-cancelled";
|
|
97
|
+
readonly workflowType: string;
|
|
98
|
+
readonly revision: string;
|
|
99
|
+
readonly kind: WorkflowSourceKind;
|
|
100
|
+
constructor(workflowType: string, revision: string, kind: WorkflowSourceKind);
|
|
101
|
+
}
|
|
102
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export class WorkflowSourceLoadStartedEvent extends Event {
|
|
2
|
+
static type = "workflow-source:load-started";
|
|
3
|
+
workflowType;
|
|
4
|
+
revision;
|
|
5
|
+
kind;
|
|
6
|
+
constructor(workflowType, revision, kind) {
|
|
7
|
+
super(WorkflowSourceLoadStartedEvent.type);
|
|
8
|
+
this.workflowType = workflowType;
|
|
9
|
+
this.revision = revision;
|
|
10
|
+
this.kind = kind;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class WorkflowSourceLoadReadyEvent extends Event {
|
|
15
|
+
static type = "workflow-source:load-ready";
|
|
16
|
+
workflowType;
|
|
17
|
+
revision;
|
|
18
|
+
kind;
|
|
19
|
+
loadDurationMs;
|
|
20
|
+
constructor(workflowType, revision, kind, loadDurationMs) {
|
|
21
|
+
super(WorkflowSourceLoadReadyEvent.type);
|
|
22
|
+
this.workflowType = workflowType;
|
|
23
|
+
this.revision = revision;
|
|
24
|
+
this.kind = kind;
|
|
25
|
+
this.loadDurationMs = loadDurationMs;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class WorkflowSourceLoadFailedEvent extends Event {
|
|
30
|
+
static type = "workflow-source:load-failed";
|
|
31
|
+
workflowType;
|
|
32
|
+
revision;
|
|
33
|
+
kind;
|
|
34
|
+
loadDurationMs;
|
|
35
|
+
failureCategory;
|
|
36
|
+
constructor(workflowType, revision, kind, loadDurationMs, failureCategory) {
|
|
37
|
+
super(WorkflowSourceLoadFailedEvent.type);
|
|
38
|
+
this.workflowType = workflowType;
|
|
39
|
+
this.revision = revision;
|
|
40
|
+
this.kind = kind;
|
|
41
|
+
this.loadDurationMs = loadDurationMs;
|
|
42
|
+
this.failureCategory = failureCategory;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class WorkflowSourceLoadCancelledEvent extends Event {
|
|
47
|
+
static type = "workflow-source:load-cancelled";
|
|
48
|
+
workflowType;
|
|
49
|
+
revision;
|
|
50
|
+
kind;
|
|
51
|
+
constructor(workflowType, revision, kind) {
|
|
52
|
+
super(WorkflowSourceLoadCancelledEvent.type);
|
|
53
|
+
this.workflowType = workflowType;
|
|
54
|
+
this.revision = revision;
|
|
55
|
+
this.kind = kind;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -85,6 +85,13 @@ export interface ExecutionStrategy extends Disposable, AsyncDisposable {
|
|
|
85
85
|
startWorkflow(parameters: {
|
|
86
86
|
workflowId: string;
|
|
87
87
|
workflowExecutionToken?: string;
|
|
88
|
+
/**
|
|
89
|
+
* The starting run's persisted `WorkflowState.revision` (WFT-20). Threaded
|
|
90
|
+
* through to the worker strategy's outbound echo/validation; an inline
|
|
91
|
+
* strategy accepts and ignores it — inline execution crosses no trust
|
|
92
|
+
* boundary this field needs to police.
|
|
93
|
+
*/
|
|
94
|
+
revision?: string;
|
|
88
95
|
workflowType: string;
|
|
89
96
|
input: unknown;
|
|
90
97
|
checkpoint: ArrayBuffer | Uint8Array;
|
|
@@ -11,7 +11,7 @@ import type { SearchAttributeSchema, WorkflowFunction } from './types.ts';
|
|
|
11
11
|
import type { WorkflowLogRecord } from './types/workflow-log.ts';
|
|
12
12
|
/** Capabilities the engine injects into the inline strategy at construction. */
|
|
13
13
|
export interface InlineExecutionDependencies {
|
|
14
|
-
getRegistration: (workflowType: string) => {
|
|
14
|
+
getRegistration: (workflowType: string, workflowId: string) => {
|
|
15
15
|
handler: WorkflowFunction;
|
|
16
16
|
version: string;
|
|
17
17
|
searchAttributes?: SearchAttributeSchema;
|
|
@@ -30,7 +30,7 @@ export class InlineExecutionStrategy {
|
|
|
30
30
|
this.#messageHandler = handler;
|
|
31
31
|
}
|
|
32
32
|
startWorkflow(parameters) {
|
|
33
|
-
const registration = this.#dependencies.getRegistration(parameters.workflowType);
|
|
33
|
+
const registration = this.#dependencies.getRegistration(parameters.workflowType, parameters.workflowId);
|
|
34
34
|
if (!registration) {
|
|
35
35
|
this.#emit({
|
|
36
36
|
type: "failed",
|
|
@@ -199,6 +199,14 @@ export type WorkerInboundMessage = {
|
|
|
199
199
|
maxProtocolMessageBytes?: number;
|
|
200
200
|
workflowId: WorkflowId;
|
|
201
201
|
workflowExecutionToken?: string;
|
|
202
|
+
/**
|
|
203
|
+
* The dispatching run's persisted `WorkflowState.revision` (WFT-20),
|
|
204
|
+
* when known. Captured worker-side and re-stamped on every outbound
|
|
205
|
+
* message for this workflow's remaining turns (including `resume`
|
|
206
|
+
* turns, which never resend this field) — see
|
|
207
|
+
* `workers/workflow-runner.ts`'s `workflowRevisions` map.
|
|
208
|
+
*/
|
|
209
|
+
workflowRevision?: string;
|
|
202
210
|
workflowType: string;
|
|
203
211
|
checkpoint: ArrayBuffer;
|
|
204
212
|
input: unknown;
|
|
@@ -236,12 +244,16 @@ export type WorkerOutboundMessage = {
|
|
|
236
244
|
workflowId: WorkflowId;
|
|
237
245
|
checkpoint: ArrayBuffer;
|
|
238
246
|
operationRequest: OperationRequest | ContextOperationRequest;
|
|
247
|
+
/** Echo of the captured `run.workflowRevision` (WFT-20) — see that field's doc. */
|
|
248
|
+
workflowRevision?: string;
|
|
239
249
|
} | {
|
|
240
250
|
type: 'completed';
|
|
241
251
|
protocolVersion?: number;
|
|
242
252
|
turnId?: number;
|
|
243
253
|
workflowId: WorkflowId;
|
|
244
254
|
result: unknown;
|
|
255
|
+
/** Echo of the captured `run.workflowRevision` (WFT-20) — see that field's doc. */
|
|
256
|
+
workflowRevision?: string;
|
|
245
257
|
} | {
|
|
246
258
|
type: 'failed';
|
|
247
259
|
protocolVersion?: number;
|
|
@@ -251,6 +263,8 @@ export type WorkerOutboundMessage = {
|
|
|
251
263
|
errorStack?: string;
|
|
252
264
|
/** Populated when the execution strategy can classify the failure cause. */
|
|
253
265
|
failureCategory?: FailureCategory;
|
|
266
|
+
/** Echo of the captured `run.workflowRevision` (WFT-20) — see that field's doc. */
|
|
267
|
+
workflowRevision?: string;
|
|
254
268
|
} | {
|
|
255
269
|
/**
|
|
256
270
|
* A `ctx.log` record forwarded from a worker to the engine host's
|
|
@@ -23,6 +23,27 @@ export type ScheduleStatus = 'active' | 'paused' | 'cancelled';
|
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
25
|
export type ScheduleOverlapPolicy = 'skip' | 'queue' | 'cancel-running' | 'allow';
|
|
26
|
+
/**
|
|
27
|
+
* Which persisted workflow revision a schedule's future occurrences resolve
|
|
28
|
+
* against (WFT-20). `'active-at-fire'` (the default) resolves whatever
|
|
29
|
+
* revision is currently active at the moment each occurrence fires — this is
|
|
30
|
+
* the schedule's pre-WFT-20 behavior, unchanged. `'pinned'` captures the
|
|
31
|
+
* revision that would run right now at create/update time
|
|
32
|
+
* ({@link ScheduleMetadata.pinnedRevision}) and forces every future
|
|
33
|
+
* occurrence to resolve against exactly that revision, pausing the schedule
|
|
34
|
+
* (see `guides/workflow-versioning.md`'s "Schedule revision policy" section)
|
|
35
|
+
* if that revision later becomes unavailable rather than silently falling
|
|
36
|
+
* back to whatever is active.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import type { ScheduleRevisionPolicy } from '@lostgradient/weft';
|
|
41
|
+
*
|
|
42
|
+
* const policy: ScheduleRevisionPolicy = 'pinned';
|
|
43
|
+
* void policy;
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export type ScheduleRevisionPolicy = 'active-at-fire' | 'pinned';
|
|
26
47
|
/**
|
|
27
48
|
* One occurrence waiting behind the active run of a `queue` overlap schedule.
|
|
28
49
|
* `workflowId` is reserved when the occurrence enters the durable queue and is
|
|
@@ -117,12 +138,27 @@ export interface ScheduleOptions {
|
|
|
117
138
|
* pre-jitter occurrence timestamp.
|
|
118
139
|
*/
|
|
119
140
|
jitter?: Duration;
|
|
141
|
+
/**
|
|
142
|
+
* Which revision future occurrences resolve against. Defaults to
|
|
143
|
+
* `'active-at-fire'`. Passing `'pinned'` captures the revision that would
|
|
144
|
+
* run right now — see {@link ScheduleRevisionPolicy}. This is a
|
|
145
|
+
* per-schedule revision override; `StartOptions` (a one-shot
|
|
146
|
+
* `engine.start()` call) has no equivalent per-call revision override.
|
|
147
|
+
*/
|
|
148
|
+
revisionPolicy?: ScheduleRevisionPolicy;
|
|
120
149
|
}
|
|
121
150
|
/**
|
|
122
151
|
* Mutable options accepted when updating an existing schedule. Omitted fields
|
|
123
152
|
* retain their persisted values. Schedule identity, workflow type, and input
|
|
124
153
|
* are intentionally excluded.
|
|
125
154
|
*
|
|
155
|
+
* Omitting `revisionPolicy` preserves the schedule's current policy AND its
|
|
156
|
+
* captured {@link ScheduleMetadata.pinnedRevision} unchanged. Passing
|
|
157
|
+
* `revisionPolicy: 'pinned'` — even when the schedule is already pinned —
|
|
158
|
+
* always RE-resolves and re-captures the pin against the revision active
|
|
159
|
+
* right now; it is never a no-op. Passing `revisionPolicy: 'active-at-fire'`
|
|
160
|
+
* clears any previously captured pin.
|
|
161
|
+
*
|
|
126
162
|
* @example
|
|
127
163
|
* ```ts
|
|
128
164
|
* import { Engine, workflow, type ScheduleUpdateOptions } from '@lostgradient/weft';
|
|
@@ -135,7 +171,7 @@ export interface ScheduleOptions {
|
|
|
135
171
|
* engine[Symbol.dispose]();
|
|
136
172
|
* ```
|
|
137
173
|
*/
|
|
138
|
-
export type ScheduleUpdateOptions = Pick<ScheduleOptions, 'description' | 'overlap' | 'backfill' | 'jitter'>;
|
|
174
|
+
export type ScheduleUpdateOptions = Pick<ScheduleOptions, 'description' | 'overlap' | 'backfill' | 'jitter' | 'revisionPolicy'>;
|
|
139
175
|
/**
|
|
140
176
|
* Declarative recurring schedule definition returned by {@link schedule}. Supply
|
|
141
177
|
* exactly one of `cron` (cron cadence) or `every` (fixed interval).
|
|
@@ -168,6 +204,7 @@ export type ScheduleDefinition<TInput = unknown> = ScheduleSpec & {
|
|
|
168
204
|
overlapPolicy?: ScheduleOverlapPolicy;
|
|
169
205
|
backfill?: boolean;
|
|
170
206
|
jitter?: Duration;
|
|
207
|
+
revisionPolicy?: ScheduleRevisionPolicy;
|
|
171
208
|
};
|
|
172
209
|
/**
|
|
173
210
|
* Create a recurring schedule definition for `engine.schedule(definition)`.
|
|
@@ -206,6 +243,20 @@ export interface ScheduleMetadata {
|
|
|
206
243
|
backfill: boolean;
|
|
207
244
|
/** Normalized deterministic jitter window in milliseconds. */
|
|
208
245
|
jitterMs?: number;
|
|
246
|
+
/**
|
|
247
|
+
* Which revision future occurrences resolve against. Required on every
|
|
248
|
+
* record this package writes; a legacy record persisted before WFT-20
|
|
249
|
+
* decodes as `'active-at-fire'` (see `validation/schedule-revision.ts`) —
|
|
250
|
+
* absence never means "unset," it means "pre-pinning."
|
|
251
|
+
*/
|
|
252
|
+
revisionPolicy: ScheduleRevisionPolicy;
|
|
253
|
+
/**
|
|
254
|
+
* The exact revision every future occurrence resolves against, captured at
|
|
255
|
+
* the moment this schedule was created or last switched to (or re-pinned
|
|
256
|
+
* under) `revisionPolicy: 'pinned'`. Present only when `revisionPolicy ===
|
|
257
|
+
* 'pinned'`.
|
|
258
|
+
*/
|
|
259
|
+
pinnedRevision?: string;
|
|
209
260
|
createdAt: number;
|
|
210
261
|
updatedAt: number;
|
|
211
262
|
/** Most recent occurrence that started a scheduled workflow. */
|
|
@@ -53,6 +53,20 @@ export interface WorkflowState {
|
|
|
53
53
|
* detect version drift.
|
|
54
54
|
*/
|
|
55
55
|
versionTuple: WorkflowVersionTuple;
|
|
56
|
+
/**
|
|
57
|
+
* The exact executable artifact this run started against — the revision
|
|
58
|
+
* of the code actually loaded in this process at admission time (an
|
|
59
|
+
* eager registration's `registeredCatalogRevisions` entry, or a dynamic
|
|
60
|
+
* source's resolved candidate revision), NOT the catalog's cached active
|
|
61
|
+
* pointer. Sibling to {@link versionTuple}: `revision` answers "which
|
|
62
|
+
* artifact" for identity, diagnostics, and pinning; `versionTuple` remains
|
|
63
|
+
* the sole semantic-compatibility axis recovery checks. Every fresh start
|
|
64
|
+
* from this release forward sets it; `undefined` only on a record
|
|
65
|
+
* persisted before this field existed (a pre-upgrade run) — recovery
|
|
66
|
+
* treats that absence as a bounded, explicitly-classified legacy case
|
|
67
|
+
* rather than falling back to the currently active revision.
|
|
68
|
+
*/
|
|
69
|
+
revision?: string;
|
|
56
70
|
/**
|
|
57
71
|
* Durable token identifying this concrete workflow run. It changes when a
|
|
58
72
|
* stable workflow id is reused with `onTerminalConflict: 'start-new'`, and
|
|
@@ -291,6 +305,8 @@ export interface WorkflowSummary {
|
|
|
291
305
|
status: WorkflowStatus;
|
|
292
306
|
tags?: string[];
|
|
293
307
|
version: string;
|
|
308
|
+
/** The exact executable artifact this run started against. See {@link WorkflowState.revision}. */
|
|
309
|
+
revision?: string;
|
|
294
310
|
createdAt: number;
|
|
295
311
|
updatedAt: number;
|
|
296
312
|
/** Execution deadline (ms epoch) if set on the workflow. */
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* }
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
|
-
export type WeftErrorCode = 'WorkflowAlreadyExistsError' | 'BulkDeleteRequiresTerminalWorkflowsError' | 'BulkOperationConfirmationError' | 'WorkflowTypeNotRegisteredForRecoveryError' | 'EngineCreateNameMismatchError' | 'EngineDisposedError' | 'EngineDisposalError' | 'WorkflowNotFoundError' | 'WorkflowNotRegisteredError' | 'WorkflowConcurrencyLimitExceededError' | 'WorkflowSuspendNotSupportedError' | 'ActivityResolutionError' | 'BranchTopologyChangedError' | 'PersistedDataIncompatibleError' | 'PersistedDataCorruptError' | 'WorkflowTimeoutError' | 'HttpClientError' | 'WorkerProtocolIncompatibleError' | 'UpdateTimeoutError' | 'UpdateValidationError' | 'WorkflowTerminalError' | 'WorkflowBuilderError' | 'VersionMismatchError' | 'EffectReplayConflictError' | 'ReviewTimeoutError' | 'AtomicStateConflictError' | 'StandardSchemaValidationError' | 'ActivityReconciliationCapabilityError' | 'ActivityReconciliationConflictError' | 'ActivityReconciliationIndeterminateError' | 'DurableActivityScopeError' | 'DurableActivityUnsupportedError' | 'AsyncActivityTokenNotFoundError' | 'ActivityScheduleToCloseTimeoutError' | 'ActivityPerAttemptTimeoutError' | 'PayloadSizeExceededError' | 'StartOrSignalConflictError' | 'WorkflowTeardownPendingError' | 'IdempotencyKeyPurgedError' | 'WorkerManifestBuildError' | 'OwnershipModeMismatchError' | 'ApplicationCommandValidationError' | 'MailboxContentionError' | 'ApplicationDeliveryValidationError' | 'OutboxContentionError' | 'WaitBudgetElapsedError' | 'WorkflowCatalogConflictError' | 'WorkflowRevisionNotInstalledError' | 'WorkflowSourceValidationError';
|
|
28
|
+
export type WeftErrorCode = 'WorkflowAlreadyExistsError' | 'BulkDeleteRequiresTerminalWorkflowsError' | 'BulkOperationConfirmationError' | 'WorkflowTypeNotRegisteredForRecoveryError' | 'EngineCreateNameMismatchError' | 'EngineDisposedError' | 'EngineDisposalError' | 'WorkflowNotFoundError' | 'WorkflowNotRegisteredError' | 'WorkflowConcurrencyLimitExceededError' | 'WorkflowSuspendNotSupportedError' | 'ActivityResolutionError' | 'BranchTopologyChangedError' | 'PersistedDataIncompatibleError' | 'PersistedDataCorruptError' | 'WorkflowTimeoutError' | 'HttpClientError' | 'WorkerProtocolIncompatibleError' | 'UpdateTimeoutError' | 'UpdateValidationError' | 'WorkflowTerminalError' | 'WorkflowBuilderError' | 'VersionMismatchError' | 'EffectReplayConflictError' | 'ReviewTimeoutError' | 'AtomicStateConflictError' | 'StandardSchemaValidationError' | 'ActivityReconciliationCapabilityError' | 'ActivityReconciliationConflictError' | 'ActivityReconciliationIndeterminateError' | 'DurableActivityScopeError' | 'DurableActivityUnsupportedError' | 'AsyncActivityTokenNotFoundError' | 'ActivityScheduleToCloseTimeoutError' | 'ActivityPerAttemptTimeoutError' | 'PayloadSizeExceededError' | 'StartOrSignalConflictError' | 'WorkflowTeardownPendingError' | 'IdempotencyKeyPurgedError' | 'WorkerManifestBuildError' | 'OwnershipModeMismatchError' | 'ApplicationCommandValidationError' | 'MailboxContentionError' | 'ApplicationDeliveryValidationError' | 'OutboxContentionError' | 'WaitBudgetElapsedError' | 'WorkflowCatalogConflictError' | 'WorkflowRevisionNotInstalledError' | 'WorkflowSourceValidationError' | 'WorkflowSourceNotRegisteredError' | 'DynamicWorkflowSourceUnavailableError' | 'WorkflowRevisionUnavailableError';
|
|
29
29
|
/**
|
|
30
30
|
* Generic abstract base for all Weft library errors. The `TCode` parameter
|
|
31
31
|
* makes each subclass's `code` its own literal type; the exported base surface
|
package/dist/core/weft-error.js
CHANGED
|
@@ -55,7 +55,10 @@ const publicWeftErrorCodeMap = {
|
|
|
55
55
|
WaitBudgetElapsedError: !0,
|
|
56
56
|
WorkflowCatalogConflictError: !0,
|
|
57
57
|
WorkflowRevisionNotInstalledError: !0,
|
|
58
|
-
WorkflowSourceValidationError: !0
|
|
58
|
+
WorkflowSourceValidationError: !0,
|
|
59
|
+
WorkflowSourceNotRegisteredError: !0,
|
|
60
|
+
DynamicWorkflowSourceUnavailableError: !0,
|
|
61
|
+
WorkflowRevisionUnavailableError: !0
|
|
59
62
|
}, PUBLIC_WEFT_ERROR_CODES = new Set(Object.keys(publicWeftErrorCodeMap));
|
|
60
63
|
export function isWeftError(value) {
|
|
61
64
|
return value instanceof WeftError;
|
|
@@ -45,6 +45,7 @@ export class WorkerExecutionStrategy {
|
|
|
45
45
|
#messageHandler;
|
|
46
46
|
#disposed;
|
|
47
47
|
#nextTurnId;
|
|
48
|
+
#workflowRevisions;
|
|
48
49
|
constructor(pool, options) {
|
|
49
50
|
const {
|
|
50
51
|
workflowTurnTimeoutMs,
|
|
@@ -58,6 +59,7 @@ export class WorkerExecutionStrategy {
|
|
|
58
59
|
} = options ?? {};
|
|
59
60
|
this.#pool = pool;
|
|
60
61
|
this.#ownership = new WorkerExecutionOwnership;
|
|
62
|
+
this.#workflowRevisions = new Map;
|
|
61
63
|
this.#workerListeners = new WorkerListenerRegistry;
|
|
62
64
|
this.#checkpointResumeState = new WorkerCheckpointResumeState;
|
|
63
65
|
this.#workflowTurnTimeoutMs = workflowTurnTimeoutMs;
|
|
@@ -81,6 +83,9 @@ export class WorkerExecutionStrategy {
|
|
|
81
83
|
pool: this.#pool,
|
|
82
84
|
emit: (message) => {
|
|
83
85
|
this.#emit(message);
|
|
86
|
+
},
|
|
87
|
+
forgetWorkflowRevision: (workflowId) => {
|
|
88
|
+
this.#workflowRevisions.delete(workflowId);
|
|
84
89
|
}
|
|
85
90
|
});
|
|
86
91
|
this.#dispatcher = new WorkerExecutionDispatcher({
|
|
@@ -97,7 +102,7 @@ export class WorkerExecutionStrategy {
|
|
|
97
102
|
},
|
|
98
103
|
ensureRealmReady: (worker, workflowId) => this.#ensureRealmReady(worker, workflowId),
|
|
99
104
|
beginTurn: (worker, workflowId, turnId, kind) => {
|
|
100
|
-
this.#turnWatchdog.begin(worker, workflowId, turnId, kind);
|
|
105
|
+
this.#turnWatchdog.begin(worker, workflowId, turnId, kind, void 0, this.#workflowRevisions.get(workflowId));
|
|
101
106
|
},
|
|
102
107
|
clearTurn: (worker) => {
|
|
103
108
|
this.#turnWatchdog.clear(worker);
|
|
@@ -132,6 +137,10 @@ export class WorkerExecutionStrategy {
|
|
|
132
137
|
startWorkflow(parameters) {
|
|
133
138
|
this.#ownership.resetWorkflow(parameters.workflowId);
|
|
134
139
|
this.#checkpointResumeState.resetWorkflow(parameters.workflowId);
|
|
140
|
+
if (parameters.revision !== void 0)
|
|
141
|
+
this.#workflowRevisions.set(parameters.workflowId, parameters.revision);
|
|
142
|
+
else
|
|
143
|
+
this.#workflowRevisions.delete(parameters.workflowId);
|
|
135
144
|
const message = buildRunMessage(parameters, this.#inboundMessageContext());
|
|
136
145
|
if (!this.#faultHandler.assertHostToWorkerMessageWithinLimit(parameters.workflowId, message))
|
|
137
146
|
return;
|
|
@@ -185,6 +194,7 @@ export class WorkerExecutionStrategy {
|
|
|
185
194
|
};
|
|
186
195
|
}
|
|
187
196
|
cancelWorkflow(workflowId) {
|
|
197
|
+
this.#workflowRevisions.delete(workflowId);
|
|
188
198
|
const worker = this.#ownership.getActiveWorker(workflowId);
|
|
189
199
|
if (worker) {
|
|
190
200
|
this.#ownership.markCancelled(workflowId);
|
|
@@ -246,6 +256,7 @@ export class WorkerExecutionStrategy {
|
|
|
246
256
|
this.#workerListeners.detachAll();
|
|
247
257
|
this.#ownership.clear();
|
|
248
258
|
this.#checkpointResumeState.clear();
|
|
259
|
+
this.#workflowRevisions.clear();
|
|
249
260
|
this.#messageHandler = null;
|
|
250
261
|
}
|
|
251
262
|
async#handleWorkerMessage(worker, message) {
|
|
@@ -279,6 +290,7 @@ export class WorkerExecutionStrategy {
|
|
|
279
290
|
return !1;
|
|
280
291
|
this.#ownership.consumeCancelled(message.workflowId);
|
|
281
292
|
this.#ownership.deleteParked(message.workflowId);
|
|
293
|
+
this.#workflowRevisions.delete(message.workflowId);
|
|
282
294
|
this.#releaseActiveWorker(message.workflowId);
|
|
283
295
|
this.#detachWorkerListenersIfIdle(worker);
|
|
284
296
|
this.#checkpointResumeState.forgetWorkflowIfClosed(message.workflowId, this.#ownership.isWorkflowClosed(message.workflowId));
|
|
@@ -17,6 +17,16 @@ export interface WorkerFaultHandlerDependencies {
|
|
|
17
17
|
protocolGuard: WorkerProtocolGuard;
|
|
18
18
|
pool: WorkerPool;
|
|
19
19
|
emit: (message: WorkerOutboundMessage) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Forget a workflow's captured revision (WFT-20), mirroring
|
|
22
|
+
* `#settleTerminalWorkerMessage`'s real-terminal-message cleanup. Every
|
|
23
|
+
* fault path here (protocol violation, turn timeout, worker crash, log
|
|
24
|
+
* abuse, realm-ready failure) synthesizes a `failed` message directly
|
|
25
|
+
* rather than routing through that settle path, so without this callback
|
|
26
|
+
* `WorkerExecutionStrategy#workflowRevisions` would keep an entry per
|
|
27
|
+
* faulted workflow ID for the life of the strategy.
|
|
28
|
+
*/
|
|
29
|
+
forgetWorkflowRevision: (workflowId: string) => void;
|
|
20
30
|
}
|
|
21
31
|
export interface WorkerDiscardOptions {
|
|
22
32
|
targetWorkflowId?: string;
|
|
@@ -71,6 +71,7 @@ export class WorkerFaultHandler {
|
|
|
71
71
|
const { ownership, checkpointResumeState } = this.#dependencies;
|
|
72
72
|
ownership.forgetWorkflow(workflowId);
|
|
73
73
|
checkpointResumeState.forgetWorkflowIfClosed(workflowId, !0);
|
|
74
|
+
this.#dependencies.forgetWorkflowRevision(workflowId);
|
|
74
75
|
if (isTarget && options.skipTarget)
|
|
75
76
|
return;
|
|
76
77
|
this.#dependencies.emit({
|
|
@@ -8,6 +8,7 @@ export function buildRunMessage(parameters, context) {
|
|
|
8
8
|
...parameters.workflowExecutionToken !== void 0 && {
|
|
9
9
|
workflowExecutionToken: parameters.workflowExecutionToken
|
|
10
10
|
},
|
|
11
|
+
...parameters.revision !== void 0 && { workflowRevision: parameters.revision },
|
|
11
12
|
workflowType: parameters.workflowType,
|
|
12
13
|
checkpoint: parameters.checkpoint,
|
|
13
14
|
input: parameters.input,
|
|
@@ -51,6 +51,10 @@ export class WorkerProtocolGuard {
|
|
|
51
51
|
throw new WorkerProtocolError("Worker message arrived outside an active turn");
|
|
52
52
|
if ((message.type === "log" ? void 0 : message.turnId) !== turn.turnId || message.workflowId !== turn.workflowId)
|
|
53
53
|
throw new WorkerProtocolError("Worker message did not match the active turn");
|
|
54
|
+
if (turn.revision !== void 0) {
|
|
55
|
+
if ((message.type === "log" ? void 0 : message.workflowRevision) !== turn.revision)
|
|
56
|
+
throw new WorkerProtocolError("Worker message revision did not match the active turn");
|
|
57
|
+
}
|
|
54
58
|
}
|
|
55
59
|
}
|
|
56
60
|
function protocolFailure(error, options) {
|
|
@@ -59,6 +59,13 @@ export declare function createBoundedWorkerFailureMessage(parameters: {
|
|
|
59
59
|
error: string;
|
|
60
60
|
failureCategory: FailureCategory;
|
|
61
61
|
turnId?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Echo of the active turn's captured revision (WFT-20). Required whenever the
|
|
64
|
+
* caller has one, so a bounded fallback for an oversized checkpoint/terminal
|
|
65
|
+
* message still passes the host's strict revision check instead of being
|
|
66
|
+
* discarded as a protocol violation.
|
|
67
|
+
*/
|
|
68
|
+
workflowRevision?: string;
|
|
62
69
|
}): WorkerOutboundMessage;
|
|
63
70
|
export declare function estimateWorkerProtocolMessageBytes(message: unknown): number;
|
|
64
71
|
export declare function assertWorkerProtocolMessageWithinLimit(message: unknown, maxBytes: number | undefined): number;
|
|
@@ -65,7 +65,8 @@ export function createBoundedWorkerFailureMessage(parameters) {
|
|
|
65
65
|
...parameters.turnId === void 0 ? {} : { turnId: parameters.turnId },
|
|
66
66
|
workflowId: parameters.workflowId,
|
|
67
67
|
error: truncateForProtocol(parameters.error),
|
|
68
|
-
failureCategory: parameters.failureCategory
|
|
68
|
+
failureCategory: parameters.failureCategory,
|
|
69
|
+
...parameters.workflowRevision === void 0 ? {} : { workflowRevision: parameters.workflowRevision }
|
|
69
70
|
};
|
|
70
71
|
}
|
|
71
72
|
export function estimateWorkerProtocolMessageBytes(message) {
|
|
@@ -4,12 +4,19 @@ export interface WorkerTurnState {
|
|
|
4
4
|
turnId: number;
|
|
5
5
|
kind: 'run' | 'resume';
|
|
6
6
|
timeoutMs: number | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* The workflow's captured revision (WFT-20), when known — carried forward
|
|
9
|
+
* on every turn for this workflow (including `resume`, which never
|
|
10
|
+
* re-supplies it), for `WorkerProtocolGuard` to validate the outbound
|
|
11
|
+
* echo against.
|
|
12
|
+
*/
|
|
13
|
+
revision?: string;
|
|
7
14
|
}
|
|
8
15
|
export type WorkerTurnTimeoutResolverForTesting = (turn: Pick<WorkerTurnState, 'workflowId' | 'kind'>) => number;
|
|
9
16
|
export declare class WorkerTurnWatchdog {
|
|
10
17
|
#private;
|
|
11
18
|
constructor(timeoutMs: number | undefined, onTimeout: (turn: WorkerTurnState) => void);
|
|
12
|
-
begin(worker: Worker, workflowId: string, turnId: number, kind: WorkerTurnState['kind'], timeoutMs?: number | undefined): void;
|
|
19
|
+
begin(worker: Worker, workflowId: string, turnId: number, kind: WorkerTurnState['kind'], timeoutMs?: number | undefined, revision?: string): void;
|
|
13
20
|
setTimeoutResolverForTesting(resolver: WorkerTurnTimeoutResolverForTesting): void;
|
|
14
21
|
clear(worker: Worker): void;
|
|
15
22
|
clearAll(): void;
|
|
@@ -7,7 +7,7 @@ export class WorkerTurnWatchdog {
|
|
|
7
7
|
this.#timeoutMs = timeoutMs;
|
|
8
8
|
this.#onTimeout = onTimeout;
|
|
9
9
|
}
|
|
10
|
-
begin(worker, workflowId, turnId, kind, timeoutMs = this.#timeoutMs) {
|
|
10
|
+
begin(worker, workflowId, turnId, kind, timeoutMs = this.#timeoutMs, revision) {
|
|
11
11
|
this.clear(worker);
|
|
12
12
|
const resolvedTimeoutMs = this.#timeoutResolverForTesting?.({ workflowId, kind }) ?? timeoutMs, turn = {
|
|
13
13
|
worker,
|
|
@@ -15,7 +15,8 @@ export class WorkerTurnWatchdog {
|
|
|
15
15
|
turnId,
|
|
16
16
|
kind,
|
|
17
17
|
timeoutMs: resolvedTimeoutMs,
|
|
18
|
-
timeout: null
|
|
18
|
+
timeout: null,
|
|
19
|
+
...revision !== void 0 && { revision }
|
|
19
20
|
};
|
|
20
21
|
if (resolvedTimeoutMs !== void 0)
|
|
21
22
|
turn.timeout = setTimeout(() => {
|
|
@@ -95,6 +95,11 @@ export function formatVersionCheckReport(report) {
|
|
|
95
95
|
lines.push(`${typeReport.type} (${typeReport.storedVersion} \u2192 ${typeReport.registeredVersion}):`);
|
|
96
96
|
lines.push(` ${typeReport.runningCount} running workflows`);
|
|
97
97
|
lines.push(` Compatibility: ${typeReport.compatibility}`);
|
|
98
|
+
const revisionEntries = Object.entries(typeReport.revisionCounts);
|
|
99
|
+
if (revisionEntries.length > 0 || typeReport.unpinnedRunningCount > 0) {
|
|
100
|
+
const revisionSummary = revisionEntries.map(([revision, count]) => `${revision} (${count})`).concat(typeReport.unpinnedRunningCount > 0 ? [`unpinned (${typeReport.unpinnedRunningCount})`] : []).join(", ");
|
|
101
|
+
lines.push(` Revisions: ${revisionSummary}`);
|
|
102
|
+
}
|
|
98
103
|
lines.push("");
|
|
99
104
|
}
|
|
100
105
|
switch (report.overallVerdict) {
|
|
@@ -148,6 +148,30 @@ export interface WorkflowTypeReport {
|
|
|
148
148
|
registeredVersion: string;
|
|
149
149
|
runningCount: number;
|
|
150
150
|
compatibility: VersionCompatibility;
|
|
151
|
+
/**
|
|
152
|
+
* Count of active (running/pending) workflows for this type that carry a
|
|
153
|
+
* persisted `WorkflowState.revision`, broken down by that EXACT revision
|
|
154
|
+
* — the exact executable artifact each run started against, distinct
|
|
155
|
+
* from `storedVersion` (the semantic `versionTuple.workflowVersion`,
|
|
156
|
+
* which many revisions can share, e.g. a documentation-only redeploy).
|
|
157
|
+
*
|
|
158
|
+
* Deliberately holds only real, persisted revision values — a dynamic
|
|
159
|
+
* source's `revision` is any non-empty, bounded string with no reserved
|
|
160
|
+
* values, so folding "no persisted revision" into this same map under a
|
|
161
|
+
* sentinel key could collide with a genuinely pinned run that happens to
|
|
162
|
+
* use that literal string. A run with no persisted revision (a
|
|
163
|
+
* pre-revision-pinning record) is counted separately, in
|
|
164
|
+
* {@link WorkflowTypeReport.unpinnedRunningCount}.
|
|
165
|
+
*/
|
|
166
|
+
revisionCounts: Record<string, number>;
|
|
167
|
+
/**
|
|
168
|
+
* Count of active (running/pending) workflows for this type with no
|
|
169
|
+
* persisted `WorkflowState.revision` — a record written before
|
|
170
|
+
* per-run revision pinning existed (WFT-17). Kept out of
|
|
171
|
+
* {@link WorkflowTypeReport.revisionCounts} so it can never collide with
|
|
172
|
+
* a genuinely pinned revision that happens to share the same string.
|
|
173
|
+
*/
|
|
174
|
+
unpinnedRunningCount: number;
|
|
151
175
|
}
|
|
152
176
|
/**
|
|
153
177
|
* Deployment-safety report produced by `weft version:check`.
|