@aikirun/workflow 0.16.0 → 0.18.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 -0
- package/dist/index.d.ts +30 -18
- package/dist/index.js +245 -148
- package/package.json +2 -2
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { WorkflowName, WorkflowVersionId } from '@aikirun/types/workflow';
|
|
2
2
|
import { Client, Logger, ApiClient } from '@aikirun/types/client';
|
|
3
3
|
import { INTERNAL } from '@aikirun/types/symbols';
|
|
4
|
-
import { WorkflowRun, TerminalWorkflowRunStatus, WorkflowRunState, WorkflowRunId, WorkflowStartOptions, WorkflowDefinitionOptions } from '@aikirun/types/workflow-run';
|
|
4
|
+
import { WorkflowRun, TerminalWorkflowRunStatus, WorkflowRunState, WorkflowRunId, WorkflowRunAddress, ChildWorkflowRunInfo, WorkflowStartOptions, WorkflowDefinitionOptions } from '@aikirun/types/workflow-run';
|
|
5
5
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
6
6
|
import { DurationObject, Duration } from '@aikirun/types/duration';
|
|
7
7
|
import { SleepResult } from '@aikirun/types/sleep';
|
|
8
|
-
import { EventSendOptions, EventWaitOptions,
|
|
8
|
+
import { EventSendOptions, EventWaitOptions, EventWaitResult } from '@aikirun/types/event';
|
|
9
9
|
import { Serializable } from '@aikirun/types/serializable';
|
|
10
10
|
import { DistributiveOmit, RequireAtLeastOneProp } from '@aikirun/types/utils';
|
|
11
|
-
import {
|
|
11
|
+
import { TaskInfo, TaskAddress } from '@aikirun/types/task';
|
|
12
12
|
import { WorkflowRunStateRequest, WorkflowRunTransitionTaskStateRequestV1 } from '@aikirun/types/workflow-run-api';
|
|
13
|
-
import {
|
|
13
|
+
import { ScheduleOverlapPolicy, ScheduleActivateOptions, ScheduleId } from '@aikirun/types/schedule';
|
|
14
14
|
|
|
15
15
|
type NonEmptyArray<T> = [T, ...T[]];
|
|
16
16
|
|
|
@@ -19,10 +19,7 @@ type IsSubtype<SubT, SuperT> = SubT extends SuperT ? true : false;
|
|
|
19
19
|
type And<T extends NonEmptyArray<boolean>> = T extends [infer First, ...infer Rest] ? false extends First ? false : Rest extends NonEmptyArray<boolean> ? And<Rest> : true : never;
|
|
20
20
|
type Or<T extends NonEmptyArray<boolean>> = T extends [infer First, ...infer Rest] ? true extends First ? true : Rest extends NonEmptyArray<boolean> ? Or<Rest> : false : never;
|
|
21
21
|
type PathFromObject<T, IncludeArrayKeys extends boolean = false> = T extends T ? PathFromObjectInternal<T, IncludeArrayKeys> : never;
|
|
22
|
-
type PathFromObjectInternal<T, IncludeArrayKeys extends boolean> = And<[
|
|
23
|
-
IsSubtype<T, object>,
|
|
24
|
-
Or<[IncludeArrayKeys, NonArrayObject<T> extends never ? false : true]>
|
|
25
|
-
]> extends true ? {
|
|
22
|
+
type PathFromObjectInternal<T, IncludeArrayKeys extends boolean> = And<[IsSubtype<T, object>, Or<[IncludeArrayKeys, NonArrayObject<T> extends never ? false : true]>]> extends true ? {
|
|
26
23
|
[K in Exclude<keyof T, symbol>]-?: And<[
|
|
27
24
|
IsSubtype<NonNullable<T[K]>, object>,
|
|
28
25
|
Or<[IncludeArrayKeys, NonArrayObject<NonNullable<T[K]>> extends never ? false : true]>
|
|
@@ -93,9 +90,7 @@ interface WorkflowRunHandle<Input, Output, AppContext, TEvents extends EventsDef
|
|
|
93
90
|
[INTERNAL]: {
|
|
94
91
|
client: Client<AppContext>;
|
|
95
92
|
transitionState: (state: WorkflowRunStateRequest) => Promise<void>;
|
|
96
|
-
transitionTaskState: (request: DistributiveOmit<WorkflowRunTransitionTaskStateRequestV1, "id" | "
|
|
97
|
-
taskId: TaskId;
|
|
98
|
-
}>;
|
|
93
|
+
transitionTaskState: (request: DistributiveOmit<WorkflowRunTransitionTaskStateRequestV1, "id" | "expectedWorkflowRunRevision">) => Promise<TaskInfo>;
|
|
99
94
|
assertExecutionAllowed: () => void;
|
|
100
95
|
};
|
|
101
96
|
}
|
|
@@ -151,8 +146,8 @@ type EventWaiters<TEvents extends EventsDefinition> = {
|
|
|
151
146
|
[K in keyof TEvents]: EventWaiter<EventData<TEvents[K]>>;
|
|
152
147
|
};
|
|
153
148
|
interface EventWaiter<Data> {
|
|
154
|
-
wait(options?: EventWaitOptions<false>): Promise<
|
|
155
|
-
wait(options: EventWaitOptions<true>): Promise<
|
|
149
|
+
wait(options?: EventWaitOptions<false>): Promise<EventWaitResult<Data, false>>;
|
|
150
|
+
wait(options: EventWaitOptions<true>): Promise<EventWaitResult<Data, true>>;
|
|
156
151
|
}
|
|
157
152
|
type EventSenders<TEvents extends EventsDefinition> = {
|
|
158
153
|
[K in keyof TEvents]: EventSender<EventData<TEvents[K]>>;
|
|
@@ -171,13 +166,29 @@ type EventMulticasters<TEvents extends EventsDefinition> = {
|
|
|
171
166
|
interface EventMulticaster<Data> {
|
|
172
167
|
with(): EventMulticasterBuilder<Data>;
|
|
173
168
|
send: <AppContext>(client: Client<AppContext>, runId: string | string[], ...args: Data extends void ? [] : [Data]) => Promise<void>;
|
|
169
|
+
sendByReferenceId: <AppContext>(client: Client<AppContext>, referenceId: string | string[], ...args: Data extends void ? [] : [Data]) => Promise<void>;
|
|
174
170
|
}
|
|
175
171
|
interface EventMulticasterBuilder<Data> {
|
|
176
172
|
opt<Path extends PathFromObject<EventSendOptions>>(path: Path, value: TypeOfValueAtPath<EventSendOptions, Path>): EventMulticasterBuilder<Data>;
|
|
177
173
|
send: <AppContext>(client: Client<AppContext>, runId: string | string[], ...args: Data extends void ? [] : [Data]) => Promise<void>;
|
|
174
|
+
sendByReferenceId: <AppContext>(client: Client<AppContext>, referenceId: string | string[], ...args: Data extends void ? [] : [Data]) => Promise<void>;
|
|
178
175
|
}
|
|
179
176
|
declare function createEventWaiters<TEvents extends EventsDefinition>(handle: WorkflowRunHandle<unknown, unknown, unknown, TEvents>, eventsDefinition: TEvents, logger: Logger): EventWaiters<TEvents>;
|
|
180
|
-
declare function createEventSenders<TEvents extends EventsDefinition>(api: ApiClient, workflowRunId: string, eventsDefinition: TEvents, logger: Logger
|
|
177
|
+
declare function createEventSenders<TEvents extends EventsDefinition>(api: ApiClient, workflowRunId: string, eventsDefinition: TEvents, logger: Logger): EventSenders<TEvents>;
|
|
178
|
+
|
|
179
|
+
/** biome-ignore-all lint/style/noNonNullAssertion: Manifest boundaries are tracked, hence, we never exceed array boundaries */
|
|
180
|
+
|
|
181
|
+
interface ReplayManifest {
|
|
182
|
+
consumeNextTask(address: TaskAddress): TaskInfo | undefined;
|
|
183
|
+
consumeNextChildWorkflowRun(address: WorkflowRunAddress): ChildWorkflowRunInfo | undefined;
|
|
184
|
+
hasUnconsumedEntries(): boolean;
|
|
185
|
+
getUnconsumedEntries(): UnconsumedManifestEntries;
|
|
186
|
+
}
|
|
187
|
+
interface UnconsumedManifestEntries {
|
|
188
|
+
taskIds: string[];
|
|
189
|
+
childWorkflowRunIds: string[];
|
|
190
|
+
}
|
|
191
|
+
declare function createReplayManifest(run: WorkflowRun): ReplayManifest;
|
|
181
192
|
|
|
182
193
|
interface WorkflowRunContext<Input, AppContext, TEvents extends EventsDefinition> {
|
|
183
194
|
id: WorkflowRunId;
|
|
@@ -189,6 +200,7 @@ interface WorkflowRunContext<Input, AppContext, TEvents extends EventsDefinition
|
|
|
189
200
|
events: EventWaiters<TEvents>;
|
|
190
201
|
[INTERNAL]: {
|
|
191
202
|
handle: WorkflowRunHandle<Input, unknown, AppContext, TEvents>;
|
|
203
|
+
replayManifest: ReplayManifest;
|
|
192
204
|
options: {
|
|
193
205
|
spinThresholdMs: number;
|
|
194
206
|
};
|
|
@@ -275,7 +287,7 @@ declare class WorkflowVersionImpl<Input, Output, AppContext, TEvents extends Eve
|
|
|
275
287
|
startWithOpts(client: Client<AppContext>, startOpts: WorkflowStartOptions, ...args: Input extends void ? [] : [Input]): Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
|
|
276
288
|
startAsChild(parentRun: WorkflowRunContext<unknown, AppContext, EventsDefinition>, ...args: Input extends void ? [] : [Input]): Promise<ChildWorkflowRunHandle<Input, Output, AppContext, TEvents>>;
|
|
277
289
|
startAsChildWithOpts(parentRun: WorkflowRunContext<unknown, AppContext, EventsDefinition>, startOpts: WorkflowStartOptions, ...args: Input extends void ? [] : [Input]): Promise<ChildWorkflowRunHandle<Input, Output, AppContext, TEvents>>;
|
|
278
|
-
private
|
|
290
|
+
private throwNonDeterminismError;
|
|
279
291
|
getHandleById(client: Client<AppContext>, runId: string): Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
|
|
280
292
|
getHandleByReferenceId(client: Client<AppContext>, referenceId: string): Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
|
|
281
293
|
private handler;
|
|
@@ -309,12 +321,12 @@ interface CronScheduleParams {
|
|
|
309
321
|
type: "cron";
|
|
310
322
|
expression: string;
|
|
311
323
|
timezone?: string;
|
|
312
|
-
overlapPolicy?:
|
|
324
|
+
overlapPolicy?: ScheduleOverlapPolicy;
|
|
313
325
|
}
|
|
314
326
|
interface IntervalScheduleParams {
|
|
315
327
|
type: "interval";
|
|
316
328
|
every: DurationObject;
|
|
317
|
-
overlapPolicy?:
|
|
329
|
+
overlapPolicy?: ScheduleOverlapPolicy;
|
|
318
330
|
}
|
|
319
331
|
type ScheduleParams = CronScheduleParams | IntervalScheduleParams;
|
|
320
332
|
interface ScheduleHandle {
|
|
@@ -393,4 +405,4 @@ interface Workflow {
|
|
|
393
405
|
};
|
|
394
406
|
}
|
|
395
407
|
|
|
396
|
-
export { type EventMulticaster, type EventMulticasters, type EventSender, type EventSenders, type EventWaiter, type EventWaiters, type ScheduleDefinition, type ScheduleHandle, type ScheduleParams, type Workflow, type WorkflowParams, type WorkflowRegistry, type WorkflowRunContext, type WorkflowRunHandle, type WorkflowRunWaitOptions, type WorkflowVersion, WorkflowVersionImpl, type WorkflowVersionParams, createEventSenders, createEventWaiters, createSleeper, event, schedule, workflow, workflowRegistry, workflowRunHandle };
|
|
408
|
+
export { type EventDefinition, type EventMulticaster, type EventMulticasters, type EventSender, type EventSenders, type EventWaiter, type EventWaiters, type ReplayManifest, type ScheduleDefinition, type ScheduleHandle, type ScheduleParams, type Workflow, type WorkflowParams, type WorkflowRegistry, type WorkflowRunContext, type WorkflowRunHandle, type WorkflowRunWaitOptions, type WorkflowVersion, WorkflowVersionImpl, type WorkflowVersionParams, createEventSenders, createEventWaiters, createReplayManifest, createSleeper, event, schedule, workflow, workflowRegistry, workflowRunHandle };
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ var WorkflowRegistryImpl = class {
|
|
|
11
11
|
return this;
|
|
12
12
|
}
|
|
13
13
|
if (workflows.has(workflow2.versionId)) {
|
|
14
|
-
throw new Error(`Workflow "${workflow2.name}
|
|
14
|
+
throw new Error(`Workflow "${workflow2.name}:${workflow2.versionId}" is already registered`);
|
|
15
15
|
}
|
|
16
16
|
workflows.set(workflow2.versionId, workflow2);
|
|
17
17
|
return this;
|
|
@@ -55,7 +55,7 @@ var WorkflowRegistryImpl = class {
|
|
|
55
55
|
|
|
56
56
|
// ../../lib/array/utils.ts
|
|
57
57
|
function isNonEmptyArray(value) {
|
|
58
|
-
return value.length > 0;
|
|
58
|
+
return value !== void 0 && value.length > 0;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// ../../lib/async/delay.ts
|
|
@@ -77,6 +77,9 @@ function delay(ms, options) {
|
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
// ../../lib/crypto/hash.ts
|
|
81
|
+
import { createHash } from "crypto";
|
|
82
|
+
|
|
80
83
|
// ../../lib/json/stable-stringify.ts
|
|
81
84
|
function stableStringify(value) {
|
|
82
85
|
return stringifyValue(value);
|
|
@@ -317,20 +320,20 @@ function createEventWaiters(handle, eventsDefinition, logger) {
|
|
|
317
320
|
return waiters;
|
|
318
321
|
}
|
|
319
322
|
function createEventWaiter(handle, eventName, schema, logger) {
|
|
320
|
-
let
|
|
323
|
+
let nextIndex = 0;
|
|
321
324
|
async function wait(options) {
|
|
322
325
|
await handle.refresh();
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
if (
|
|
326
|
-
|
|
327
|
-
if (
|
|
326
|
+
const eventWaits = handle.run.eventWaitQueues[eventName]?.eventWaits ?? [];
|
|
327
|
+
const existingEventWait = eventWaits[nextIndex];
|
|
328
|
+
if (existingEventWait) {
|
|
329
|
+
nextIndex++;
|
|
330
|
+
if (existingEventWait.status === "timeout") {
|
|
328
331
|
logger.debug("Timed out waiting for event");
|
|
329
332
|
return { timeout: true };
|
|
330
333
|
}
|
|
331
|
-
let data =
|
|
334
|
+
let data = existingEventWait.data;
|
|
332
335
|
if (schema) {
|
|
333
|
-
const schemaValidation = schema["~standard"].validate(
|
|
336
|
+
const schemaValidation = schema["~standard"].validate(existingEventWait.data);
|
|
334
337
|
const schemaValidationResult = schemaValidation instanceof Promise ? await schemaValidation : schemaValidation;
|
|
335
338
|
if (!schemaValidationResult.issues) {
|
|
336
339
|
data = schemaValidationResult.value;
|
|
@@ -370,7 +373,7 @@ function createEventWaiter(handle, eventName, schema, logger) {
|
|
|
370
373
|
}
|
|
371
374
|
return { wait };
|
|
372
375
|
}
|
|
373
|
-
function createEventSenders(api, workflowRunId, eventsDefinition, logger
|
|
376
|
+
function createEventSenders(api, workflowRunId, eventsDefinition, logger) {
|
|
374
377
|
const senders = {};
|
|
375
378
|
for (const [eventName, eventDefinition] of Object.entries(eventsDefinition)) {
|
|
376
379
|
const sender = createEventSender(
|
|
@@ -378,18 +381,17 @@ function createEventSenders(api, workflowRunId, eventsDefinition, logger, onSend
|
|
|
378
381
|
workflowRunId,
|
|
379
382
|
eventName,
|
|
380
383
|
eventDefinition.schema,
|
|
381
|
-
logger.child({ "aiki.eventName": eventName })
|
|
382
|
-
onSend
|
|
384
|
+
logger.child({ "aiki.eventName": eventName })
|
|
383
385
|
);
|
|
384
386
|
senders[eventName] = sender;
|
|
385
387
|
}
|
|
386
388
|
return senders;
|
|
387
389
|
}
|
|
388
|
-
function createEventSender(api, workflowRunId, eventName, schema, logger,
|
|
390
|
+
function createEventSender(api, workflowRunId, eventName, schema, logger, options) {
|
|
389
391
|
const optsOverrider = objectOverrider(options ?? {});
|
|
390
392
|
const createBuilder = (optsBuilder) => ({
|
|
391
393
|
opt: (path, value) => createBuilder(optsBuilder.with(path, value)),
|
|
392
|
-
send: (...args) => createEventSender(api, workflowRunId, eventName, schema, logger,
|
|
394
|
+
send: (...args) => createEventSender(api, workflowRunId, eventName, schema, logger, optsBuilder.build()).send(...args)
|
|
393
395
|
});
|
|
394
396
|
async function send(...args) {
|
|
395
397
|
let data = args[0];
|
|
@@ -402,13 +404,12 @@ function createEventSender(api, workflowRunId, eventName, schema, logger, onSend
|
|
|
402
404
|
}
|
|
403
405
|
data = schemaValidationResult.value;
|
|
404
406
|
}
|
|
405
|
-
|
|
407
|
+
await api.workflowRun.sendEventV1({
|
|
406
408
|
id: workflowRunId,
|
|
407
409
|
eventName,
|
|
408
410
|
data,
|
|
409
411
|
options
|
|
410
412
|
});
|
|
411
|
-
onSend(run);
|
|
412
413
|
logger.info("Sent event to workflow", {
|
|
413
414
|
...options?.reference ? { "aiki.referenceId": options.reference.id } : {}
|
|
414
415
|
});
|
|
@@ -439,6 +440,11 @@ function createEventMulticaster(workflowName, workflowVersionId, eventName, sche
|
|
|
439
440
|
client,
|
|
440
441
|
runId,
|
|
441
442
|
...args
|
|
443
|
+
),
|
|
444
|
+
sendByReferenceId: (client, referenceId, ...args) => createEventMulticaster(workflowName, workflowVersionId, eventName, schema, optsBuilder.build()).sendByReferenceId(
|
|
445
|
+
client,
|
|
446
|
+
referenceId,
|
|
447
|
+
...args
|
|
442
448
|
)
|
|
443
449
|
});
|
|
444
450
|
async function send(client, runId, ...args) {
|
|
@@ -472,12 +478,51 @@ function createEventMulticaster(workflowName, workflowVersionId, eventName, sche
|
|
|
472
478
|
"aiki.workflowVersionId": workflowVersionId,
|
|
473
479
|
"aiki.workflowRunIds": runIds,
|
|
474
480
|
"aiki.eventName": eventName,
|
|
475
|
-
...options?.reference ? { "aiki.
|
|
481
|
+
...options?.reference ? { "aiki.eventReferenceId": options.reference.id } : {}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
async function sendByReferenceId(client, referenceId, ...args) {
|
|
485
|
+
let data = args[0];
|
|
486
|
+
if (schema) {
|
|
487
|
+
const schemaValidation = schema["~standard"].validate(data);
|
|
488
|
+
const schemaValidationResult = schemaValidation instanceof Promise ? await schemaValidation : schemaValidation;
|
|
489
|
+
if (schemaValidationResult.issues) {
|
|
490
|
+
client.logger.error("Invalid event data", {
|
|
491
|
+
"aiki.workflowName": workflowName,
|
|
492
|
+
"aiki.workflowVersionId": workflowVersionId,
|
|
493
|
+
"aiki.eventName": eventName,
|
|
494
|
+
"aiki.issues": schemaValidationResult.issues
|
|
495
|
+
});
|
|
496
|
+
throw new SchemaValidationError("Invalid event data", schemaValidationResult.issues);
|
|
497
|
+
}
|
|
498
|
+
data = schemaValidationResult.value;
|
|
499
|
+
}
|
|
500
|
+
const referenceIds = Array.isArray(referenceId) ? referenceId : [referenceId];
|
|
501
|
+
if (!isNonEmptyArray(referenceIds)) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
await client.api.workflowRun.multicastEventByReferenceV1({
|
|
505
|
+
references: referenceIds.map((referenceId2) => ({
|
|
506
|
+
name: workflowName,
|
|
507
|
+
versionId: workflowVersionId,
|
|
508
|
+
referenceId: referenceId2
|
|
509
|
+
})),
|
|
510
|
+
eventName,
|
|
511
|
+
data,
|
|
512
|
+
options
|
|
513
|
+
});
|
|
514
|
+
client.logger.info("Multicasted event by reference", {
|
|
515
|
+
"aiki.workflowName": workflowName,
|
|
516
|
+
"aiki.workflowVersionId": workflowVersionId,
|
|
517
|
+
"aiki.referenceIds": referenceIds,
|
|
518
|
+
"aiki.eventName": eventName,
|
|
519
|
+
...options?.reference ? { "aiki.eventReferenceId": options.reference.id } : {}
|
|
476
520
|
});
|
|
477
521
|
}
|
|
478
522
|
return {
|
|
479
523
|
with: () => createBuilder(optsOverrider()),
|
|
480
|
-
send
|
|
524
|
+
send,
|
|
525
|
+
sendByReferenceId
|
|
481
526
|
};
|
|
482
527
|
}
|
|
483
528
|
|
|
@@ -506,9 +551,7 @@ var WorkflowRunHandleImpl = class {
|
|
|
506
551
|
this._run = _run;
|
|
507
552
|
this.logger = logger;
|
|
508
553
|
this.api = client.api;
|
|
509
|
-
this.events = createEventSenders(client.api, this._run.id, eventsDefinition, this.logger
|
|
510
|
-
this._run = run;
|
|
511
|
-
});
|
|
554
|
+
this.events = createEventSenders(client.api, this._run.id, eventsDefinition, this.logger);
|
|
512
555
|
this[INTERNAL2] = {
|
|
513
556
|
client,
|
|
514
557
|
transitionState: this.transitionState.bind(this),
|
|
@@ -604,24 +647,26 @@ var WorkflowRunHandleImpl = class {
|
|
|
604
647
|
}
|
|
605
648
|
async transitionState(targetState) {
|
|
606
649
|
try {
|
|
650
|
+
let response;
|
|
607
651
|
if (targetState.status === "scheduled" && (targetState.reason === "new" || targetState.reason === "resume" || targetState.reason === "awake_early") || targetState.status === "paused" || targetState.status === "cancelled") {
|
|
608
|
-
|
|
652
|
+
response = await this.api.workflowRun.transitionStateV1({
|
|
609
653
|
type: "pessimistic",
|
|
610
654
|
id: this.run.id,
|
|
611
655
|
state: targetState
|
|
612
656
|
});
|
|
613
|
-
|
|
614
|
-
|
|
657
|
+
} else {
|
|
658
|
+
response = await this.api.workflowRun.transitionStateV1({
|
|
659
|
+
type: "optimistic",
|
|
660
|
+
id: this.run.id,
|
|
661
|
+
state: targetState,
|
|
662
|
+
expectedRevision: this.run.revision
|
|
663
|
+
});
|
|
615
664
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
state: targetState,
|
|
620
|
-
expectedRevision: this.run.revision
|
|
621
|
-
});
|
|
622
|
-
this._run = run;
|
|
665
|
+
this._run.revision = response.revision;
|
|
666
|
+
this._run.state = response.state;
|
|
667
|
+
this._run.attempts = response.attempts;
|
|
623
668
|
} catch (error) {
|
|
624
|
-
if (
|
|
669
|
+
if (isWorkflowRunRevisionConflictError(error)) {
|
|
625
670
|
throw new WorkflowRunRevisionConflictError2(this.run.id);
|
|
626
671
|
}
|
|
627
672
|
throw error;
|
|
@@ -629,15 +674,14 @@ var WorkflowRunHandleImpl = class {
|
|
|
629
674
|
}
|
|
630
675
|
async transitionTaskState(request) {
|
|
631
676
|
try {
|
|
632
|
-
const {
|
|
677
|
+
const { taskInfo } = await this.api.workflowRun.transitionTaskStateV1({
|
|
633
678
|
...request,
|
|
634
679
|
id: this.run.id,
|
|
635
|
-
|
|
680
|
+
expectedWorkflowRunRevision: this.run.revision
|
|
636
681
|
});
|
|
637
|
-
|
|
638
|
-
return { taskId };
|
|
682
|
+
return taskInfo;
|
|
639
683
|
} catch (error) {
|
|
640
|
-
if (
|
|
684
|
+
if (isWorkflowRunRevisionConflictError(error)) {
|
|
641
685
|
throw new WorkflowRunRevisionConflictError2(this.run.id);
|
|
642
686
|
}
|
|
643
687
|
throw error;
|
|
@@ -650,8 +694,73 @@ var WorkflowRunHandleImpl = class {
|
|
|
650
694
|
}
|
|
651
695
|
}
|
|
652
696
|
};
|
|
653
|
-
function
|
|
654
|
-
return error != null && typeof error === "object" && "code" in error && error.code === "
|
|
697
|
+
function isWorkflowRunRevisionConflictError(error) {
|
|
698
|
+
return error != null && typeof error === "object" && "code" in error && error.code === "WORKFLOW_RUN_REVISION_CONFLICT";
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// run/replay-manifest.ts
|
|
702
|
+
function createReplayManifest(run) {
|
|
703
|
+
const { taskQueues, childWorkflowRunQueues } = run;
|
|
704
|
+
let totalEntries = 0;
|
|
705
|
+
const taskCountByAddress = {};
|
|
706
|
+
const childWorkflowRunCountByAddress = {};
|
|
707
|
+
for (const [address, queue] of Object.entries(taskQueues)) {
|
|
708
|
+
taskCountByAddress[address] = queue.tasks.length;
|
|
709
|
+
totalEntries += queue.tasks.length;
|
|
710
|
+
}
|
|
711
|
+
for (const [address, queue] of Object.entries(childWorkflowRunQueues)) {
|
|
712
|
+
childWorkflowRunCountByAddress[address] = queue.childWorkflowRuns.length;
|
|
713
|
+
totalEntries += queue.childWorkflowRuns.length;
|
|
714
|
+
}
|
|
715
|
+
const nextTaskIndexByAddress = {};
|
|
716
|
+
const nextChildWorkflowRunIndexByAddress = {};
|
|
717
|
+
let consumedEntries = 0;
|
|
718
|
+
return {
|
|
719
|
+
consumeNextTask(address) {
|
|
720
|
+
const taskCount = taskCountByAddress[address] ?? 0;
|
|
721
|
+
const nextIndex = nextTaskIndexByAddress[address] ?? 0;
|
|
722
|
+
if (nextIndex >= taskCount) {
|
|
723
|
+
return void 0;
|
|
724
|
+
}
|
|
725
|
+
const task = taskQueues[address].tasks[nextIndex];
|
|
726
|
+
nextTaskIndexByAddress[address] = nextIndex + 1;
|
|
727
|
+
consumedEntries++;
|
|
728
|
+
return task;
|
|
729
|
+
},
|
|
730
|
+
consumeNextChildWorkflowRun(address) {
|
|
731
|
+
const childWorkflowRunCount = childWorkflowRunCountByAddress[address] ?? 0;
|
|
732
|
+
const nextIndex = nextChildWorkflowRunIndexByAddress[address] ?? 0;
|
|
733
|
+
if (nextIndex >= childWorkflowRunCount) {
|
|
734
|
+
return void 0;
|
|
735
|
+
}
|
|
736
|
+
const childWorkflowRun = childWorkflowRunQueues[address].childWorkflowRuns[nextIndex];
|
|
737
|
+
nextChildWorkflowRunIndexByAddress[address] = nextIndex + 1;
|
|
738
|
+
consumedEntries++;
|
|
739
|
+
return childWorkflowRun;
|
|
740
|
+
},
|
|
741
|
+
hasUnconsumedEntries() {
|
|
742
|
+
return consumedEntries < totalEntries;
|
|
743
|
+
},
|
|
744
|
+
getUnconsumedEntries() {
|
|
745
|
+
const taskIds = [];
|
|
746
|
+
const childWorkflowRunIds = [];
|
|
747
|
+
for (const [address, taskCount] of Object.entries(taskCountByAddress)) {
|
|
748
|
+
const tasks = taskQueues[address].tasks;
|
|
749
|
+
const nextIndex = nextTaskIndexByAddress[address] ?? 0;
|
|
750
|
+
for (let i = nextIndex; i < taskCount; i++) {
|
|
751
|
+
taskIds.push(tasks[i].id);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
for (const [address, childWorkflowRunCount] of Object.entries(childWorkflowRunCountByAddress)) {
|
|
755
|
+
const childWorkflowRuns = childWorkflowRunQueues[address].childWorkflowRuns;
|
|
756
|
+
const nextIndex = nextChildWorkflowRunIndexByAddress[address] ?? 0;
|
|
757
|
+
for (let i = nextIndex; i < childWorkflowRunCount; i++) {
|
|
758
|
+
childWorkflowRunIds.push(childWorkflowRuns[i].id);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return { taskIds, childWorkflowRunIds };
|
|
762
|
+
}
|
|
763
|
+
};
|
|
655
764
|
}
|
|
656
765
|
|
|
657
766
|
// run/sleeper.ts
|
|
@@ -663,17 +772,17 @@ import {
|
|
|
663
772
|
var MAX_SLEEP_YEARS = 10;
|
|
664
773
|
var MAX_SLEEP_MS = MAX_SLEEP_YEARS * 365 * 24 * 60 * 60 * 1e3;
|
|
665
774
|
function createSleeper(handle, logger) {
|
|
666
|
-
const
|
|
775
|
+
const nextIndexBySleepName = {};
|
|
667
776
|
return async (name, duration) => {
|
|
668
777
|
const sleepName = name;
|
|
669
778
|
let durationMs = toMilliseconds(duration);
|
|
670
779
|
if (durationMs > MAX_SLEEP_MS) {
|
|
671
780
|
throw new Error(`Sleep duration ${durationMs}ms exceeds maximum of ${MAX_SLEEP_YEARS} years`);
|
|
672
781
|
}
|
|
673
|
-
const
|
|
674
|
-
const sleepQueue = handle.run.
|
|
675
|
-
const
|
|
676
|
-
if (!
|
|
782
|
+
const nextIndex = nextIndexBySleepName[sleepName] ?? 0;
|
|
783
|
+
const sleepQueue = handle.run.sleepQueues[sleepName] ?? { sleeps: [] };
|
|
784
|
+
const existingSleep = sleepQueue.sleeps[nextIndex];
|
|
785
|
+
if (!existingSleep) {
|
|
677
786
|
try {
|
|
678
787
|
await handle[INTERNAL3].transitionState({ status: "sleeping", sleepName, durationMs });
|
|
679
788
|
logger.info("Going to sleep", {
|
|
@@ -688,37 +797,37 @@ function createSleeper(handle, logger) {
|
|
|
688
797
|
}
|
|
689
798
|
throw new WorkflowRunSuspendedError2(handle.run.id);
|
|
690
799
|
}
|
|
691
|
-
if (
|
|
800
|
+
if (existingSleep.status === "sleeping") {
|
|
692
801
|
logger.debug("Already sleeping", {
|
|
693
802
|
"aiki.sleepName": sleepName,
|
|
694
|
-
"aiki.awakeAt":
|
|
803
|
+
"aiki.awakeAt": existingSleep.awakeAt
|
|
695
804
|
});
|
|
696
805
|
throw new WorkflowRunSuspendedError2(handle.run.id);
|
|
697
806
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
if (
|
|
807
|
+
existingSleep.status;
|
|
808
|
+
nextIndexBySleepName[sleepName] = nextIndex + 1;
|
|
809
|
+
if (existingSleep.status === "cancelled") {
|
|
701
810
|
logger.debug("Sleep cancelled", {
|
|
702
811
|
"aiki.sleepName": sleepName,
|
|
703
|
-
"aiki.cancelledAt":
|
|
812
|
+
"aiki.cancelledAt": existingSleep.cancelledAt
|
|
704
813
|
});
|
|
705
814
|
return { cancelled: true };
|
|
706
815
|
}
|
|
707
|
-
if (durationMs ===
|
|
816
|
+
if (durationMs === existingSleep.durationMs) {
|
|
708
817
|
logger.debug("Sleep completed", {
|
|
709
818
|
"aiki.sleepName": sleepName,
|
|
710
819
|
"aiki.durationMs": durationMs,
|
|
711
|
-
"aiki.completedAt":
|
|
820
|
+
"aiki.completedAt": existingSleep.completedAt
|
|
712
821
|
});
|
|
713
822
|
return { cancelled: false };
|
|
714
823
|
}
|
|
715
|
-
if (durationMs >
|
|
824
|
+
if (durationMs > existingSleep.durationMs) {
|
|
716
825
|
logger.warn("Higher sleep duration encountered during replay. Sleeping for remaining duration", {
|
|
717
826
|
"aiki.sleepName": sleepName,
|
|
718
|
-
"aiki.historicDurationMs":
|
|
827
|
+
"aiki.historicDurationMs": existingSleep.durationMs,
|
|
719
828
|
"aiki.latestDurationMs": durationMs
|
|
720
829
|
});
|
|
721
|
-
durationMs -=
|
|
830
|
+
durationMs -= existingSleep.durationMs;
|
|
722
831
|
} else {
|
|
723
832
|
return { cancelled: false };
|
|
724
833
|
}
|
|
@@ -804,7 +913,7 @@ import { INTERNAL as INTERNAL6 } from "@aikirun/types/symbols";
|
|
|
804
913
|
|
|
805
914
|
// ../../lib/address/index.ts
|
|
806
915
|
function getWorkflowRunAddress(name, versionId, referenceId) {
|
|
807
|
-
return `${name}
|
|
916
|
+
return `${name}:${versionId}:${referenceId}`;
|
|
808
917
|
}
|
|
809
918
|
|
|
810
919
|
// workflow-version.ts
|
|
@@ -812,6 +921,7 @@ import { INTERNAL as INTERNAL5 } from "@aikirun/types/symbols";
|
|
|
812
921
|
import { TaskFailedError } from "@aikirun/types/task";
|
|
813
922
|
import { SchemaValidationError as SchemaValidationError2 } from "@aikirun/types/validator";
|
|
814
923
|
import {
|
|
924
|
+
NonDeterminismError,
|
|
815
925
|
WorkflowRunFailedError as WorkflowRunFailedError2,
|
|
816
926
|
WorkflowRunRevisionConflictError as WorkflowRunRevisionConflictError5,
|
|
817
927
|
WorkflowRunSuspendedError as WorkflowRunSuspendedError4
|
|
@@ -824,13 +934,13 @@ import {
|
|
|
824
934
|
WorkflowRunRevisionConflictError as WorkflowRunRevisionConflictError4,
|
|
825
935
|
WorkflowRunSuspendedError as WorkflowRunSuspendedError3
|
|
826
936
|
} from "@aikirun/types/workflow-run";
|
|
827
|
-
async function childWorkflowRunHandle(client, run, parentRun, logger, eventsDefinition) {
|
|
937
|
+
async function childWorkflowRunHandle(client, run, parentRun, childWorkflowRunWaitQueues, logger, eventsDefinition) {
|
|
828
938
|
const handle = await workflowRunHandle(client, run, eventsDefinition, logger);
|
|
829
939
|
return {
|
|
830
940
|
run: handle.run,
|
|
831
941
|
events: handle.events,
|
|
832
942
|
refresh: handle.refresh.bind(handle),
|
|
833
|
-
waitForStatus: createStatusWaiter(handle, parentRun, logger),
|
|
943
|
+
waitForStatus: createStatusWaiter(handle, parentRun, childWorkflowRunWaitQueues, logger),
|
|
834
944
|
cancel: handle.cancel.bind(handle),
|
|
835
945
|
pause: handle.pause.bind(handle),
|
|
836
946
|
resume: handle.resume.bind(handle),
|
|
@@ -838,15 +948,21 @@ async function childWorkflowRunHandle(client, run, parentRun, logger, eventsDefi
|
|
|
838
948
|
[INTERNAL4]: handle[INTERNAL4]
|
|
839
949
|
};
|
|
840
950
|
}
|
|
841
|
-
function createStatusWaiter(handle, parentRun, logger) {
|
|
842
|
-
|
|
951
|
+
function createStatusWaiter(handle, parentRun, childWorkflowRunWaitQueues, logger) {
|
|
952
|
+
const nextIndexByStatus = {
|
|
953
|
+
cancelled: 0,
|
|
954
|
+
completed: 0,
|
|
955
|
+
failed: 0
|
|
956
|
+
};
|
|
843
957
|
async function waitForStatus(expectedStatus, options) {
|
|
844
958
|
const parentRunHandle = parentRun[INTERNAL4].handle;
|
|
845
|
-
const
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
959
|
+
const nextIndex = nextIndexByStatus[expectedStatus];
|
|
960
|
+
const { run } = handle;
|
|
961
|
+
const childWorkflowRunWaits = childWorkflowRunWaitQueues[expectedStatus].childWorkflowRunWaits;
|
|
962
|
+
const existingChildWorkflowRunWait = childWorkflowRunWaits[nextIndex];
|
|
963
|
+
if (existingChildWorkflowRunWait) {
|
|
964
|
+
nextIndexByStatus[expectedStatus] = nextIndex + 1;
|
|
965
|
+
if (existingChildWorkflowRunWait.status === "timeout") {
|
|
850
966
|
logger.debug("Timed out waiting for child workflow status", {
|
|
851
967
|
"aiki.childWorkflowExpectedStatus": expectedStatus
|
|
852
968
|
});
|
|
@@ -855,43 +971,29 @@ function createStatusWaiter(handle, parentRun, logger) {
|
|
|
855
971
|
cause: "timeout"
|
|
856
972
|
};
|
|
857
973
|
}
|
|
858
|
-
|
|
974
|
+
const childWorkflowRunStatus = existingChildWorkflowRunWait.childWorkflowRunState.status;
|
|
975
|
+
if (childWorkflowRunStatus === expectedStatus) {
|
|
859
976
|
return {
|
|
860
977
|
success: true,
|
|
861
|
-
state:
|
|
978
|
+
state: existingChildWorkflowRunWait.childWorkflowRunState
|
|
862
979
|
};
|
|
863
980
|
}
|
|
864
|
-
if (isTerminalWorkflowRunStatus2(
|
|
981
|
+
if (isTerminalWorkflowRunStatus2(childWorkflowRunStatus)) {
|
|
865
982
|
logger.debug("Child workflow run reached termnial state", {
|
|
866
|
-
"aiki.childWorkflowTerminalStatus":
|
|
983
|
+
"aiki.childWorkflowTerminalStatus": childWorkflowRunStatus
|
|
867
984
|
});
|
|
868
985
|
return {
|
|
869
986
|
success: false,
|
|
870
987
|
cause: "run_terminated"
|
|
871
988
|
};
|
|
872
989
|
}
|
|
873
|
-
|
|
874
|
-
const { state } = handle.run;
|
|
875
|
-
if (state.status === expectedStatus) {
|
|
876
|
-
return {
|
|
877
|
-
success: true,
|
|
878
|
-
state
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
if (isTerminalWorkflowRunStatus2(state.status)) {
|
|
882
|
-
logger.debug("Child workflow run reached termnial state", {
|
|
883
|
-
"aiki.childWorkflowTerminalStatus": state.status
|
|
884
|
-
});
|
|
885
|
-
return {
|
|
886
|
-
success: false,
|
|
887
|
-
cause: "run_terminated"
|
|
888
|
-
};
|
|
990
|
+
childWorkflowRunStatus;
|
|
889
991
|
}
|
|
890
992
|
const timeoutInMs = options?.timeout && toMilliseconds(options.timeout);
|
|
891
993
|
try {
|
|
892
994
|
await parentRunHandle[INTERNAL4].transitionState({
|
|
893
995
|
status: "awaiting_child_workflow",
|
|
894
|
-
childWorkflowRunId:
|
|
996
|
+
childWorkflowRunId: run.id,
|
|
895
997
|
childWorkflowRunStatus: expectedStatus,
|
|
896
998
|
timeoutInMs
|
|
897
999
|
});
|
|
@@ -945,7 +1047,7 @@ var WorkflowVersionImpl = class {
|
|
|
945
1047
|
}
|
|
946
1048
|
input = schemaValidationResult.value;
|
|
947
1049
|
}
|
|
948
|
-
const {
|
|
1050
|
+
const { id } = await client.api.workflowRun.createV1({
|
|
949
1051
|
name: this.name,
|
|
950
1052
|
versionId: this.versionId,
|
|
951
1053
|
input,
|
|
@@ -954,9 +1056,9 @@ var WorkflowVersionImpl = class {
|
|
|
954
1056
|
client.logger.info("Created workflow", {
|
|
955
1057
|
"aiki.workflowName": this.name,
|
|
956
1058
|
"aiki.workflowVersionId": this.versionId,
|
|
957
|
-
"aiki.workflowRunId":
|
|
1059
|
+
"aiki.workflowRunId": id
|
|
958
1060
|
});
|
|
959
|
-
return workflowRunHandle(client,
|
|
1061
|
+
return workflowRunHandle(client, id, this[INTERNAL5].eventsDefinition);
|
|
960
1062
|
}
|
|
961
1063
|
async startAsChild(parentRun, ...args) {
|
|
962
1064
|
return this.startAsChildWithOpts(parentRun, this.params.opts ?? {}, ...args);
|
|
@@ -968,48 +1070,41 @@ var WorkflowVersionImpl = class {
|
|
|
968
1070
|
const inputRaw = args[0];
|
|
969
1071
|
const input = await this.parse(parentRunHandle, this.params.schema?.input, inputRaw, parentRun.logger);
|
|
970
1072
|
const inputHash = await hashInput(input);
|
|
971
|
-
const
|
|
972
|
-
const address = getWorkflowRunAddress(this.name, this.versionId,
|
|
973
|
-
const
|
|
974
|
-
if (
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
existingRunInfo
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1073
|
+
const referenceId = startOpts.reference?.id;
|
|
1074
|
+
const address = getWorkflowRunAddress(this.name, this.versionId, referenceId ?? inputHash);
|
|
1075
|
+
const replayManifest = parentRun[INTERNAL5].replayManifest;
|
|
1076
|
+
if (replayManifest.hasUnconsumedEntries()) {
|
|
1077
|
+
const existingRunInfo = replayManifest.consumeNextChildWorkflowRun(address);
|
|
1078
|
+
if (existingRunInfo) {
|
|
1079
|
+
const { run: existingRun } = await client.api.workflowRun.getByIdV1({ id: existingRunInfo.id });
|
|
1080
|
+
if (existingRun.state.status === "completed") {
|
|
1081
|
+
await this.parse(parentRunHandle, this.params.schema?.output, existingRun.state.output, parentRun.logger);
|
|
1082
|
+
}
|
|
1083
|
+
const logger2 = parentRun.logger.child({
|
|
1084
|
+
"aiki.childWorkflowName": existingRun.name,
|
|
1085
|
+
"aiki.childWorkflowVersionId": existingRun.versionId,
|
|
1086
|
+
"aiki.childWorkflowRunId": existingRun.id
|
|
1087
|
+
});
|
|
1088
|
+
return childWorkflowRunHandle(
|
|
1089
|
+
client,
|
|
1090
|
+
existingRun,
|
|
1091
|
+
parentRun,
|
|
1092
|
+
existingRunInfo.childWorkflowRunWaitQueues,
|
|
1093
|
+
logger2,
|
|
1094
|
+
this[INTERNAL5].eventsDefinition
|
|
1095
|
+
);
|
|
985
1096
|
}
|
|
986
|
-
|
|
987
|
-
"aiki.childWorkflowName": existingRun.name,
|
|
988
|
-
"aiki.childWorkflowVersionId": existingRun.versionId,
|
|
989
|
-
"aiki.childWorkflowRunId": existingRun.id
|
|
990
|
-
});
|
|
991
|
-
return childWorkflowRunHandle(
|
|
992
|
-
client,
|
|
993
|
-
existingRun,
|
|
994
|
-
parentRun,
|
|
995
|
-
logger2,
|
|
996
|
-
this[INTERNAL5].eventsDefinition
|
|
997
|
-
);
|
|
1097
|
+
await this.throwNonDeterminismError(parentRun, parentRunHandle, inputHash, referenceId, replayManifest);
|
|
998
1098
|
}
|
|
999
|
-
const
|
|
1099
|
+
const shard = parentRun.options.shard;
|
|
1100
|
+
const { id: newRunId } = await client.api.workflowRun.createV1({
|
|
1000
1101
|
name: this.name,
|
|
1001
1102
|
versionId: this.versionId,
|
|
1002
1103
|
input,
|
|
1003
1104
|
parentWorkflowRunId: parentRun.id,
|
|
1004
|
-
options: startOpts
|
|
1105
|
+
options: shard === void 0 ? startOpts : { ...startOpts, shard }
|
|
1005
1106
|
});
|
|
1006
|
-
|
|
1007
|
-
id: newRun.id,
|
|
1008
|
-
name: newRun.name,
|
|
1009
|
-
versionId: newRun.versionId,
|
|
1010
|
-
inputHash,
|
|
1011
|
-
statusWaitResults: []
|
|
1012
|
-
};
|
|
1107
|
+
const { run: newRun } = await client.api.workflowRun.getByIdV1({ id: newRunId });
|
|
1013
1108
|
const logger = parentRun.logger.child({
|
|
1014
1109
|
"aiki.childWorkflowName": newRun.name,
|
|
1015
1110
|
"aiki.childWorkflowVersionId": newRun.versionId,
|
|
@@ -1020,32 +1115,33 @@ var WorkflowVersionImpl = class {
|
|
|
1020
1115
|
client,
|
|
1021
1116
|
newRun,
|
|
1022
1117
|
parentRun,
|
|
1118
|
+
{
|
|
1119
|
+
cancelled: { childWorkflowRunWaits: [] },
|
|
1120
|
+
completed: { childWorkflowRunWaits: [] },
|
|
1121
|
+
failed: { childWorkflowRunWaits: [] }
|
|
1122
|
+
},
|
|
1023
1123
|
logger,
|
|
1024
1124
|
this[INTERNAL5].eventsDefinition
|
|
1025
1125
|
);
|
|
1026
1126
|
}
|
|
1027
|
-
async
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
});
|
|
1037
|
-
const error = new WorkflowRunFailedError2(
|
|
1038
|
-
parentRunHandle.run.id,
|
|
1039
|
-
parentRunHandle.run.attempts,
|
|
1040
|
-
`Reference ID "${reference.id}" already used by another child workflow run ${existingRunInfo.id}`
|
|
1041
|
-
);
|
|
1042
|
-
await parentRunHandle[INTERNAL5].transitionState({
|
|
1043
|
-
status: "failed",
|
|
1044
|
-
cause: "self",
|
|
1045
|
-
error: createSerializableError(error)
|
|
1046
|
-
});
|
|
1047
|
-
throw error;
|
|
1127
|
+
async throwNonDeterminismError(parentRun, parentRunHandle, inputHash, referenceId, manifest) {
|
|
1128
|
+
const unconsumedManifestEntries = manifest.getUnconsumedEntries();
|
|
1129
|
+
const logMeta = {
|
|
1130
|
+
"aiki.workflowName": this.name,
|
|
1131
|
+
"aiki.inputHash": inputHash,
|
|
1132
|
+
"aiki.unconsumedManifestEntries": unconsumedManifestEntries
|
|
1133
|
+
};
|
|
1134
|
+
if (referenceId !== void 0) {
|
|
1135
|
+
logMeta["aiki.referenceId"] = referenceId;
|
|
1048
1136
|
}
|
|
1137
|
+
parentRun.logger.error("Replay divergence", logMeta);
|
|
1138
|
+
const error = new NonDeterminismError(parentRun.id, parentRunHandle.run.attempts, unconsumedManifestEntries);
|
|
1139
|
+
await parentRunHandle[INTERNAL5].transitionState({
|
|
1140
|
+
status: "failed",
|
|
1141
|
+
cause: "self",
|
|
1142
|
+
error: createSerializableError(error)
|
|
1143
|
+
});
|
|
1144
|
+
throw error;
|
|
1049
1145
|
}
|
|
1050
1146
|
async getHandleById(client, runId) {
|
|
1051
1147
|
return workflowRunHandle(client, runId, this[INTERNAL5].eventsDefinition);
|
|
@@ -1081,7 +1177,7 @@ var WorkflowVersionImpl = class {
|
|
|
1081
1177
|
const output = await this.parse(handle, this.params.schema?.output, outputRaw, run.logger);
|
|
1082
1178
|
return output;
|
|
1083
1179
|
} catch (error) {
|
|
1084
|
-
if (error instanceof WorkflowRunSuspendedError4 || error instanceof WorkflowRunFailedError2 || error instanceof WorkflowRunRevisionConflictError5) {
|
|
1180
|
+
if (error instanceof WorkflowRunSuspendedError4 || error instanceof WorkflowRunFailedError2 || error instanceof WorkflowRunRevisionConflictError5 || error instanceof NonDeterminismError) {
|
|
1085
1181
|
throw error;
|
|
1086
1182
|
}
|
|
1087
1183
|
const attempts = handle.run.attempts;
|
|
@@ -1211,7 +1307,7 @@ var WorkflowImpl = class {
|
|
|
1211
1307
|
}
|
|
1212
1308
|
v(versionId, params) {
|
|
1213
1309
|
if (this.workflowVersions.has(versionId)) {
|
|
1214
|
-
throw new Error(`Workflow "${this.name}
|
|
1310
|
+
throw new Error(`Workflow "${this.name}:${versionId}" already exists`);
|
|
1215
1311
|
}
|
|
1216
1312
|
const workflowVersion = new WorkflowVersionImpl(this.name, versionId, params);
|
|
1217
1313
|
this.workflowVersions.set(
|
|
@@ -1231,6 +1327,7 @@ export {
|
|
|
1231
1327
|
WorkflowVersionImpl,
|
|
1232
1328
|
createEventSenders,
|
|
1233
1329
|
createEventWaiters,
|
|
1330
|
+
createReplayManifest,
|
|
1234
1331
|
createSleeper,
|
|
1235
1332
|
event,
|
|
1236
1333
|
schedule,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikirun/workflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Workflow SDK for Aiki - define durable workflows with tasks, sleeps, waits, and event handling",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"build": "tsup"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@aikirun/types": "0.
|
|
21
|
+
"@aikirun/types": "0.18.0",
|
|
22
22
|
"@standard-schema/spec": "^1.1.0"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|