@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
|
@@ -30,6 +30,7 @@ export { ActivityReconciliationCapabilityError, ActivityReconciliationConflictEr
|
|
|
30
30
|
export { AsyncActivityTokenNotFoundError } from './async-activity-completion.ts';
|
|
31
31
|
export type { PendingAsyncActivity } from './async-activity-records.ts';
|
|
32
32
|
export { getWorkflowRevisionDiagnostics, removeWorkflowRevision, type WorkflowCatalogRemovalResult, type WorkflowRevisionDiagnostics, } from './catalog-removal.ts';
|
|
33
|
+
export { DynamicWorkflowSourceUnavailableError, WorkflowSourceNotRegisteredError, } from './dynamic-source-errors.ts';
|
|
33
34
|
export type { PendingTimelineEntry, RegistrationEntry, ResolvedOptions, TrackedWaiterKeys, WorkflowResultWaiter, } from './engine-internal-types.ts';
|
|
34
35
|
export { ActivityResolutionError, BulkDeleteRequiresTerminalWorkflowsError, BulkOperationConfirmationError, EngineCreateNameMismatchError, EngineDisposalError, EngineDisposedError, IdempotencyKeyPurgedError, PersistedDataCorruptError, PersistedDataIncompatibleError, StartOrSignalConflictError, WorkflowAlreadyExistsError, WorkflowConcurrencyLimitExceededError, WorkflowNotFoundError, WorkflowNotRegisteredError, WorkflowSuspendNotSupportedError, WorkflowTeardownPendingError, WorkflowTypeNotRegisteredForRecoveryError, } from './errors.ts';
|
|
35
36
|
export { HANDLE_RESULT_PROMISE, WorkflowHandle } from './handles.ts';
|
|
@@ -39,6 +40,7 @@ export type { WorkflowWakeKind } from './lease-deposition.ts';
|
|
|
39
40
|
export { EngineLeaseAcquisitionTimeoutError, EngineLeaseCorruptedError, EngineLeaseNotHeldError, OwnershipModeMismatchError, WorkflowClaimUnavailableError, } from './lease-errors.ts';
|
|
40
41
|
export type { EngineLeaseHealth, LeaseLostReason } from './lease-health.ts';
|
|
41
42
|
export type { RecoverAllOptions, RecoveredWorkflowInfo } from './lifecycle.ts';
|
|
43
|
+
export { WorkflowRevisionUnavailableError } from './revision-errors.ts';
|
|
42
44
|
export { ScheduleHandle } from './schedule-handle.ts';
|
|
43
45
|
export type { ResolveWorkflowSourceOptions } from './source-resolution.ts';
|
|
44
46
|
export type { WorkflowFeedListener, WorkflowFeedRecord, WorkflowFeedSelector, } from './workflow-feed.ts';
|
|
@@ -299,11 +301,15 @@ export declare class Engine<TWorkflows extends object = DefaultWorkflowRegistry,
|
|
|
299
301
|
getActivityDefinition(name: string): ActivityMetadata | undefined;
|
|
300
302
|
listActivityDefinitions(): ActivityMetadata[];
|
|
301
303
|
/**
|
|
302
|
-
* Resolve one activity's catalog metadata the
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
304
|
+
* Resolve one activity's catalog metadata against the EAGER per-workflow
|
|
305
|
+
* `.activities({...})` registry only, falling back to the global registry
|
|
306
|
+
* — never a `registerSource()`-registered type's dynamic-source revision,
|
|
307
|
+
* resolved or not (WFT-19: `internals.activityRegistriesByWorkflow` is
|
|
308
|
+
* eager-only as of this batch; a dynamic-source workflow's own activity
|
|
309
|
+
* dispatch resolves via the running instance's exact `(type, revision)`
|
|
310
|
+
* pin instead, in `activity-resolution.ts`, which this synchronous,
|
|
311
|
+
* type-only accessor has no instance to pin against). Build tooling
|
|
312
|
+
* (`buildWorkerManifestFromRegistry`) uses this so a workflow-scoped
|
|
307
313
|
* activity's schema, not a same-named global activity's, feeds its
|
|
308
314
|
* `contractHash` when both are registered.
|
|
309
315
|
*/
|
|
@@ -318,11 +324,13 @@ export declare class Engine<TWorkflows extends object = DefaultWorkflowRegistry,
|
|
|
318
324
|
*
|
|
319
325
|
* Companion to {@link getWorkflowActivityDefinition}: that resolves one
|
|
320
326
|
* activity by name with the per-workflow-first, global-fallback dispatch
|
|
321
|
-
* order; this enumerates only the names the per-workflow registry
|
|
322
|
-
* declares — activities a workflow reaches solely through the
|
|
323
|
-
* registry are deliberately excluded,
|
|
324
|
-
*
|
|
325
|
-
*
|
|
327
|
+
* order; this enumerates only the names the EAGER per-workflow registry
|
|
328
|
+
* itself declares — activities a workflow reaches solely through the
|
|
329
|
+
* global registry are deliberately excluded, and so (as of WFT-19) is
|
|
330
|
+
* every `registerSource()`-registered type regardless of whether a
|
|
331
|
+
* revision has been resolved (see {@link getWorkflowActivityDefinition}'s
|
|
332
|
+
* doc). Build tooling that needs a workflow's full scoped-activity
|
|
333
|
+
* contract set (`buildRegistrySnapshot`,
|
|
326
334
|
* `worker/manifest/registry-contract-builder.ts`) uses this instead of
|
|
327
335
|
* reaching into engine internals directly.
|
|
328
336
|
*/
|
|
@@ -66,6 +66,10 @@ import {
|
|
|
66
66
|
typedEngineView
|
|
67
67
|
} from "./construction.js";
|
|
68
68
|
import { disposeEngine } from "./disposal.js";
|
|
69
|
+
import {
|
|
70
|
+
canResolveRevisionLocally,
|
|
71
|
+
getResolvedDynamicRegistration
|
|
72
|
+
} from "./dynamic-source-execution.js";
|
|
69
73
|
import {
|
|
70
74
|
consumeNextEngineLeakWarningTokenForTesting,
|
|
71
75
|
engineCleanupIntervalFinalizer
|
|
@@ -175,6 +179,7 @@ import { registerSource as registerWorkflowSource } from "./source-registration.
|
|
|
175
179
|
import {
|
|
176
180
|
resolveWorkflowSource as resolveWorkflowSourceImpl
|
|
177
181
|
} from "./source-resolution.js";
|
|
182
|
+
import { createWorkflowSourceRuntimeState } from "./source-runtime-state.js";
|
|
178
183
|
import {
|
|
179
184
|
loadScheduleState,
|
|
180
185
|
loadWorkflowState,
|
|
@@ -216,6 +221,10 @@ export {
|
|
|
216
221
|
getWorkflowRevisionDiagnostics,
|
|
217
222
|
removeWorkflowRevision
|
|
218
223
|
} from "./catalog-removal.js";
|
|
224
|
+
export {
|
|
225
|
+
DynamicWorkflowSourceUnavailableError,
|
|
226
|
+
WorkflowSourceNotRegisteredError
|
|
227
|
+
} from "./dynamic-source-errors.js";
|
|
219
228
|
export {
|
|
220
229
|
ActivityResolutionError,
|
|
221
230
|
BulkDeleteRequiresTerminalWorkflowsError,
|
|
@@ -250,6 +259,7 @@ export {
|
|
|
250
259
|
OwnershipModeMismatchError,
|
|
251
260
|
WorkflowClaimUnavailableError
|
|
252
261
|
} from "./lease-errors.js";
|
|
262
|
+
export { WorkflowRevisionUnavailableError } from "./revision-errors.js";
|
|
253
263
|
export { ScheduleHandle } from "./schedule-handle.js";
|
|
254
264
|
export {
|
|
255
265
|
clearEngineLeakWarningTokenForTesting,
|
|
@@ -270,7 +280,8 @@ function scheduleDefinitionFromInternals(internals, definition) {
|
|
|
270
280
|
...definition.description !== void 0 && { description: definition.description },
|
|
271
281
|
...definition.overlapPolicy !== void 0 && { overlap: definition.overlapPolicy },
|
|
272
282
|
...definition.backfill !== void 0 && { backfill: definition.backfill },
|
|
273
|
-
...definition.jitter !== void 0 && { jitter: definition.jitter }
|
|
283
|
+
...definition.jitter !== void 0 && { jitter: definition.jitter },
|
|
284
|
+
...definition.revisionPolicy !== void 0 && { revisionPolicy: definition.revisionPolicy }
|
|
274
285
|
});
|
|
275
286
|
}
|
|
276
287
|
|
|
@@ -318,8 +329,11 @@ export class Engine extends EventTarget {
|
|
|
318
329
|
maxNestingDepth: resolvedOptions.maxNestingDepth,
|
|
319
330
|
development: resolvedOptions.development,
|
|
320
331
|
broadcastEvents: resolvedOptions.broadcastEvents,
|
|
321
|
-
getRegistration: getInternals(this)
|
|
322
|
-
listRegisteredWorkflowTypes: () =>
|
|
332
|
+
getRegistration: (workflowType, workflowId) => getResolvedDynamicRegistration(getInternals(this), workflowType, getInternals(this).workflowTypeByWorkflowId.get(workflowId)?.revision),
|
|
333
|
+
listRegisteredWorkflowTypes: () => new Set([
|
|
334
|
+
...getInternals(this).registrations.keys(),
|
|
335
|
+
...getInternals(this).sources.lastResolvedRevisionByName.keys()
|
|
336
|
+
]).values(),
|
|
323
337
|
getComposedWorkflowInterceptor: () => getComposedWorkflowInterceptor(getInternals(this)),
|
|
324
338
|
resolveWorkflowType: this.#resolveWorkflowTypeTarget.bind(this),
|
|
325
339
|
registerCancelHandler: (workflowId, handler) => registerCancelHandler(getInternals(this), workflowId, handler),
|
|
@@ -445,10 +459,7 @@ export class Engine extends EventTarget {
|
|
|
445
459
|
getInternals(this).catalogDrainPromise = null;
|
|
446
460
|
getInternals(this).registeredCatalogRevisions = new Map;
|
|
447
461
|
getInternals(this).inFlightStartsByRevision = new Map;
|
|
448
|
-
getInternals(this).
|
|
449
|
-
getInternals(this).sourceResolutionsInFlight = new Map;
|
|
450
|
-
getInternals(this).sourceResolutionWaiterControllers = new Set;
|
|
451
|
-
getInternals(this).resolvedWorkflowSources = new Map;
|
|
462
|
+
getInternals(this).sources = createWorkflowSourceRuntimeState();
|
|
452
463
|
this.#ensureRetentionSweepInterval();
|
|
453
464
|
this.#startSecondInstanceDetection();
|
|
454
465
|
}
|
|
@@ -539,7 +550,7 @@ export class Engine extends EventTarget {
|
|
|
539
550
|
getNow: internals.options.getNow,
|
|
540
551
|
claimTtlMs: internals.options.workflowClaimTtlMs,
|
|
541
552
|
claimRenewIntervalMs: internals.options.workflowClaimRenewIntervalMs,
|
|
542
|
-
isWorkflowTypeRegistered: (workflowType) => internals
|
|
553
|
+
isWorkflowTypeRegistered: (workflowType, revision) => canResolveRevisionLocally(internals, workflowType, revision),
|
|
543
554
|
onWorkflowClaimReclaimed: async (workflowId) => {
|
|
544
555
|
internals.inlineStrategy?.evictContextForReclaim(workflowId);
|
|
545
556
|
internals.deliveredPendingUpdateIds.delete(workflowId);
|
|
@@ -1143,7 +1154,7 @@ export class Engine extends EventTarget {
|
|
|
1143
1154
|
if (leaseManager !== null && leaseManager.currentEpochBytes() !== null)
|
|
1144
1155
|
process.emitWarning("engine ownership lease disposed synchronously; lease release is fire-and-forget, so exiting before the release completes can make the next instance wait for leaseTtl. Use await engine.shutdown(), await using, or await engine[Symbol.asyncDispose]() for prompt rolling-deploy handoff.", ENGINE_LEASE_SYNCHRONOUS_DISPOSE_WARNING_NAME);
|
|
1145
1156
|
const { registry: workflowClaimRegistry } = this.#detachWorkflowClaimOwnership();
|
|
1146
|
-
disposeEngine(getInternals(this));
|
|
1157
|
+
disposeEngine(getInternals(this), (event) => this.dispatchEvent(event));
|
|
1147
1158
|
if (this.#synchronousDisposeResult === null)
|
|
1148
1159
|
this.#synchronousDisposeResult = leaseManager?.release() ?? Promise.resolve(!0);
|
|
1149
1160
|
this.#synchronousDisposeResult;
|
|
@@ -1161,7 +1172,7 @@ export class Engine extends EventTarget {
|
|
|
1161
1172
|
drainFailure = { error };
|
|
1162
1173
|
} finally {
|
|
1163
1174
|
const { registry: workflowClaimRegistry } = this.#detachWorkflowClaimOwnership();
|
|
1164
|
-
disposeEngine(getInternals(this));
|
|
1175
|
+
disposeEngine(getInternals(this), (event) => this.dispatchEvent(event));
|
|
1165
1176
|
await getInternals(this).inFlightLeaseAcquire?.catch(() => {});
|
|
1166
1177
|
leaseReleased = await (this.#synchronousDisposeResult ?? leaseManager?.release() ?? Promise.resolve(!0));
|
|
1167
1178
|
await this.#releaseWorkflowClaimsBestEffort(workflowClaimRegistry);
|
|
@@ -99,7 +99,7 @@ async function startQueuedInlineWorkflowExecution(internals, start, callbacks, o
|
|
|
99
99
|
return;
|
|
100
100
|
internals.queuedInlineWorkflowStartIds.delete(start.workflowId);
|
|
101
101
|
internals.engine.dispatchEvent(new WorkflowStartedEvent(start.workflowId, start.workflowType, start.input));
|
|
102
|
-
startWorkflowExecution(internals, start.workflowId, state.workflowExecutionToken, start.workflowType, start.input, start.checkpoint, start.nestingDepth, start.executionDeadline, start.executionStateOwnerId);
|
|
102
|
+
startWorkflowExecution(internals, start.workflowId, state.workflowExecutionToken, start.workflowType, state.revision, start.input, start.checkpoint, start.nestingDepth, start.executionDeadline, start.executionStateOwnerId);
|
|
103
103
|
if (options?.abortStartedWorkflows === !0)
|
|
104
104
|
internals.inlineStrategy?.abortWorkflowAdvanceForShutdown(start.workflowId);
|
|
105
105
|
else
|
|
@@ -29,18 +29,19 @@ import type { InlineExecutionStrategy } from '../inline-execution-strategy.ts';
|
|
|
29
29
|
import type { ComposedActivityInterceptor, ComposedWorkflowInterceptor, Interceptor } from '../interceptor.ts';
|
|
30
30
|
import type { HumanReviewResult, ReviewCoordinator } from '../review/index.ts';
|
|
31
31
|
import type { Scheduler } from '../scheduler.ts';
|
|
32
|
-
import type { Checkpoint, StartWorkflowOptions
|
|
32
|
+
import type { Checkpoint, StartWorkflowOptions } from '../types.ts';
|
|
33
33
|
import type { UpdateCoordinator } from '../updates.ts';
|
|
34
34
|
import type { WorkflowVersionTuple } from '../workflow-version-tuple.ts';
|
|
35
35
|
import type { RecoveredWorkflowInfo } from './lifecycle/shared.ts';
|
|
36
36
|
import type { ActivityHeartbeatKey } from './activity-heartbeat-tracking.ts';
|
|
37
|
-
import type { PendingTimelineEntry, QueuedInlineWorkflowExecutionStart, RegistrationEntry, ResolvedOptions, TrackedWaiterKeys, WorkflowResultWaiter } from './engine-internal-types.ts';
|
|
37
|
+
import type { PendingTimelineEntry, QueuedInlineWorkflowExecutionStart, RegistrationEntry, ResolvedOptions, TrackedWaiterKeys, WorkflowExecutionIdentity, WorkflowResultWaiter } from './engine-internal-types.ts';
|
|
38
38
|
import type { EngineCleanupIntervalDisposalTracker } from './engine-leak-warnings.ts';
|
|
39
39
|
import type { WorkflowHandle, WorkflowHandleEngine } from './handles.ts';
|
|
40
40
|
import type { WorkflowFeedListener } from './index.ts';
|
|
41
41
|
import type { LeaseManager } from './lease-manager.ts';
|
|
42
42
|
import type { ScheduleHandleEngine } from './schedule-handle.ts';
|
|
43
43
|
import type { SecondInstanceDetector } from './second-instance-detector.ts';
|
|
44
|
+
import type { WorkflowSourceRuntimeState } from './source-runtime-state.ts';
|
|
44
45
|
import type { WorkflowClaimMetricsRecorder } from './workflow-claim-metrics.ts';
|
|
45
46
|
import type { WorkflowClaimRegistry } from './workflow-claim-registry.ts';
|
|
46
47
|
import type { WorkflowClaimRenewalTask } from './workflow-claim-renewal-task.ts';
|
|
@@ -58,12 +59,6 @@ export type DurableInlineOperation = {
|
|
|
58
59
|
type EngineRuntime = WorkflowHandleEngine & ScheduleHandleEngine & {
|
|
59
60
|
start(type: string, input: unknown, options?: StartWorkflowOptions): Promise<WorkflowHandle>;
|
|
60
61
|
};
|
|
61
|
-
type SourceHandle = import('../source/index.ts').WorkflowSourceHandle;
|
|
62
|
-
type CatalogRevisionRecord = import('../catalog/index.ts').WorkflowRevisionRecord;
|
|
63
|
-
type ResolvedSource = {
|
|
64
|
-
definition: WorkflowDefinition;
|
|
65
|
-
activityRegistry: ActivityRegistry;
|
|
66
|
-
};
|
|
67
62
|
export interface EngineInternals {
|
|
68
63
|
engine: EngineRuntime;
|
|
69
64
|
storage: WeftStorage;
|
|
@@ -155,15 +150,15 @@ export interface EngineInternals {
|
|
|
155
150
|
/**
|
|
156
151
|
* Per-workflow activity registries built from
|
|
157
152
|
* `workflow({ name }).activities({ ... }).execute(...)`, indexed by workflow
|
|
158
|
-
* type.
|
|
159
|
-
*
|
|
160
|
-
* that registry
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
153
|
+
* type. Lookup consults `activityRegistriesByWorkflow.get(type)` first,
|
|
154
|
+
* falling back to {@link EngineInternals.activityRegistry} for any activity
|
|
155
|
+
* that registry lacks. Each entry is a defensive deep clone+freeze of the
|
|
156
|
+
* workflow's `activities` map. EAGER-ONLY as of WFT-19: only `registration.ts`
|
|
157
|
+
* writes here (never a `registerSource()` load), so a dynamic-source type's
|
|
158
|
+
* per-revision activities resolve instead via
|
|
159
|
+
* `internals.sources.resolved.get(type)?.get(revision)?.activityRegistry`
|
|
160
|
+
* (`activity-resolution.ts`), keyed by the running instance's own pin —
|
|
161
|
+
* never this type-only map, which would clobber across revisions.
|
|
167
162
|
*/
|
|
168
163
|
activityRegistriesByWorkflow: Map<string, ActivityRegistry>;
|
|
169
164
|
/**
|
|
@@ -175,12 +170,13 @@ export interface EngineInternals {
|
|
|
175
170
|
*/
|
|
176
171
|
workflowDefinitionsByName: Map<string, object>;
|
|
177
172
|
/**
|
|
178
|
-
* In-memory cache of `workflowId ->
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
173
|
+
* In-memory cache of `workflowId -> {type, revision}`, populated whenever a
|
|
174
|
+
* workflow begins executing in THIS process (fresh start, delayed-start
|
|
175
|
+
* fire, resume, recovery, workflow-lease reclaim redrive — WFT-19) and
|
|
176
|
+
* cleared on terminal cleanup. Lets the activity-dispatch hot path, and any
|
|
177
|
+
* other lookup needing an instance's own exact pin, resolve synchronously.
|
|
182
178
|
*/
|
|
183
|
-
workflowTypeByWorkflowId: Map<string,
|
|
179
|
+
workflowTypeByWorkflowId: Map<string, WorkflowExecutionIdentity>;
|
|
184
180
|
activityWorkerDispatcher: ActivityWorkerDispatcher | null;
|
|
185
181
|
checkpoints: Map<string, Checkpoint>;
|
|
186
182
|
broadcastChannel: BroadcastChannel | null;
|
|
@@ -409,14 +405,14 @@ export interface EngineInternals {
|
|
|
409
405
|
registeredCatalogRevisions: Map<string, string>;
|
|
410
406
|
/** Name -> revision -> in-flight `startWorkflow` count (WFT-12, `lifecycle/start.ts`). Process-local, never persisted. */
|
|
411
407
|
inFlightStartsByRevision: Map<string, Map<string, number>>;
|
|
412
|
-
/**
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
408
|
+
/**
|
|
409
|
+
* Dynamic workflow sources (WFT-13/14, WFT-15/16): candidates, in-flight
|
|
410
|
+
* single-flight loads, per-caller waiter controllers, locally-resolved
|
|
411
|
+
* definitions, and bounded per-`(name, revision)` diagnostics — see
|
|
412
|
+
* {@link import('./source-runtime-state.ts').WorkflowSourceRuntimeState}
|
|
413
|
+
* for the full field-by-field breakdown. Process-local, never persisted.
|
|
414
|
+
*/
|
|
415
|
+
sources: WorkflowSourceRuntimeState;
|
|
420
416
|
}
|
|
421
417
|
/**
|
|
422
418
|
* Set up an empty `EngineInternals` skeleton in the WeakMap. The Engine
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launch a workflow run from an already-built `WorkflowState`/`Checkpoint`
|
|
3
|
+
* pair — the shared tail `fork()` (`transition.ts`) drives after planting a
|
|
4
|
+
* new run's durable records. Split out of `transition.ts`, which has no
|
|
5
|
+
* headroom under the repository's 500-line implementation-file ceiling for
|
|
6
|
+
* this logic inline.
|
|
7
|
+
*
|
|
8
|
+
* @module core/engine/lifecycle/checkpoint-launch
|
|
9
|
+
*/
|
|
10
|
+
import type { Checkpoint, WorkflowState } from '../../types.ts';
|
|
11
|
+
import { type WorkflowHandle } from '../handles.ts';
|
|
12
|
+
import type { EngineInternals } from '../internals.ts';
|
|
13
|
+
import { type LifecycleCallbacks, type RegistrationEntry } from './shared.ts';
|
|
14
|
+
export declare function launchWorkflowFromCheckpoint(internals: EngineInternals, workflowId: string, state: WorkflowState, checkpoint: Checkpoint, registration: RegistrationEntry,
|
|
15
|
+
/**
|
|
16
|
+
* The EXACT revision `registration` was resolved against — from the same
|
|
17
|
+
* `resolveExecutableRegistrationForRevision()` call that produced
|
|
18
|
+
* `registration`, threaded through explicitly rather than re-read off
|
|
19
|
+
* `state.revision` (WFT-19 review round 5, Codex, mirroring the identical
|
|
20
|
+
* fix in `lifecycle/resume.ts`). `fork()`'s own `forkState.revision` is
|
|
21
|
+
* now stamped with this same resolved value too (WFT-19 review round 6,
|
|
22
|
+
* Codex — `createForkedWorkflowState()`'s matching fix, so a legacy
|
|
23
|
+
* fork's durable pin agrees with this in-memory one), so `state.revision`
|
|
24
|
+
* and `resolvedRevision` coincide for every current caller; kept as an
|
|
25
|
+
* explicit parameter regardless, since this function's identity-cache
|
|
26
|
+
* write must never depend on that invariant holding at a future call
|
|
27
|
+
* site.
|
|
28
|
+
*/
|
|
29
|
+
resolvedRevision: string | undefined, callbacks: LifecycleCallbacks): WorkflowHandle;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { serializeCheckpoint } from "../../checkpoint.js";
|
|
2
|
+
import { Context, setContextWorkflowInterceptor } from "../../context.js";
|
|
3
|
+
import { WorkflowStartedEvent } from "../../events.js";
|
|
4
|
+
import { createCancelHandlerRegistration, resetCancelHandlers } from "../cancel-handlers.js";
|
|
5
|
+
import { getWorkflowExecutionStartedAt } from "../handles.js";
|
|
6
|
+
import { getComposedWorkflowInterceptor } from "../strategy-helpers.js";
|
|
7
|
+
import { createWorkflowVersionTuple } from "./persist.js";
|
|
8
|
+
import { createWorkflowHandle } from "./shared.js";
|
|
9
|
+
function launchInlineWorkflowFromCheckpoint(internals, workflowId, state, checkpoint, registration, callbacks) {
|
|
10
|
+
const inlineStrategy = internals.inlineStrategy;
|
|
11
|
+
if (!inlineStrategy)
|
|
12
|
+
throw Error("Inline workflow launch requested without an inline strategy.");
|
|
13
|
+
const accumulatedResults = new Map(checkpoint.accumulatedResults), workflowAbort = new AbortController;
|
|
14
|
+
resetCancelHandlers(internals, workflowId);
|
|
15
|
+
const context = new Context({
|
|
16
|
+
workflowId,
|
|
17
|
+
...state.workflowExecutionToken !== void 0 && {
|
|
18
|
+
workflowExecutionToken: state.workflowExecutionToken
|
|
19
|
+
},
|
|
20
|
+
workflowType: state.type,
|
|
21
|
+
startedAt: getWorkflowExecutionStartedAt(state),
|
|
22
|
+
abortController: workflowAbort,
|
|
23
|
+
getNow: internals.options.getNow,
|
|
24
|
+
resolveWorkflowType: callbacks.resolveWorkflowTypeTarget,
|
|
25
|
+
executionStateOwnerId: state.executionStateOwnerId ?? workflowId,
|
|
26
|
+
accumulatedResults,
|
|
27
|
+
searchAttributes: checkpoint.searchAttributes,
|
|
28
|
+
registerCancelHandler: createCancelHandlerRegistration(internals, workflowId),
|
|
29
|
+
...registration.searchAttributes && {
|
|
30
|
+
searchAttributeSchema: registration.searchAttributes
|
|
31
|
+
},
|
|
32
|
+
sleepReferenceTime: checkpoint.createdAt,
|
|
33
|
+
...state.executionDeadline !== void 0 && { deadline: state.executionDeadline },
|
|
34
|
+
...internals.options.onLog != null && { logSink: internals.options.onLog }
|
|
35
|
+
});
|
|
36
|
+
setContextWorkflowInterceptor(context, getComposedWorkflowInterceptor(internals));
|
|
37
|
+
if (internals.options.development)
|
|
38
|
+
context.explain(!0);
|
|
39
|
+
const generator = registration.handler(context, state.input);
|
|
40
|
+
inlineStrategy.adoptWorkflow(workflowId, generator, context, workflowAbort);
|
|
41
|
+
inlineStrategy.continueWorkflow(workflowId, void 0);
|
|
42
|
+
callbacks.swallowPromiseRejection(callbacks.processPendingUpdatesAfterInlineAdvance(workflowId));
|
|
43
|
+
}
|
|
44
|
+
function launchWorkerWorkflowFromCheckpoint(internals, workflowId, state, checkpoint) {
|
|
45
|
+
const serialized = serializeCheckpoint(checkpoint);
|
|
46
|
+
internals.strategy.startWorkflow({
|
|
47
|
+
workflowId,
|
|
48
|
+
...state.workflowExecutionToken !== void 0 && {
|
|
49
|
+
workflowExecutionToken: state.workflowExecutionToken
|
|
50
|
+
},
|
|
51
|
+
workflowType: state.type,
|
|
52
|
+
input: state.input,
|
|
53
|
+
checkpoint: serialized,
|
|
54
|
+
executionStateOwnerId: state.executionStateOwnerId ?? workflowId,
|
|
55
|
+
...state.executionDeadline !== void 0 && { deadline: state.executionDeadline },
|
|
56
|
+
...internals.workflowHeaders.has(workflowId) && {
|
|
57
|
+
headers: [...internals.workflowHeaders.get(workflowId)]
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export function launchWorkflowFromCheckpoint(internals, workflowId, state, checkpoint, registration, resolvedRevision, callbacks) {
|
|
62
|
+
internals.workflowTypeByWorkflowId.set(workflowId, {
|
|
63
|
+
type: state.type,
|
|
64
|
+
revision: resolvedRevision
|
|
65
|
+
});
|
|
66
|
+
internals.checkpoints.set(workflowId, checkpoint);
|
|
67
|
+
internals.workflowVersionTuples.set(workflowId, createWorkflowVersionTuple(internals, registration, callbacks));
|
|
68
|
+
const handle = createWorkflowHandle(internals, workflowId, callbacks);
|
|
69
|
+
callbacks.dispatchEvent(new WorkflowStartedEvent(workflowId, state.type, state.input));
|
|
70
|
+
if (internals.inlineStrategy)
|
|
71
|
+
launchInlineWorkflowFromCheckpoint(internals, workflowId, state, checkpoint, registration, callbacks);
|
|
72
|
+
else
|
|
73
|
+
launchWorkerWorkflowFromCheckpoint(internals, workflowId, state, checkpoint);
|
|
74
|
+
return handle;
|
|
75
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { TimerEntry } from '../../types.ts';
|
|
2
|
+
import type { ExecutableRegistration } from '../dynamic-source-execution.ts';
|
|
3
|
+
import type { EngineInternals } from '../internals.ts';
|
|
4
|
+
import type { TimeOperationCallbacks } from '../operations-time.ts';
|
|
5
|
+
/**
|
|
6
|
+
* Resolve `type`'s registration for a delayed-start fire, failing the
|
|
7
|
+
* workflow (via `callbacks.failWorkflow`) and returning `null` on any
|
|
8
|
+
* resolution error instead of throwing. ADR 0002: acquires this workflow's
|
|
9
|
+
* claim standalone + hydrates cleanup tracking BEFORE failing — the
|
|
10
|
+
* happy-path pending→running fold later in `startDelayedWorkflow` would
|
|
11
|
+
* otherwise establish both.
|
|
12
|
+
*
|
|
13
|
+
* Resolved against `revision` — the pending run's own persisted
|
|
14
|
+
* `WorkflowState.revision` (WFT-17), `undefined` for a legacy record —
|
|
15
|
+
* never the catalog's active pointer, so a delayed-start fire honors the
|
|
16
|
+
* exact revision this run was created against, matching
|
|
17
|
+
* `resumeWorkflowFromStorage()`'s use of the same resolver.
|
|
18
|
+
*
|
|
19
|
+
* Returns the FULL `{ entry, revision }` pair, not just `entry` — mirroring
|
|
20
|
+
* `resolveExecutableRegistrationOrRenamedNotFound()`'s shape used by
|
|
21
|
+
* `resume.ts`/`transition.ts`. For a legacy, pre-revision-pinning pending
|
|
22
|
+
* record with exactly one registered `registerSource()` candidate, the
|
|
23
|
+
* resolver can unambiguously resolve that candidate even though the input
|
|
24
|
+
* `revision` was `undefined` — the caller MUST thread this returned
|
|
25
|
+
* `revision` (not the input `revision` or the persisted state's own,
|
|
26
|
+
* still-`undefined` `revision`) through the pending→running transition and
|
|
27
|
+
* into `beginWorkflowExecution`, or the per-instance identity cache ends up
|
|
28
|
+
* stamped with the wrong (missing) revision (WFT-19 review round 7).
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveDelayedStartRegistrationOrFail(internals: EngineInternals, entry: TimerEntry, type: string, revision: string | undefined, callbacks: Pick<TimeOperationCallbacks, 'failWorkflow' | 'resolveExecutableRegistrationForRevision'>): Promise<ExecutableRegistration | null>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ensureDelayedStartClaimAndCleanupBeforeFailure } from "./standalone-claim-acquire.js";
|
|
2
|
+
export async function resolveDelayedStartRegistrationOrFail(internals, entry, type, revision, callbacks) {
|
|
3
|
+
try {
|
|
4
|
+
return await callbacks.resolveExecutableRegistrationForRevision(type, revision);
|
|
5
|
+
} catch (error) {
|
|
6
|
+
await ensureDelayedStartClaimAndCleanupBeforeFailure(internals, entry.workflowId);
|
|
7
|
+
const asError = error instanceof Error ? error : Error(String(error));
|
|
8
|
+
await callbacks.failWorkflow(entry.workflowId, asError);
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -5,5 +5,37 @@ import type { EngineInternals } from '../internals.ts';
|
|
|
5
5
|
import { type LifecycleCallbacks } from './shared.ts';
|
|
6
6
|
export declare function createForkLineage(_internals: EngineInternals, sourceWorkflowId: string, checkpoint: Checkpoint, _callbacks: LifecycleCallbacks): ForkLineage;
|
|
7
7
|
export declare function buildForkSearchAttributes(_internals: EngineInternals, checkpoint: Checkpoint, lineage: ForkLineage, _callbacks: LifecycleCallbacks): Record<string, SearchAttributeValue>;
|
|
8
|
-
export declare function createForkedWorkflowState(_internals: EngineInternals, workflowId: string, sourceState: WorkflowState, versionTuple: WorkflowVersionTuple, lineage: ForkLineage, forkedAt: number, _callbacks: LifecycleCallbacks
|
|
8
|
+
export declare function createForkedWorkflowState(_internals: EngineInternals, workflowId: string, sourceState: WorkflowState, versionTuple: WorkflowVersionTuple, lineage: ForkLineage, forkedAt: number, _callbacks: LifecycleCallbacks,
|
|
9
|
+
/**
|
|
10
|
+
* `fork()`'s own resolver-returned revision — used to fill in the fork's
|
|
11
|
+
* persisted `revision` ONLY when `sourceState.revision` is itself
|
|
12
|
+
* `undefined` (WFT-19 review round 6, Codex, P1). Never overrides an
|
|
13
|
+
* already-defined `sourceState.revision`: `resolveExecutableRegistrationForRevision()`
|
|
14
|
+
* ALWAYS returns `revision: undefined` for an eager registration (eager
|
|
15
|
+
* has no ambiguity to resolve against — see `canResolveRevisionLocally`'s
|
|
16
|
+
* doc), even though the eager source run's own `sourceState.revision` is
|
|
17
|
+
* a real, independently-meaningful value (`resolveCachedStartRevision()`'s
|
|
18
|
+
* `registeredCatalogRevisions` fallback stamps it at ordinary start time,
|
|
19
|
+
* for every registration kind). Blindly preferring `resolvedRevision`
|
|
20
|
+
* here would have dropped that real value for every eager-type fork —
|
|
21
|
+
* caught by `tests/replay-fixtures/fork-from-checkpoint.json`'s golden
|
|
22
|
+
* byte comparison. For a legacy (pre-revision-pinning) source run on a
|
|
23
|
+
* dynamic-source type with exactly one registered candidate,
|
|
24
|
+
* `sourceState.revision` genuinely IS `undefined` even though the
|
|
25
|
+
* resolver resolved — and the fork launches against — that sole
|
|
26
|
+
* candidate's code; `sourceState.revision ?? resolvedRevision` falls
|
|
27
|
+
* through to the resolver's answer only in that case. Stamping the fork's
|
|
28
|
+
* persisted `revision` with the raw `undefined` legacy pin (as this
|
|
29
|
+
* function did before this fix existed) left the fork durably unpinned:
|
|
30
|
+
* it would run correctly until the next restart, but a fresh-process
|
|
31
|
+
* `recoverAll()` after a second candidate is later registered would
|
|
32
|
+
* classify the fork `legacy-ambiguous` and refuse to resume it, even
|
|
33
|
+
* though the fork's own resolver already knew exactly which revision it
|
|
34
|
+
* belonged to at creation time. Mirrors the identical fix already applied
|
|
35
|
+
* to the process-local identity cache in `checkpoint-launch.ts`'s
|
|
36
|
+
* `launchWorkflowFromCheckpoint()` (WFT-19 review round 5) — that fix
|
|
37
|
+
* closed the in-memory gap; this one closes the matching durable-state
|
|
38
|
+
* gap the same bug left behind, for the one case it actually applies to.
|
|
39
|
+
*/
|
|
40
|
+
resolvedRevision: string | undefined): WorkflowState;
|
|
9
41
|
export declare function buildForkBatchOperations(_internals: EngineInternals, workflowId: string, state: WorkflowState, checkpoint: Checkpoint, serializedCheckpoint: Uint8Array, workflowStartHeaders: Map<string, string> | undefined, _callbacks: LifecycleCallbacks): BatchOperation[];
|
|
@@ -16,7 +16,8 @@ export function buildForkSearchAttributes(_internals, checkpoint, lineage, _call
|
|
|
16
16
|
[FORK_LINEAGE_ATTRIBUTE]: lineage.workflowId
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
|
-
export function createForkedWorkflowState(_internals, workflowId, sourceState, versionTuple, lineage, forkedAt, _callbacks) {
|
|
19
|
+
export function createForkedWorkflowState(_internals, workflowId, sourceState, versionTuple, lineage, forkedAt, _callbacks, resolvedRevision) {
|
|
20
|
+
const forkRevision = sourceState.revision ?? resolvedRevision;
|
|
20
21
|
return {
|
|
21
22
|
id: workflowId,
|
|
22
23
|
type: sourceState.type,
|
|
@@ -24,6 +25,7 @@ export function createForkedWorkflowState(_internals, workflowId, sourceState, v
|
|
|
24
25
|
input: sourceState.input,
|
|
25
26
|
workflowExecutionToken: crypto.randomUUID(),
|
|
26
27
|
versionTuple,
|
|
28
|
+
...forkRevision !== void 0 && { revision: forkRevision },
|
|
27
29
|
executionStateOwnerId: workflowId,
|
|
28
30
|
createdAt: forkedAt,
|
|
29
31
|
startedAt: forkedAt,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `recoverAll()`'s per-`(type, revision)` preload barrier (WFT-17/WFT-18),
|
|
3
|
+
* replacing the per-`type`-only barrier `recovery-dynamic-sources.ts` used
|
|
4
|
+
* to own. Split out of `transition.ts`, which has no headroom under the
|
|
5
|
+
* repository's 500-line implementation-file ceiling for this logic inline
|
|
6
|
+
* — the same reason `recovery-dynamic-sources.ts` was split out for
|
|
7
|
+
* WFT-15/16.
|
|
8
|
+
*
|
|
9
|
+
* Recovery scans non-terminal workflow states, groups them by their exact
|
|
10
|
+
* `(type, revision)` pin (a legacy record with no persisted `revision`
|
|
11
|
+
* groups under `undefined` for its type), and preloads/classifies every
|
|
12
|
+
* group BEFORE any group's runs advance — ready, or unavailable with the
|
|
13
|
+
* error every run in that group fails with. Grouping and classification use
|
|
14
|
+
* nested `Map<type, Map<revision, …>>` structures rather than a
|
|
15
|
+
* delimiter-joined composite key, so an arbitrary workflow `type` or
|
|
16
|
+
* `revision` string can never collide with a delimiter.
|
|
17
|
+
*
|
|
18
|
+
* @module core/engine/lifecycle/recovery-revision-groups
|
|
19
|
+
*/
|
|
20
|
+
import { DynamicWorkflowSourceUnavailableError } from '../dynamic-source-errors.ts';
|
|
21
|
+
import type { EngineInternals } from '../internals.ts';
|
|
22
|
+
import { WorkflowRevisionUnavailableError } from '../revision-errors.ts';
|
|
23
|
+
import type { LifecycleCallbacks } from './shared.ts';
|
|
24
|
+
/** One preflight-recoverable entry's identity, as `transition.ts`'s preflight scan produces it. */
|
|
25
|
+
export type RecoverableRevisionEntry = {
|
|
26
|
+
readonly workflowId: string;
|
|
27
|
+
readonly type: string;
|
|
28
|
+
readonly revision: string | undefined;
|
|
29
|
+
};
|
|
30
|
+
/** One `(type, revision)` group: every non-terminal run pinned to that exact revision. */
|
|
31
|
+
export type RecoveryRevisionGroup = {
|
|
32
|
+
readonly type: string;
|
|
33
|
+
readonly revision: string | undefined;
|
|
34
|
+
readonly workflowIds: readonly string[];
|
|
35
|
+
};
|
|
36
|
+
/** `type -> revision -> group`. A legacy entry (no persisted `revision`) groups under the `undefined` key. */
|
|
37
|
+
export type RecoveryRevisionGroups = ReadonlyMap<string, ReadonlyMap<string | undefined, RecoveryRevisionGroup>>;
|
|
38
|
+
/**
|
|
39
|
+
* Group preflight-recoverable entries by their exact `(type, revision)` pin.
|
|
40
|
+
* Every non-terminal run pinned to the same revision of the same type lands
|
|
41
|
+
* in one group, regardless of scan order.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildRecoveryRevisionGroups(entries: readonly RecoverableRevisionEntry[]): RecoveryRevisionGroups;
|
|
44
|
+
/** Outcome of classifying one `(type, revision)` group. */
|
|
45
|
+
export type RevisionGroupClassification = {
|
|
46
|
+
readonly status: 'ready';
|
|
47
|
+
} | {
|
|
48
|
+
readonly status: 'unavailable';
|
|
49
|
+
readonly error: WorkflowRevisionUnavailableError | DynamicWorkflowSourceUnavailableError;
|
|
50
|
+
};
|
|
51
|
+
/** `type -> revision -> classification`, mirroring {@link RecoveryRevisionGroups}'s shape. */
|
|
52
|
+
export type RevisionGroupClassifications = ReadonlyMap<string, ReadonlyMap<string | undefined, RevisionGroupClassification>>;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve every `(type, revision)` group to `'ready'` or `'unavailable'`,
|
|
55
|
+
* concurrently, one `resolveExecutableRegistrationForRevision()` call per
|
|
56
|
+
* group — even when many non-terminal runs share the same pin. An eager
|
|
57
|
+
* type's groups are omitted from the result entirely (eager is always
|
|
58
|
+
* ready and needs no preload call); only dynamic-source groups are
|
|
59
|
+
* classified. A disposal mid-preload aborts the whole barrier by
|
|
60
|
+
* rethrowing, matching `recoverEntryOrIsolateFailure`'s own un-isolated
|
|
61
|
+
* treatment of {@link EngineDisposedError}.
|
|
62
|
+
*/
|
|
63
|
+
export declare function classifyRevisionGroups(internals: EngineInternals, callbacks: LifecycleCallbacks, groups: RecoveryRevisionGroups): Promise<RevisionGroupClassifications>;
|
|
64
|
+
/**
|
|
65
|
+
* Wrap `callbacks` so `recoverAll()`'s own per-entry `resume()` calls below
|
|
66
|
+
* see a batch's cached `classifyRevisionGroups()` failure for the entry's
|
|
67
|
+
* `(type, revision)` group, WITHOUT touching any state shared with other
|
|
68
|
+
* callers. `classifications` is a plain, closure-local `Map` structure —
|
|
69
|
+
* nothing outside `recoverAll()` ever sees it, so a concurrent, unrelated
|
|
70
|
+
* `engine.start()`, `engine.resume()`, or a second concurrent `recoverAll()`
|
|
71
|
+
* batch for the SAME `(type, revision)` keeps using the real, un-wrapped
|
|
72
|
+
* `callbacks.resolveExecutableRegistrationForRevision` and can never observe
|
|
73
|
+
* (or race the reset of) this batch's classification.
|
|
74
|
+
*/
|
|
75
|
+
export declare function createRecoveryScopedRevisionCallbacks(callbacks: LifecycleCallbacks, classifications: RevisionGroupClassifications): LifecycleCallbacks;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { DynamicWorkflowSourceUnavailableError } from "../dynamic-source-errors.js";
|
|
2
|
+
import { EngineDisposedError } from "../errors.js";
|
|
3
|
+
import { WorkflowRevisionUnavailableError } from "../revision-errors.js";
|
|
4
|
+
export function buildRecoveryRevisionGroups(entries) {
|
|
5
|
+
const groups = new Map;
|
|
6
|
+
for (const entry of entries) {
|
|
7
|
+
let byRevision = groups.get(entry.type);
|
|
8
|
+
if (byRevision === void 0) {
|
|
9
|
+
byRevision = new Map;
|
|
10
|
+
groups.set(entry.type, byRevision);
|
|
11
|
+
}
|
|
12
|
+
const existing = byRevision.get(entry.revision);
|
|
13
|
+
if (existing === void 0)
|
|
14
|
+
byRevision.set(entry.revision, {
|
|
15
|
+
type: entry.type,
|
|
16
|
+
revision: entry.revision,
|
|
17
|
+
workflowIds: [entry.workflowId]
|
|
18
|
+
});
|
|
19
|
+
else
|
|
20
|
+
existing.workflowIds.push(entry.workflowId);
|
|
21
|
+
}
|
|
22
|
+
return groups;
|
|
23
|
+
}
|
|
24
|
+
export async function classifyRevisionGroups(internals, callbacks, groups) {
|
|
25
|
+
const dynamicGroups = [];
|
|
26
|
+
for (const [type, byRevision] of groups) {
|
|
27
|
+
if (internals.registrations.has(type))
|
|
28
|
+
continue;
|
|
29
|
+
dynamicGroups.push(...byRevision.values());
|
|
30
|
+
}
|
|
31
|
+
const results = new Map;
|
|
32
|
+
if (dynamicGroups.length === 0)
|
|
33
|
+
return results;
|
|
34
|
+
const settled = await Promise.allSettled(dynamicGroups.map((group) => callbacks.resolveExecutableRegistrationForRevision(group.type, group.revision)));
|
|
35
|
+
for (const [index, outcome] of settled.entries()) {
|
|
36
|
+
const group = dynamicGroups[index];
|
|
37
|
+
let byRevision = results.get(group.type);
|
|
38
|
+
if (byRevision === void 0) {
|
|
39
|
+
byRevision = new Map;
|
|
40
|
+
results.set(group.type, byRevision);
|
|
41
|
+
}
|
|
42
|
+
if (outcome.status === "fulfilled") {
|
|
43
|
+
byRevision.set(group.revision, { status: "ready" });
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const reason = outcome.reason;
|
|
47
|
+
if (reason instanceof EngineDisposedError)
|
|
48
|
+
throw reason;
|
|
49
|
+
const error = reason instanceof WorkflowRevisionUnavailableError || reason instanceof DynamicWorkflowSourceUnavailableError ? reason : new DynamicWorkflowSourceUnavailableError(group.type, group.revision, "load-failed", reason);
|
|
50
|
+
byRevision.set(group.revision, { status: "unavailable", error });
|
|
51
|
+
}
|
|
52
|
+
return results;
|
|
53
|
+
}
|
|
54
|
+
export function createRecoveryScopedRevisionCallbacks(callbacks, classifications) {
|
|
55
|
+
return {
|
|
56
|
+
...callbacks,
|
|
57
|
+
resolveExecutableRegistrationForRevision: (type, revision) => {
|
|
58
|
+
const classification = classifications.get(type)?.get(revision);
|
|
59
|
+
if (classification?.status === "unavailable")
|
|
60
|
+
return Promise.reject(classification.error);
|
|
61
|
+
return callbacks.resolveExecutableRegistrationForRevision(type, revision);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|