@lostgradient/weft 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/api.d.ts +2 -0
- package/dist/cli/api.js +3 -4
- package/dist/cli/generated/operation-catalog.snapshot.json +5167 -0
- package/dist/cli/generated/operation-client.generated.d.ts +119 -10
- package/dist/cli/generated/operation-client.generated.js +74 -1
- package/dist/cli/index.js +1 -0
- package/dist/cli/operation-catalog-snapshot.d.ts +11 -2
- package/dist/cli/operation-catalog-snapshot.js +14 -5
- package/dist/cli/operation-client-runtime.d.ts +29 -0
- package/dist/cli-main.js +227 -401
- package/dist/client/client-storage.d.ts +24 -0
- package/dist/client/client-storage.js +14 -0
- package/dist/client/handle-delegation.d.ts +3 -3
- package/dist/client/handle-delegation.js +2 -2
- package/dist/client/http-client-requests.d.ts +5 -3
- package/dist/client/http-client-requests.js +20 -2
- package/dist/client/http-client-storage.d.ts +3 -0
- package/dist/client/http-client-storage.js +110 -0
- package/dist/client/http-client.d.ts +9 -6
- package/dist/client/http-client.js +13 -18
- package/dist/client/http-operations.d.ts +3 -1
- package/dist/client/http-operations.js +84 -2
- package/dist/client/http-request.d.ts +35 -6
- package/dist/client/http-request.js +31 -10
- package/dist/client/in-process-operations.d.ts +2 -2
- package/dist/client/in-process-operations.js +2 -2
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +18 -0
- package/dist/client/interface.d.ts +26 -26
- package/dist/client/local.d.ts +9 -6
- package/dist/client/local.js +9 -4
- package/dist/client/search-params.js +10 -6
- package/dist/client/start-body.d.ts +3 -1
- package/dist/client/start-body.js +9 -0
- package/dist/connection.d.ts +8 -0
- package/dist/connection.js +41 -17
- package/dist/core/bulk-workflow-filter.d.ts +2 -1
- package/dist/core/bulk-workflow-filter.js +5 -1
- package/dist/core/engine/async-activity-records.d.ts +12 -1
- package/dist/core/engine/async-activity-records.js +81 -2
- package/dist/core/engine/bulk-operations-purge.js +16 -1
- package/dist/core/engine/bulk-operations-shared.js +2 -0
- package/dist/core/engine/callback-creators-core.js +2 -1
- package/dist/core/engine/callback-creators-schedule.d.ts +2 -1
- package/dist/core/engine/callback-creators-schedule.js +3 -3
- package/dist/core/engine/checkpoint-reads.js +11 -1
- package/dist/core/engine/child-workflow.js +15 -6
- package/dist/core/engine/construction.js +3 -0
- package/dist/core/engine/disposal.js +2 -0
- package/dist/core/engine/engine-internal-types.d.ts +4 -1
- package/dist/core/engine/fenced-write.d.ts +5 -3
- package/dist/core/engine/finalizer-status.d.ts +10 -0
- package/dist/core/engine/finalizer-status.js +82 -0
- package/dist/core/engine/index.d.ts +69 -2
- package/dist/core/engine/index.js +71 -8
- package/dist/core/engine/internals.d.ts +3 -0
- package/dist/core/engine/lease-health.d.ts +64 -0
- package/dist/core/engine/lease-health.js +0 -0
- package/dist/core/engine/lease-manager.d.ts +4 -2
- package/dist/core/engine/lease-manager.js +32 -4
- package/dist/core/engine/lifecycle/recovered-services.js +0 -2
- package/dist/core/engine/lifecycle/shared.d.ts +28 -0
- package/dist/core/engine/lifecycle/start-batch.js +12 -0
- package/dist/core/engine/lifecycle/start.d.ts +1 -1
- package/dist/core/engine/lifecycle/start.js +34 -5
- package/dist/core/engine/lifecycle/transition.js +19 -9
- package/dist/core/engine/operations-coordination.d.ts +1 -12
- package/dist/core/engine/operations-coordination.js +57 -10
- package/dist/core/engine/operations-speculate.d.ts +1 -1
- package/dist/core/engine/operations-speculate.js +19 -5
- package/dist/core/engine/registration.js +14 -0
- package/dist/core/engine/schedule-handle.d.ts +4 -4
- package/dist/core/engine/schedule-handle.js +2 -2
- package/dist/core/engine/schedule-overlap.d.ts +11 -0
- package/dist/core/engine/schedule-overlap.js +50 -0
- package/dist/core/engine/schedule-run.d.ts +11 -3
- package/dist/core/engine/schedule-run.js +27 -4
- package/dist/core/engine/schedule-timer.js +10 -1
- package/dist/core/engine/schedules.d.ts +5 -4
- package/dist/core/engine/schedules.js +62 -56
- package/dist/core/engine/state-utilities.js +1 -1
- package/dist/core/engine/storage-io.d.ts +12 -0
- package/dist/core/engine/storage-io.js +49 -10
- package/dist/core/engine/termination/finalizer-claim.d.ts +8 -6
- package/dist/core/engine/termination/finalizer-claim.js +2 -1
- package/dist/core/engine/termination/finalizer.js +10 -14
- package/dist/core/engine/timeline-coordinator-constants.d.ts +3 -0
- package/dist/core/engine/timeline-coordinator-constants.js +1 -0
- package/dist/core/engine/timeline-coordinator-detail.d.ts +13 -0
- package/dist/core/engine/timeline-coordinator-detail.js +76 -0
- package/dist/core/engine/validation/schedule-options.d.ts +8 -1
- package/dist/core/engine/validation/schedule-options.js +18 -0
- package/dist/core/engine/validation/schedule.d.ts +1 -1
- package/dist/core/engine/validation/schedule.js +27 -3
- package/dist/core/engine/validation.js +66 -2
- package/dist/core/engine/workflow-state-stream.d.ts +5 -1
- package/dist/core/engine/workflow-state-stream.js +37 -14
- package/dist/core/events/schedule-events.d.ts +3 -4
- package/dist/core/events/workflow-events.d.ts +2 -1
- package/dist/core/list-filter-validation.d.ts +3 -0
- package/dist/core/list-filter-validation.js +11 -0
- package/dist/core/registry-snapshot.d.ts +32 -0
- package/dist/core/registry-snapshot.js +28 -0
- package/dist/core/review/index.d.ts +4 -4
- package/dist/core/types/activity.d.ts +52 -5
- package/dist/core/types/bulk.d.ts +1 -0
- package/dist/core/types/definition-schema-to-json.d.ts +11 -0
- package/dist/core/types/definition-schema-to-json.js +11 -2
- package/dist/core/types/list-options.d.ts +6 -0
- package/dist/core/types/options.d.ts +6 -5
- package/dist/core/types/reviews.d.ts +2 -2
- package/dist/core/types/schedules.d.ts +45 -2
- package/dist/core/types/services-resolution.d.ts +5 -5
- package/dist/core/types/state.d.ts +64 -0
- package/dist/core/types/workflow-log.d.ts +3 -2
- package/dist/core/types/workflow-observability.d.ts +50 -0
- package/dist/core/types/workflow-observability.js +0 -0
- package/dist/core/types/workflow-registry.d.ts +7 -0
- package/dist/core/types.d.ts +1 -0
- package/dist/core/types.js +1 -0
- package/dist/http.js +2 -0
- package/dist/index.d.ts +4 -3
- package/dist/indexeddb.js +1 -0
- package/dist/json-schema.js +3 -3
- package/dist/mcp/cli.js +63 -61
- package/dist/mcp/http.d.ts +7 -2
- package/dist/mcp/session.d.ts +3 -3
- package/dist/mcp/session.js +3 -2
- package/dist/mcp/stdio.d.ts +7 -2
- package/dist/observability/index.js +2 -2
- package/dist/runtime/portable.d.ts +33 -0
- package/dist/runtime/portable.js +17 -6
- package/dist/server/access-policy-metadata.d.ts +92 -0
- package/dist/server/access-policy-metadata.js +78 -0
- package/dist/server/engine-event-feed-backend.d.ts +22 -3
- package/dist/server/fault-to-http.d.ts +3 -7
- package/dist/server/fault-to-http.js +2 -34
- package/dist/server/fleet-event-feed.d.ts +65 -0
- package/dist/server/handler/index.d.ts +11 -2
- package/dist/server/handler/index.js +8 -1
- package/dist/server/handler/route-dispatch.d.ts +10 -3
- package/dist/server/handler/route-dispatch.js +19 -4
- package/dist/server/handler.js +1 -56
- package/dist/server/index.d.ts +13 -2
- package/dist/server/index.js +88 -64
- package/dist/server/interactive-operations.js +2 -0
- package/dist/server/openapi-error-responses.js +55 -4
- package/dist/server/openapi.d.ts +3 -1
- package/dist/server/openapi.js +4 -0
- package/dist/server/openrpc-document-schema.d.ts +64 -0
- package/dist/server/openrpc-document-schema.js +6 -26
- package/dist/server/openrpc.d.ts +4 -0
- package/dist/server/openrpc.js +9 -33
- package/dist/server/operation-fault.d.ts +24 -7
- package/dist/server/operation-fault.js +65 -13
- package/dist/server/operations/aggregate-workflows.d.ts +6 -0
- package/dist/server/operations/aggregate-workflows.js +3 -2
- package/dist/server/operations/async-activity.d.ts +14 -0
- package/dist/server/operations/async-activity.js +88 -4
- package/dist/server/operations/bulk-filter-helpers.d.ts +3 -0
- package/dist/server/operations/bulk-filter-helpers.js +27 -3
- package/dist/server/operations/bulk-mutate-workflow-tags.d.ts +6 -0
- package/dist/server/operations/bulk-signal-workflows.d.ts +6 -0
- package/dist/server/operations/get-stream-chunks.js +2 -7
- package/dist/server/operations/get-system-lease.d.ts +19 -0
- package/dist/server/operations/get-system-lease.js +48 -0
- package/dist/server/operations/get-workflow-observability.d.ts +24 -0
- package/dist/server/operations/get-workflow-observability.js +80 -0
- package/dist/server/operations/get-workflow-result.js +2 -2
- package/dist/server/operations/get-workflow.d.ts +2 -2
- package/dist/server/operations/list-filter-query-extractor.js +15 -0
- package/dist/server/operations/list-workflows.d.ts +6 -0
- package/dist/server/operations/list-workflows.js +12 -3
- package/dist/server/operations/operation-helpers.d.ts +9 -18
- package/dist/server/operations/operation-helpers.js +5 -23
- package/dist/server/operations/purge-workflows.d.ts +3 -0
- package/dist/server/operations/recover-all.js +2 -1
- package/dist/server/operations/single-workflow-tag-mutation.js +2 -2
- package/dist/server/operations/sse-stream.js +2 -7
- package/dist/server/operations/storage-capabilities.d.ts +34 -0
- package/dist/server/operations/storage-capabilities.js +38 -0
- package/dist/server/operations/storage.js +8 -17
- package/dist/server/operations/stream-workflow-sse.js +3 -9
- package/dist/server/operations/submit-review-decision.d.ts +2 -0
- package/dist/server/operations/submit-review-decision.js +19 -2
- package/dist/server/operations/update-schedule.d.ts +8 -0
- package/dist/server/operations/update-schedule.js +70 -7
- package/dist/server/operations/workflow-events-sse.js +2 -2
- package/dist/server/replay-live-feed-internals.d.ts +19 -0
- package/dist/server/replay-live-feed-internals.js +76 -0
- package/dist/server/rest-binding.d.ts +7 -6
- package/dist/server/rest-bindings.d.ts +2 -13
- package/dist/server/rest-bindings.js +19 -10
- package/dist/server/runtime/authentication-bridge.d.ts +2 -2
- package/dist/server/serve-internals.js +3 -3
- package/dist/server/task-queue.d.ts +7 -5
- package/dist/server/workflow-event-feed.d.ts +100 -11
- package/dist/server/workflow-event-feed.js +1 -140
- package/dist/service-worker/index.d.ts +8 -2
- package/dist/service-worker/index.js +37 -56
- package/dist/service-worker/setup.d.ts +6 -2
- package/dist/storage/auto.js +1 -1
- package/dist/storage/bun-sql.js +3 -3
- package/dist/storage/capabilities.d.ts +1 -1
- package/dist/storage/cloudflare-durable-object-sql.d.ts +75 -0
- package/dist/storage/cloudflare-durable-object-sql.js +0 -0
- package/dist/storage/cloudflare-value-codec.d.ts +67 -0
- package/dist/storage/cloudflare-value-codec.js +46 -0
- package/dist/storage/cloudflare.d.ts +140 -0
- package/dist/storage/cloudflare.js +127 -0
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/http.js +200 -2
- package/dist/storage/index.d.ts +8 -1
- package/dist/storage/indexeddb.js +311 -1
- package/dist/storage/interface.d.ts +16 -4
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +4 -0
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.js +4 -4
- package/dist/storage/node-sqlite.js +3 -3
- package/dist/storage/postgres-key-value-queries.d.ts +6 -0
- package/dist/storage/postgres-key-value-queries.js +2 -3
- package/dist/storage/postgres.js +4 -4
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/sql-identifier.d.ts +35 -0
- package/dist/storage/sql-identifier.js +5 -0
- package/dist/storage/sqlite-key-value-queries.d.ts +11 -6
- package/dist/storage/sqlite-key-value-queries.js +3 -3
- package/dist/storage/testing.js +1 -1
- package/dist/storage/turso.js +2 -2
- package/dist/storage/typed-storage.js +138 -2
- package/dist/storage/web-extension.js +298 -1
- package/dist/testing/index.js +21 -60
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web-extension.js +1 -0
- package/dist/worker/protocol.js +1 -1
- package/package.json +8 -3
|
@@ -223,7 +223,7 @@ function matchesListFilterStatus(state, filter) {
|
|
|
223
223
|
return (Array.isArray(filter.status) ? filter.status : [filter.status]).includes(state.status);
|
|
224
224
|
}
|
|
225
225
|
function matchesListFilterIdentity(state, filter) {
|
|
226
|
-
return matchesListFilterStatus(state, filter) && (filter.type === void 0 || state.type === filter.type) && (filter.idPrefix === void 0 || state.id.startsWith(filter.idPrefix));
|
|
226
|
+
return matchesListFilterStatus(state, filter) && (filter.type === void 0 || state.type === filter.type) && (filter.parentWorkflowId === void 0 || state.parentWorkflowId === filter.parentWorkflowId) && (filter.parentWorkflowExecutionToken === void 0 || state.parentWorkflowExecutionToken === filter.parentWorkflowExecutionToken) && (filter.idPrefix === void 0 || state.id.startsWith(filter.idPrefix));
|
|
227
227
|
}
|
|
228
228
|
function matchesListFilterTimeRanges(state, filter) {
|
|
229
229
|
if (filter.createdAt !== void 0 && !timestampInRange(state.createdAt, filter.createdAt))
|
|
@@ -3,6 +3,16 @@ import type { ScheduleState, WorkflowState } from '../types.ts';
|
|
|
3
3
|
import type { EngineInternals } from './internals.ts';
|
|
4
4
|
/** Run a workflow-state write after any earlier write for that workflow has settled. */
|
|
5
5
|
export declare function runSerializedWorkflowStateWrite<TResult>(internals: EngineInternals, workflowId: string, writeOperation: () => Promise<TResult>): Promise<TResult>;
|
|
6
|
+
/**
|
|
7
|
+
* Serialize a schedule read-modify-write operation with its timer callback.
|
|
8
|
+
*
|
|
9
|
+
* Unlike workflow-state write serialization, a failed predecessor is propagated
|
|
10
|
+
* to callers that were already queued behind it. In particular, an update that
|
|
11
|
+
* arrives while a scanned timer callback is rearming must not delete the fired
|
|
12
|
+
* timer after that callback fails; rejecting the queued update leaves the durable
|
|
13
|
+
* timer available for the scheduler (or a lease successor) to retry.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runSerializedScheduleStateOperation<TResult>(internals: EngineInternals, scheduleId: string, operation: () => Promise<TResult>): Promise<TResult>;
|
|
6
16
|
/** Load and decode persisted workflow state by workflow ID. */
|
|
7
17
|
export declare function loadWorkflowState(internals: EngineInternals, workflowId: string): Promise<WorkflowState | null>;
|
|
8
18
|
/** Load a terminal workflow result or throw the persisted terminal error. */
|
|
@@ -29,6 +39,8 @@ export declare function requireScheduleState(internals: EngineInternals, schedul
|
|
|
29
39
|
/** Persist schedule state and optionally write the next schedule timer. */
|
|
30
40
|
export declare function writeScheduleState(internals: EngineInternals, state: ScheduleState, options?: {
|
|
31
41
|
includeTimer?: boolean;
|
|
42
|
+
replaceTimerFrom?: ScheduleState;
|
|
43
|
+
additionalOperations?: BatchOperation[];
|
|
32
44
|
}): Promise<void>;
|
|
33
45
|
/** Load persisted workflow start headers by workflow ID. */
|
|
34
46
|
export declare function loadWorkflowStartHeaders(internals: EngineInternals, workflowId: string): Promise<Map<string, string> | undefined>;
|
|
@@ -30,6 +30,21 @@ export async function runSerializedWorkflowStateWrite(internals, workflowId, wri
|
|
|
30
30
|
internals.workflowStateWriteChains.delete(workflowId);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
export async function runSerializedScheduleStateOperation(internals, scheduleId, operation) {
|
|
34
|
+
const execution = (internals.scheduleStateOperationChains.get(scheduleId) ?? Promise.resolve()).then(operation), trackedExecution = execution.then(() => {
|
|
35
|
+
return;
|
|
36
|
+
});
|
|
37
|
+
trackedExecution.catch(() => {
|
|
38
|
+
return;
|
|
39
|
+
});
|
|
40
|
+
internals.scheduleStateOperationChains.set(scheduleId, trackedExecution);
|
|
41
|
+
try {
|
|
42
|
+
return await execution;
|
|
43
|
+
} finally {
|
|
44
|
+
if (internals.scheduleStateOperationChains.get(scheduleId) === trackedExecution)
|
|
45
|
+
internals.scheduleStateOperationChains.delete(scheduleId);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
33
48
|
export async function loadWorkflowState(internals, workflowId) {
|
|
34
49
|
const bytes = await internals.storage.get(KEYS.workflow(workflowId));
|
|
35
50
|
if (!bytes)
|
|
@@ -80,19 +95,43 @@ export async function requireScheduleState(internals, scheduleId) {
|
|
|
80
95
|
throw Error(`Schedule "${scheduleId}" not found`);
|
|
81
96
|
return state;
|
|
82
97
|
}
|
|
98
|
+
function buildPreviousScheduleTimerDeleteOperations(previousState) {
|
|
99
|
+
if (previousState?.status !== "active" || previousState.nextFireAt === null)
|
|
100
|
+
return [];
|
|
101
|
+
return [
|
|
102
|
+
{
|
|
103
|
+
type: "delete",
|
|
104
|
+
key: KEYS.scheduleTick(resolveEffectiveScheduleFireAt(previousState, previousState.nextFireAt), previousState.id)
|
|
105
|
+
}
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
function buildNextScheduleTimerOperations(state, includeTimer) {
|
|
109
|
+
if (!includeTimer || state.status !== "active" || state.nextFireAt === null)
|
|
110
|
+
return [];
|
|
111
|
+
return buildTimerBatchOperations({
|
|
112
|
+
id: createScheduleTimerId(state.id),
|
|
113
|
+
workflowId: state.id,
|
|
114
|
+
fireAt: resolveEffectiveScheduleFireAt(state, state.nextFireAt),
|
|
115
|
+
kind: "schedule"
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function buildScheduleTimerReplacementOperations(state, includeTimer, previousState) {
|
|
119
|
+
const deleteOperations = buildPreviousScheduleTimerDeleteOperations(previousState), nextTimerOperations = buildNextScheduleTimerOperations(state, includeTimer);
|
|
120
|
+
if (nextTimerOperations.length > 0)
|
|
121
|
+
return [...deleteOperations, ...nextTimerOperations];
|
|
122
|
+
if (previousState === void 0)
|
|
123
|
+
return deleteOperations;
|
|
124
|
+
return [
|
|
125
|
+
...deleteOperations,
|
|
126
|
+
{ type: "delete", key: `timer-idx:${createScheduleTimerId(state.id)}` }
|
|
127
|
+
];
|
|
128
|
+
}
|
|
83
129
|
export async function writeScheduleState(internals, state, options) {
|
|
84
130
|
const operations = [
|
|
85
131
|
{ type: "put", key: KEYS.schedule(state.id), value: encode(state) }
|
|
86
|
-
];
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
operations.push(...buildTimerBatchOperations({
|
|
90
|
-
id: createScheduleTimerId(state.id),
|
|
91
|
-
workflowId: state.id,
|
|
92
|
-
fireAt: effectiveFireAt,
|
|
93
|
-
kind: "schedule"
|
|
94
|
-
}));
|
|
95
|
-
}
|
|
132
|
+
], includeTimer = options?.includeTimer ?? state.status === "active";
|
|
133
|
+
operations.push(...buildScheduleTimerReplacementOperations(state, includeTimer, options?.replaceTimerFrom));
|
|
134
|
+
operations.push(...options?.additionalOperations ?? []);
|
|
96
135
|
await commitFencedEngineWrite(internals, operations, [], () => Error(`Schedule state commit for schedule "${state.id}" lost its precondition.`));
|
|
97
136
|
}
|
|
98
137
|
export async function loadWorkflowStartHeaders(internals, workflowId) {
|
|
@@ -37,11 +37,11 @@ export declare function runningClaimIsStale(internals: EngineInternals, claim: T
|
|
|
37
37
|
/**
|
|
38
38
|
* Durable audit record written to {@link KEYS.teardownDeadLetter} when a workflow's
|
|
39
39
|
* finalizer permanently fails — the retry horizon was reached, or the recorded resource
|
|
40
|
-
* state vanished so the finalizer can never run. It is the supported operator
|
|
41
|
-
* for a leaked external resource
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
40
|
+
* state vanished so the finalizer can never run. It is the supported durable operator
|
|
41
|
+
* record for a leaked external resource; {@link Engine.getFinalizerStatus} and the matching
|
|
42
|
+
* transport operation expose it without relying on best-effort teardown events. The record
|
|
43
|
+
* is excluded from the workflow purge delete-set so it survives after the workflow record
|
|
44
|
+
* is gone.
|
|
45
45
|
*/
|
|
46
46
|
export interface TeardownDeadLetterRecord {
|
|
47
47
|
/** The workflow type whose finalizer leaked. */
|
|
@@ -52,6 +52,8 @@ export interface TeardownDeadLetterRecord {
|
|
|
52
52
|
attempts: number;
|
|
53
53
|
/** Engine clock when the record was written. */
|
|
54
54
|
deadLetteredAt: number;
|
|
55
|
+
/** Concrete run identity, present on records written from current workflow state. */
|
|
56
|
+
workflowExecutionToken?: string;
|
|
55
57
|
/** The decoded `ctx.setFinalizerState` payload, when it was still recoverable. */
|
|
56
58
|
finalizerInput?: unknown;
|
|
57
59
|
}
|
|
@@ -107,4 +109,4 @@ export declare function settleOnRunningClaim(internals: EngineInternals, workflo
|
|
|
107
109
|
export declare function deadLetterTeardown(internals: EngineInternals, workflowId: string, workflowType: string, attempts: number, expectedBytes: Uint8Array, details: {
|
|
108
110
|
lastError: string;
|
|
109
111
|
finalizerInput: unknown;
|
|
110
|
-
}): Promise<boolean>;
|
|
112
|
+
}, workflowExecutionToken?: string): Promise<boolean>;
|
|
@@ -64,12 +64,13 @@ export async function settleOnRunningClaim(internals, workflowId, runningBytes,
|
|
|
64
64
|
{ key: KEYS.teardownOwed(workflowId), expectedValue: runningBytes }
|
|
65
65
|
]);
|
|
66
66
|
}
|
|
67
|
-
export async function deadLetterTeardown(internals, workflowId, workflowType, attempts, expectedBytes, details) {
|
|
67
|
+
export async function deadLetterTeardown(internals, workflowId, workflowType, attempts, expectedBytes, details, workflowExecutionToken) {
|
|
68
68
|
const deadLetter = {
|
|
69
69
|
type: workflowType,
|
|
70
70
|
lastError: details.lastError,
|
|
71
71
|
attempts,
|
|
72
72
|
deadLetteredAt: internals.options.getNow(),
|
|
73
|
+
...workflowExecutionToken === void 0 ? {} : { workflowExecutionToken },
|
|
73
74
|
...details.finalizerInput === void 0 ? {} : { finalizerInput: details.finalizerInput }
|
|
74
75
|
};
|
|
75
76
|
return settleOnRunningClaim(internals, workflowId, expectedBytes, [
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { KEYS } from "../../../storage/interface.js";
|
|
2
2
|
import { decode } from "../../codec.js";
|
|
3
3
|
import { WorkflowTeardownEvent } from "../../events.js";
|
|
4
|
+
import { buildTeardownSuccessOperations } from "../finalizer-status.js";
|
|
4
5
|
import { isTeardownClaim, parseTeardownTimerId } from "../state-utilities.js";
|
|
5
6
|
import { runFinalizerActivity } from "./finalizer-activity.js";
|
|
6
7
|
import {
|
|
@@ -21,10 +22,7 @@ export { teardownStaleThresholdMs } from "./finalizer-claim.js";
|
|
|
21
22
|
const TERMINAL_STATUSES_OWED_TEARDOWN = new Set([
|
|
22
23
|
"cancelled",
|
|
23
24
|
"timed-out"
|
|
24
|
-
]);
|
|
25
|
-
function errorMessage(error) {
|
|
26
|
-
return error instanceof Error ? error.message : String(error);
|
|
27
|
-
}
|
|
25
|
+
]), errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
28
26
|
async function rearmOnLostSettle(internals, workflowId, token) {
|
|
29
27
|
await rearmTeardownTimer(internals, workflowId, token, TEARDOWN_SELF_HEAL_DELAY_MS);
|
|
30
28
|
}
|
|
@@ -63,7 +61,7 @@ async function resolveTeardownDrive(internals, workflowId, token, callbacks) {
|
|
|
63
61
|
return { kind: "rearm", token };
|
|
64
62
|
const finalizerStateBytes = await internals.storage.get(KEYS.finalizerState(workflowId));
|
|
65
63
|
if (finalizerStateBytes === null) {
|
|
66
|
-
await deadLetterMissingState(internals, workflowId, state.type, claim.attempts, markerBytes, callbacks);
|
|
64
|
+
await deadLetterMissingState(internals, workflowId, state.type, state.workflowExecutionToken, claim.attempts, markerBytes, callbacks);
|
|
67
65
|
return { kind: "cleared" };
|
|
68
66
|
}
|
|
69
67
|
return {
|
|
@@ -71,12 +69,12 @@ async function resolveTeardownDrive(internals, workflowId, token, callbacks) {
|
|
|
71
69
|
drive: { state, claim, markerBytes, finalizer, finalizerInput: decode(finalizerStateBytes) }
|
|
72
70
|
};
|
|
73
71
|
}
|
|
74
|
-
async function deadLetterMissingState(internals, workflowId, workflowType, attempts, expectedBytes, callbacks) {
|
|
72
|
+
async function deadLetterMissingState(internals, workflowId, workflowType, workflowExecutionToken, attempts, expectedBytes, callbacks) {
|
|
75
73
|
const lastError = "finalizer state missing \u2014 recorded resource cannot be torn down";
|
|
76
74
|
if (await deadLetterTeardown(internals, workflowId, workflowType, attempts, expectedBytes, {
|
|
77
75
|
lastError,
|
|
78
76
|
finalizerInput: void 0
|
|
79
|
-
}))
|
|
77
|
+
}, workflowExecutionToken))
|
|
80
78
|
callbacks.dispatchEvent(new WorkflowTeardownEvent(workflowId, workflowType, "dead-lettered", attempts, lastError));
|
|
81
79
|
}
|
|
82
80
|
export async function runWorkflowFinalizer(internals, workflowId, timerId, callbacks) {
|
|
@@ -105,7 +103,7 @@ async function driveResolvedTeardown(internals, workflowId, token, drive, callba
|
|
|
105
103
|
}
|
|
106
104
|
const result = await runFinalizerActivity(finalizer, finalizerInput, attempt, internals.abortController.signal, state.workflowExecutionToken);
|
|
107
105
|
if (result.ok) {
|
|
108
|
-
await settleTeardownSuccess(internals, workflowId, state.type, token, attempt, runningBytes, callbacks);
|
|
106
|
+
await settleTeardownSuccess(internals, workflowId, state.type, state.workflowExecutionToken, token, attempt, runningBytes, callbacks);
|
|
109
107
|
return;
|
|
110
108
|
}
|
|
111
109
|
if (result.abortedByShutdown) {
|
|
@@ -123,11 +121,9 @@ async function driveResolvedTeardown(internals, workflowId, token, drive, callba
|
|
|
123
121
|
}
|
|
124
122
|
await settleTeardownFailure(internals, workflowId, state, attempt, token, runningBytes, result.error, callbacks);
|
|
125
123
|
}
|
|
126
|
-
async function settleTeardownSuccess(internals, workflowId, workflowType, token, attempt, runningBytes, callbacks) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
{ type: "delete", key: KEYS.finalizerState(workflowId) }
|
|
130
|
-
])) {
|
|
124
|
+
async function settleTeardownSuccess(internals, workflowId, workflowType, workflowExecutionToken, token, attempt, runningBytes, callbacks) {
|
|
125
|
+
const operations = buildTeardownSuccessOperations(workflowId, attempt, internals.options.getNow(), workflowExecutionToken);
|
|
126
|
+
if (!await settleOnRunningClaim(internals, workflowId, runningBytes, operations)) {
|
|
131
127
|
await rearmOnLostSettle(internals, workflowId, token);
|
|
132
128
|
return;
|
|
133
129
|
}
|
|
@@ -140,7 +136,7 @@ async function settleTeardownFailure(internals, workflowId, state, attempt, toke
|
|
|
140
136
|
if (!await deadLetterTeardown(internals, workflowId, state.type, attempt, runningBytes, {
|
|
141
137
|
lastError: message,
|
|
142
138
|
finalizerInput: finalizerStateBytes === null ? void 0 : decode(finalizerStateBytes)
|
|
143
|
-
})) {
|
|
139
|
+
}, state.workflowExecutionToken)) {
|
|
144
140
|
await rearmOnLostSettle(internals, workflowId, token);
|
|
145
141
|
return;
|
|
146
142
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MAX_TIMELINE_COORDINATOR_DETAILS = 100, MAX_TIMELINE_DETAIL_STRING_LENGTH = 512;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ContextOperationRequest } from '../context.ts';
|
|
2
|
+
import type { ParallelBranchSlot } from '../context/parallel-operations.ts';
|
|
3
|
+
import type { WorkflowTimelineOperationDetail } from '../types.ts';
|
|
4
|
+
import type { EngineInternals } from './internals.ts';
|
|
5
|
+
export declare function operationTimelineDetail(operation: ContextOperationRequest, index: number, outcome: WorkflowTimelineOperationDetail['outcome'], options?: {
|
|
6
|
+
error?: unknown;
|
|
7
|
+
key?: string;
|
|
8
|
+
}): WorkflowTimelineOperationDetail;
|
|
9
|
+
export declare function parallelTimelineDetails(operations: readonly ContextOperationRequest[], slots: readonly ParallelBranchSlot[], branchNames?: readonly string[]): WorkflowTimelineOperationDetail[];
|
|
10
|
+
export declare function raceTimelineDetails(operations: readonly ContextOperationRequest[], winnerIndex: number, branchNames?: readonly string[], winnerError?: unknown): WorkflowTimelineOperationDetail[];
|
|
11
|
+
export declare function runAllTimelineDetails(branchNames: readonly string[], slots: readonly ParallelBranchSlot[], branches: Record<string, readonly [Function] | readonly [Function, unknown]>): WorkflowTimelineOperationDetail[];
|
|
12
|
+
export declare function recordTimelineBranches(internals: EngineInternals, workflowId: string, details: readonly WorkflowTimelineOperationDetail[]): void;
|
|
13
|
+
export declare function recordTimelineSpeculation(internals: EngineInternals, workflowId: string, details: readonly WorkflowTimelineOperationDetail[], outcome: 'committed' | 'rolled-back'): void;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { getTimelineOperationLabel, summarizeTimelineValue } from "./state-utilities.js";
|
|
2
|
+
import {
|
|
3
|
+
MAX_TIMELINE_COORDINATOR_DETAILS,
|
|
4
|
+
MAX_TIMELINE_DETAIL_STRING_LENGTH
|
|
5
|
+
} from "./timeline-coordinator-constants.js";
|
|
6
|
+
function boundedString(value) {
|
|
7
|
+
return value.length <= MAX_TIMELINE_DETAIL_STRING_LENGTH ? value : `${value.slice(0, MAX_TIMELINE_DETAIL_STRING_LENGTH - 1)}\u2026`;
|
|
8
|
+
}
|
|
9
|
+
function boundedErrorSummary(error) {
|
|
10
|
+
return boundedString(summarizeTimelineValue(error));
|
|
11
|
+
}
|
|
12
|
+
export function operationTimelineDetail(operation, index, outcome, options = {}) {
|
|
13
|
+
return {
|
|
14
|
+
index,
|
|
15
|
+
...options.key === void 0 ? {} : { key: boundedString(options.key) },
|
|
16
|
+
operationId: boundedString(operation.operationId),
|
|
17
|
+
operationType: operation.type,
|
|
18
|
+
operationLabel: boundedString(getTimelineOperationLabel(operation)),
|
|
19
|
+
outcome,
|
|
20
|
+
...options.error === void 0 ? {} : { errorSummary: boundedErrorSummary(options.error) }
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function parallelTimelineDetails(operations, slots, branchNames) {
|
|
24
|
+
return operations.map((operation, index) => {
|
|
25
|
+
const slot = slots[index];
|
|
26
|
+
return operationTimelineDetail(operation, index, slot?.status === "fulfilled" ? "fulfilled" : "rejected", {
|
|
27
|
+
...branchNames?.[index] === void 0 ? {} : { key: branchNames[index] },
|
|
28
|
+
...slot?.status === "rejected" ? { error: slot.reason } : {}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function raceTimelineDetails(operations, winnerIndex, branchNames, winnerError) {
|
|
33
|
+
return operations.map((operation, index) => operationTimelineDetail(operation, index, index === winnerIndex ? "won" : "lost", {
|
|
34
|
+
...branchNames?.[index] === void 0 ? {} : { key: branchNames[index] },
|
|
35
|
+
...index === winnerIndex && winnerError !== void 0 ? { error: winnerError } : {}
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
export function runAllTimelineDetails(branchNames, slots, branches) {
|
|
39
|
+
return branchNames.map((key, index) => {
|
|
40
|
+
const slot = slots[index], branchFunction = branches[key]?.[0];
|
|
41
|
+
return {
|
|
42
|
+
index,
|
|
43
|
+
key: boundedString(key),
|
|
44
|
+
operationId: boundedString(slot?.operationId ?? `run-all:${String(index)}:${key}`),
|
|
45
|
+
operationType: "activity",
|
|
46
|
+
operationLabel: boundedString(branchFunction?.name || key),
|
|
47
|
+
outcome: slot?.status === "fulfilled" ? "fulfilled" : "rejected",
|
|
48
|
+
...slot?.status === "rejected" ? { errorSummary: boundedErrorSummary(slot.reason) } : {}
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function boundedDetails(details) {
|
|
53
|
+
return {
|
|
54
|
+
details: details.slice(0, MAX_TIMELINE_COORDINATOR_DETAILS),
|
|
55
|
+
omitted: Math.max(0, details.length - MAX_TIMELINE_COORDINATOR_DETAILS)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export function recordTimelineBranches(internals, workflowId, details) {
|
|
59
|
+
const entry = internals.pendingTimelineEntries?.get(workflowId)?.entry;
|
|
60
|
+
if (entry === void 0)
|
|
61
|
+
return;
|
|
62
|
+
const bounded = boundedDetails(details);
|
|
63
|
+
entry.branches = bounded.details;
|
|
64
|
+
if (bounded.omitted > 0)
|
|
65
|
+
entry.branchesOmitted = bounded.omitted;
|
|
66
|
+
}
|
|
67
|
+
export function recordTimelineSpeculation(internals, workflowId, details, outcome) {
|
|
68
|
+
const entry = internals.pendingTimelineEntries?.get(workflowId)?.entry;
|
|
69
|
+
if (entry === void 0)
|
|
70
|
+
return;
|
|
71
|
+
const bounded = boundedDetails(details);
|
|
72
|
+
entry.children = bounded.details;
|
|
73
|
+
if (bounded.omitted > 0)
|
|
74
|
+
entry.childrenOmitted = bounded.omitted;
|
|
75
|
+
entry.speculationOutcome = outcome;
|
|
76
|
+
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import type { ScheduleOptions, ScheduleOverlapPolicy } from '../../types.ts';
|
|
1
|
+
import type { ScheduleOptions, ScheduleOverlapPolicy, ScheduleUpdateOptions } from '../../types.ts';
|
|
2
2
|
export declare const SCHEDULE_OVERLAP_POLICIES: Set<ScheduleOverlapPolicy>;
|
|
3
3
|
type NormalizedScheduleOptions = Required<Pick<ScheduleOptions, 'overlap' | 'backfill'>> & {
|
|
4
4
|
id?: string;
|
|
5
5
|
description?: string;
|
|
6
6
|
jitterMs?: number;
|
|
7
7
|
};
|
|
8
|
+
type NormalizedScheduleUpdateOptions = {
|
|
9
|
+
description?: string;
|
|
10
|
+
overlap?: ScheduleOverlapPolicy;
|
|
11
|
+
backfill?: boolean;
|
|
12
|
+
jitterMs?: number;
|
|
13
|
+
};
|
|
8
14
|
export declare function normalizeScheduleOptions(options: ScheduleOptions | undefined): NormalizedScheduleOptions;
|
|
15
|
+
export declare function normalizeScheduleUpdateOptions(options: ScheduleUpdateOptions | undefined): NormalizedScheduleUpdateOptions;
|
|
9
16
|
export {};
|
|
@@ -23,6 +23,24 @@ export function normalizeScheduleOptions(options) {
|
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
export function normalizeScheduleUpdateOptions(options) {
|
|
27
|
+
if (options === void 0)
|
|
28
|
+
return {};
|
|
29
|
+
if (typeof options !== "object" || options === null)
|
|
30
|
+
throw Error("options must be an object when provided");
|
|
31
|
+
return {
|
|
32
|
+
...options.description !== void 0 && {
|
|
33
|
+
description: normalizeScheduleDescription(options.description)
|
|
34
|
+
},
|
|
35
|
+
...options.overlap !== void 0 && { overlap: normalizeScheduleOverlap(options.overlap) },
|
|
36
|
+
...options.backfill !== void 0 && {
|
|
37
|
+
backfill: normalizeScheduleBackfill(options.backfill)
|
|
38
|
+
},
|
|
39
|
+
...options.jitter !== void 0 && {
|
|
40
|
+
jitterMs: normalizeScheduleJitter(options.jitter, "options.jitter")
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
26
44
|
function normalizeScheduleDescription(description) {
|
|
27
45
|
if (typeof description !== "string")
|
|
28
46
|
throw Error("options.description must be a string when provided");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ScheduleFilter, ScheduleOverlapPolicy, ScheduleSpec, ScheduleState, ScheduleStatus } from '../../types.ts';
|
|
2
|
-
export { normalizeScheduleOptions, SCHEDULE_OVERLAP_POLICIES } from './schedule-options.ts';
|
|
2
|
+
export { normalizeScheduleOptions, normalizeScheduleUpdateOptions, SCHEDULE_OVERLAP_POLICIES, } from './schedule-options.ts';
|
|
3
3
|
export declare const SCHEDULE_STATUSES: Set<ScheduleStatus>;
|
|
4
4
|
export declare function isValidScheduleTimestamp(value: unknown): value is number;
|
|
5
5
|
export declare function isValidScheduleStatus(value: unknown): value is ScheduleStatus;
|
|
@@ -4,7 +4,11 @@ import { parseCronExpression } from "../../schedule.js";
|
|
|
4
4
|
import { parseDuration } from "../../scheduler.js";
|
|
5
5
|
import { coerceStartWorkflowId } from "../../start-workflow-validation.js";
|
|
6
6
|
import { SCHEDULE_OVERLAP_POLICIES } from "./schedule-options.js";
|
|
7
|
-
export {
|
|
7
|
+
export {
|
|
8
|
+
normalizeScheduleOptions,
|
|
9
|
+
normalizeScheduleUpdateOptions,
|
|
10
|
+
SCHEDULE_OVERLAP_POLICIES
|
|
11
|
+
} from "./schedule-options.js";
|
|
8
12
|
export const SCHEDULE_STATUSES = new Set(["active", "paused", "cancelled"]);
|
|
9
13
|
export function isValidScheduleTimestamp(value) {
|
|
10
14
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
@@ -206,11 +210,29 @@ function decodeScheduleCurrentWorkflowId(decoded, scheduleId) {
|
|
|
206
210
|
}
|
|
207
211
|
function decodeScheduleQueuedRuns(decoded, scheduleId) {
|
|
208
212
|
const queuedRuns = decoded.queuedRuns;
|
|
209
|
-
if (
|
|
213
|
+
if (!Array.isArray(queuedRuns)) {
|
|
210
214
|
rejectInvalidScheduleRecord(scheduleId, "with invalid queuedRuns");
|
|
211
215
|
return null;
|
|
212
216
|
}
|
|
213
|
-
|
|
217
|
+
const result = [], workflowIds = new Set;
|
|
218
|
+
for (const queuedRun of queuedRuns) {
|
|
219
|
+
if (!isRecord(queuedRun)) {
|
|
220
|
+
rejectInvalidScheduleRecord(scheduleId, "with invalid queuedRuns");
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const { workflowId, queuedAt, occurrence } = queuedRun;
|
|
224
|
+
if (!isValidScheduleIdentifier(workflowId) || !isValidScheduleTimestamp(queuedAt) || occurrence !== void 0 && !isValidScheduleTimestamp(occurrence)) {
|
|
225
|
+
rejectInvalidScheduleRecord(scheduleId, "with invalid queuedRuns");
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
if (workflowIds.has(workflowId)) {
|
|
229
|
+
rejectInvalidScheduleRecord(scheduleId, "with duplicate queued workflow ids");
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
workflowIds.add(workflowId);
|
|
233
|
+
result.push({ workflowId, queuedAt, ...occurrence !== void 0 && { occurrence } });
|
|
234
|
+
}
|
|
235
|
+
return result;
|
|
214
236
|
}
|
|
215
237
|
function decodeScheduleMissedFireCount(decoded, scheduleId) {
|
|
216
238
|
const missedFireCount = decoded.missedFireCount;
|
|
@@ -263,6 +285,8 @@ export function decodeScheduleRuntimeFields(decoded, scheduleId) {
|
|
|
263
285
|
const queueFields = decodeScheduleQueueFields(decoded, scheduleId);
|
|
264
286
|
if (queueFields === null)
|
|
265
287
|
return null;
|
|
288
|
+
if (currentWorkflow.value !== void 0 && queueFields.queuedRuns.some((queuedRun) => queuedRun.workflowId === currentWorkflow.value))
|
|
289
|
+
return rejectInvalidScheduleRecord(scheduleId, "whose current workflow is also queued");
|
|
266
290
|
return {
|
|
267
291
|
...optionFields,
|
|
268
292
|
...timestamps,
|
|
@@ -4,6 +4,10 @@ import { normalizeFailureCategory } from "../failure-categories.js";
|
|
|
4
4
|
import { coerceStartWorkflowId, parseStartWorkflowDuration } from "../start-workflow-validation.js";
|
|
5
5
|
import { DEFAULT_WORKFLOW_VERSION } from "../versioning.js";
|
|
6
6
|
import { isWorkflowTagArray } from "../workflow-tags.js";
|
|
7
|
+
import {
|
|
8
|
+
MAX_TIMELINE_COORDINATOR_DETAILS,
|
|
9
|
+
MAX_TIMELINE_DETAIL_STRING_LENGTH
|
|
10
|
+
} from "./timeline-coordinator-constants.js";
|
|
7
11
|
const WORKFLOW_TIMELINE_STATUSES = new Set([
|
|
8
12
|
"running",
|
|
9
13
|
"completed",
|
|
@@ -28,12 +32,15 @@ const WORKFLOW_STATE_FIELD_NAMES = new Set(defineCompleteWorkflowStateFieldNames
|
|
|
28
32
|
"versionTuple",
|
|
29
33
|
"workflowExecutionToken",
|
|
30
34
|
"executionStateOwnerId",
|
|
35
|
+
"parentWorkflowId",
|
|
36
|
+
"parentWorkflowExecutionToken",
|
|
31
37
|
"createdAt",
|
|
32
38
|
"startedAt",
|
|
33
39
|
"updatedAt",
|
|
34
40
|
"terminalCleanupToken",
|
|
35
41
|
"executionDeadline",
|
|
36
|
-
"forkedFrom"
|
|
42
|
+
"forkedFrom",
|
|
43
|
+
"restartedFrom"
|
|
37
44
|
]));
|
|
38
45
|
export function isSanitizedSearchAttributeValue(value) {
|
|
39
46
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
@@ -53,6 +60,30 @@ export function isFiniteNumber(value) {
|
|
|
53
60
|
export function isTimelineStep(value) {
|
|
54
61
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
55
62
|
}
|
|
63
|
+
const WORKFLOW_TIMELINE_DETAIL_OUTCOMES = new Set(["fulfilled", "rejected", "won", "lost"]);
|
|
64
|
+
function isBoundedTimelineDetailString(value) {
|
|
65
|
+
return typeof value === "string" && value.length <= MAX_TIMELINE_DETAIL_STRING_LENGTH;
|
|
66
|
+
}
|
|
67
|
+
const TIMELINE_DETAIL_FIELD_CHECKS = [
|
|
68
|
+
(detail) => typeof detail.index === "number" && Number.isSafeInteger(detail.index) && detail.index >= 0,
|
|
69
|
+
(detail) => detail.key === void 0 || isBoundedTimelineDetailString(detail.key),
|
|
70
|
+
(detail) => isBoundedTimelineDetailString(detail.operationId),
|
|
71
|
+
(detail) => isBoundedTimelineDetailString(detail.operationType),
|
|
72
|
+
(detail) => isBoundedTimelineDetailString(detail.operationLabel),
|
|
73
|
+
(detail) => WORKFLOW_TIMELINE_DETAIL_OUTCOMES.has(detail.outcome),
|
|
74
|
+
(detail) => detail.errorSummary === void 0 || isBoundedTimelineDetailString(detail.errorSummary)
|
|
75
|
+
];
|
|
76
|
+
function isWorkflowTimelineOperationDetail(value) {
|
|
77
|
+
if (!isRecord(value))
|
|
78
|
+
return !1;
|
|
79
|
+
return TIMELINE_DETAIL_FIELD_CHECKS.every((check) => check(value));
|
|
80
|
+
}
|
|
81
|
+
function isWorkflowTimelineOperationDetails(value) {
|
|
82
|
+
return value === void 0 || Array.isArray(value) && value.length <= MAX_TIMELINE_COORDINATOR_DETAILS && value.every(isWorkflowTimelineOperationDetail);
|
|
83
|
+
}
|
|
84
|
+
function isOmittedTimelineDetailCount(value) {
|
|
85
|
+
return value === void 0 || typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
86
|
+
}
|
|
56
87
|
const TIMELINE_ENTRY_FIELD_CHECKS = [
|
|
57
88
|
(entry) => isTimelineStep(entry.step),
|
|
58
89
|
(entry) => typeof entry.operationType === "string",
|
|
@@ -62,7 +93,12 @@ const TIMELINE_ENTRY_FIELD_CHECKS = [
|
|
|
62
93
|
(entry) => WORKFLOW_TIMELINE_STATUSES.has(entry.status),
|
|
63
94
|
(entry) => entry.outputSummary === void 0 || typeof entry.outputSummary === "string",
|
|
64
95
|
(entry) => entry.duration === void 0 || isFiniteNumber(entry.duration),
|
|
65
|
-
(entry) => entry.versionTuple === void 0 || isWorkflowVersionTuple(entry.versionTuple)
|
|
96
|
+
(entry) => entry.versionTuple === void 0 || isWorkflowVersionTuple(entry.versionTuple),
|
|
97
|
+
(entry) => isWorkflowTimelineOperationDetails(entry.branches),
|
|
98
|
+
(entry) => isOmittedTimelineDetailCount(entry.branchesOmitted),
|
|
99
|
+
(entry) => isWorkflowTimelineOperationDetails(entry.children),
|
|
100
|
+
(entry) => isOmittedTimelineDetailCount(entry.childrenOmitted),
|
|
101
|
+
(entry) => entry.speculationOutcome === void 0 || entry.speculationOutcome === "committed" || entry.speculationOutcome === "rolled-back"
|
|
66
102
|
];
|
|
67
103
|
export function isWorkflowTimelineEntry(value) {
|
|
68
104
|
if (!isRecord(value))
|
|
@@ -102,8 +138,36 @@ export function decodeWorkflowState(bytes) {
|
|
|
102
138
|
console.warn(`[weft] Decoded workflow state for "${String(state.id)}" has an invalid executionStateOwnerId field; falling back to the workflow id as the execution owner. This usually indicates corruption or tampering of the storage record.`);
|
|
103
139
|
delete state.executionStateOwnerId;
|
|
104
140
|
}
|
|
141
|
+
sanitizeDecodedParentLineage(state);
|
|
142
|
+
sanitizeDecodedRestartLineage(state);
|
|
105
143
|
return decodedRecord === void 0 ? state : stripUnknownWorkflowStateFields(state, decodedRecord);
|
|
106
144
|
}
|
|
145
|
+
function sanitizeDecodedParentLineage(state) {
|
|
146
|
+
if (state.parentWorkflowId !== void 0)
|
|
147
|
+
try {
|
|
148
|
+
coerceStartWorkflowId(state.parentWorkflowId, "parentWorkflowId");
|
|
149
|
+
} catch {
|
|
150
|
+
delete state.parentWorkflowId;
|
|
151
|
+
}
|
|
152
|
+
if (typeof state.parentWorkflowExecutionToken !== "string" || state.parentWorkflowExecutionToken.length === 0 || state.parentWorkflowId === void 0)
|
|
153
|
+
delete state.parentWorkflowExecutionToken;
|
|
154
|
+
}
|
|
155
|
+
function sanitizeDecodedRestartLineage(state) {
|
|
156
|
+
const restartedFrom = state.restartedFrom;
|
|
157
|
+
if (!isRecord(restartedFrom)) {
|
|
158
|
+
delete state.restartedFrom;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
coerceStartWorkflowId(restartedFrom.workflowId, "restartedFrom.workflowId");
|
|
163
|
+
} catch {
|
|
164
|
+
delete state.restartedFrom;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const { workflowExecutionToken, replacedAt } = restartedFrom;
|
|
168
|
+
if (workflowExecutionToken !== void 0 && (typeof workflowExecutionToken !== "string" || workflowExecutionToken.length === 0) || typeof replacedAt !== "number" || !Number.isSafeInteger(replacedAt) || replacedAt < 0)
|
|
169
|
+
delete state.restartedFrom;
|
|
170
|
+
}
|
|
107
171
|
function stripUnknownWorkflowStateFields(state, stateFields) {
|
|
108
172
|
for (const fieldName of Object.keys(stateFields))
|
|
109
173
|
if (!WORKFLOW_STATE_FIELD_NAMES.has(fieldName))
|
|
@@ -2,8 +2,12 @@ import type { AttributeFilter, ListFilter, WorkflowState } from '../types.ts';
|
|
|
2
2
|
import type { EngineInternals } from './internals.ts';
|
|
3
3
|
/** Stream decoded workflow states that match a list filter. */
|
|
4
4
|
export declare function streamMatchingWorkflowStates(internals: EngineInternals, filter?: ListFilter): AsyncGenerator<WorkflowState>;
|
|
5
|
-
/** Resolve
|
|
5
|
+
/** Resolve indexed workflow IDs implied by lineage, schedule, tag, and search-attribute filters. */
|
|
6
6
|
export declare function resolveConstrainedIds(internals: EngineInternals, filter: ListFilter | undefined, normalizedTagFilters: readonly string[] | undefined): Promise<Set<string> | null>;
|
|
7
7
|
/** Query a single search-attribute index filter and return matching workflow IDs. */
|
|
8
8
|
export declare function queryAttributeIndex(internals: EngineInternals, filter: AttributeFilter): Promise<Set<string>>;
|
|
9
|
+
/** Resolve the durable reverse index from one concrete parent run to its direct children. */
|
|
10
|
+
export declare function queryChildWorkflowIndex(internals: EngineInternals, parentWorkflowId: string, parentWorkflowExecutionToken: string | undefined): Promise<Set<string>>;
|
|
11
|
+
/** Resolve the durable reverse index from a schedule id to launched workflow ids. */
|
|
12
|
+
export declare function queryScheduleRunIndex(internals: EngineInternals, scheduleId: string): Promise<Set<string>>;
|
|
9
13
|
export declare function isTopLevelWorkflowStateKey(key: string): boolean;
|