@aikirun/workflow 0.9.2 → 0.10.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/dist/index.d.ts +7 -7
- package/dist/index.js +75 -43
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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 { Schema } from '@aikirun/types/validator';
|
|
5
4
|
import { WorkflowRun, TerminalWorkflowRunStatus, WorkflowRunState, WorkflowRunId, WorkflowOptions } from '@aikirun/types/workflow-run';
|
|
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
8
|
import { EventSendOptions, EventWaitOptions, EventWaitState } from '@aikirun/types/event';
|
|
@@ -138,11 +138,11 @@ type WorkflowRunWaitResult<Status extends TerminalWorkflowRunStatus, Output, Tim
|
|
|
138
138
|
declare function event(): EventDefinition<void>;
|
|
139
139
|
declare function event<Data extends Serializable>(params?: EventParams<Data>): EventDefinition<Data>;
|
|
140
140
|
interface EventParams<Data> {
|
|
141
|
-
schema?:
|
|
141
|
+
schema?: StandardSchemaV1<Data>;
|
|
142
142
|
}
|
|
143
143
|
interface EventDefinition<Data> {
|
|
144
144
|
_type: Data;
|
|
145
|
-
schema?:
|
|
145
|
+
schema?: StandardSchemaV1<Data>;
|
|
146
146
|
}
|
|
147
147
|
type EventsDefinition = Record<string, EventDefinition<unknown>>;
|
|
148
148
|
type EventData<TEventDefinition> = TEventDefinition extends EventDefinition<infer Data> ? Data : never;
|
|
@@ -244,8 +244,8 @@ interface WorkflowVersionParams<Input, Output, AppContext, TEventsDefinition ext
|
|
|
244
244
|
events?: TEventsDefinition;
|
|
245
245
|
opts?: WorkflowOptions;
|
|
246
246
|
schema?: RequireAtLeastOneProp<{
|
|
247
|
-
input?:
|
|
248
|
-
output?:
|
|
247
|
+
input?: StandardSchemaV1<Input>;
|
|
248
|
+
output?: StandardSchemaV1<Output>;
|
|
249
249
|
}>;
|
|
250
250
|
}
|
|
251
251
|
interface WorkflowVersion<Input, Output, AppContext, TEventsDefinition extends EventsDefinition = EventsDefinition> {
|
|
@@ -255,7 +255,7 @@ interface WorkflowVersion<Input, Output, AppContext, TEventsDefinition extends E
|
|
|
255
255
|
with(): WorkflowBuilder<Input, Output, AppContext, TEventsDefinition>;
|
|
256
256
|
start: (client: Client<AppContext>, ...args: Input extends void ? [] : [Input]) => Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
|
|
257
257
|
startAsChild: <ParentInput, ParentEventsDefinition extends EventsDefinition>(parentRun: WorkflowRunContext<ParentInput, AppContext, ParentEventsDefinition>, ...args: Input extends void ? [] : [Input]) => Promise<ChildWorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
|
|
258
|
-
getHandle: (client: Client<AppContext>, runId:
|
|
258
|
+
getHandle: (client: Client<AppContext>, runId: string) => Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
|
|
259
259
|
[INTERNAL]: {
|
|
260
260
|
eventsDefinition: TEventsDefinition;
|
|
261
261
|
handler: (run: WorkflowRunContext<Input, AppContext, TEventsDefinition>, input: Input, context: AppContext) => Promise<void>;
|
|
@@ -277,7 +277,7 @@ declare class WorkflowVersionImpl<Input, Output, AppContext, TEventsDefinition e
|
|
|
277
277
|
start(client: Client<AppContext>, ...args: Input extends void ? [] : [Input]): Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
|
|
278
278
|
startAsChild(parentRun: WorkflowRunContext<unknown, AppContext, EventsDefinition>, ...args: Input extends void ? [] : [Input]): Promise<ChildWorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
|
|
279
279
|
private assertUniqueChildRunReferenceId;
|
|
280
|
-
getHandle(client: Client<AppContext>, runId:
|
|
280
|
+
getHandle(client: Client<AppContext>, runId: string): Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
|
|
281
281
|
private handler;
|
|
282
282
|
private tryExecuteWorkflow;
|
|
283
283
|
private assertRetryAllowed;
|
package/dist/index.js
CHANGED
|
@@ -289,6 +289,7 @@ function getRetryParams(attempts, strategy) {
|
|
|
289
289
|
|
|
290
290
|
// run/event.ts
|
|
291
291
|
import { INTERNAL } from "@aikirun/types/symbols";
|
|
292
|
+
import { SchemaValidationError } from "@aikirun/types/validator";
|
|
292
293
|
import {
|
|
293
294
|
WorkflowRunConflictError,
|
|
294
295
|
WorkflowRunFailedError,
|
|
@@ -325,17 +326,24 @@ function createEventWaiter(handle, eventName, schema, logger) {
|
|
|
325
326
|
logger.debug("Timed out waiting for event");
|
|
326
327
|
return { timeout: true };
|
|
327
328
|
}
|
|
328
|
-
let data;
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
329
|
+
let data = event2.data;
|
|
330
|
+
if (schema) {
|
|
331
|
+
const schemaValidation = schema["~standard"].validate(event2.data);
|
|
332
|
+
const schemaValidationResult = schemaValidation instanceof Promise ? await schemaValidation : schemaValidation;
|
|
333
|
+
if (!schemaValidationResult.issues) {
|
|
334
|
+
data = schemaValidationResult.value;
|
|
335
|
+
} else {
|
|
336
|
+
logger.error("Invalid event data", { "aiki.issues": schemaValidationResult.issues });
|
|
337
|
+
await handle[INTERNAL].transitionState({
|
|
338
|
+
status: "failed",
|
|
339
|
+
cause: "self",
|
|
340
|
+
error: {
|
|
341
|
+
name: "SchemaValidationError",
|
|
342
|
+
message: JSON.stringify(schemaValidationResult.issues)
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
throw new WorkflowRunFailedError(handle.run.id, handle.run.attempts);
|
|
346
|
+
}
|
|
339
347
|
}
|
|
340
348
|
logger.debug("Event received");
|
|
341
349
|
return { timeout: false, data };
|
|
@@ -382,9 +390,16 @@ function createEventSender(api, workflowRunId, eventName, schema, logger, onSend
|
|
|
382
390
|
send: (...args) => createEventSender(api, workflowRunId, eventName, schema, logger, onSend, optsBuilder.build()).send(...args)
|
|
383
391
|
});
|
|
384
392
|
async function send(...args) {
|
|
385
|
-
const
|
|
393
|
+
const dataRaw = isNonEmptyArray(args) ? args[0] : void 0;
|
|
394
|
+
let data = dataRaw;
|
|
386
395
|
if (schema) {
|
|
387
|
-
schema.
|
|
396
|
+
const schemaValidation = schema["~standard"].validate(dataRaw);
|
|
397
|
+
const schemaValidationResult = schemaValidation instanceof Promise ? await schemaValidation : schemaValidation;
|
|
398
|
+
if (schemaValidationResult.issues) {
|
|
399
|
+
logger.error("Invalid event data", { "aiki.issues": schemaValidationResult.issues });
|
|
400
|
+
throw new SchemaValidationError("Invalid event data", schemaValidationResult.issues);
|
|
401
|
+
}
|
|
402
|
+
data = schemaValidationResult.value;
|
|
388
403
|
}
|
|
389
404
|
const { run } = await api.workflowRun.sendEventV1({
|
|
390
405
|
id: workflowRunId,
|
|
@@ -426,26 +441,33 @@ function createEventMulticaster(workflowName, workflowVersionId, eventName, sche
|
|
|
426
441
|
)
|
|
427
442
|
});
|
|
428
443
|
async function send(client, runId, ...args) {
|
|
429
|
-
const
|
|
444
|
+
const dataRaw = isNonEmptyArray(args) ? args[0] : void 0;
|
|
445
|
+
let data = dataRaw;
|
|
430
446
|
if (schema) {
|
|
431
|
-
schema.
|
|
447
|
+
const schemaValidation = schema["~standard"].validate(dataRaw);
|
|
448
|
+
const schemaValidationResult = schemaValidation instanceof Promise ? await schemaValidation : schemaValidation;
|
|
449
|
+
if (schemaValidationResult.issues) {
|
|
450
|
+
client.logger.error("Invalid event data", {
|
|
451
|
+
"aiki.workflowName": workflowName,
|
|
452
|
+
"aiki.workflowVersionId": workflowVersionId,
|
|
453
|
+
"aiki.eventName": eventName,
|
|
454
|
+
"aiki.issues": schemaValidationResult.issues
|
|
455
|
+
});
|
|
456
|
+
throw new SchemaValidationError("Invalid event data", schemaValidationResult.issues);
|
|
457
|
+
}
|
|
458
|
+
data = schemaValidationResult.value;
|
|
432
459
|
}
|
|
433
460
|
const runIds = Array.isArray(runId) ? runId : [runId];
|
|
434
461
|
if (!isNonEmptyArray(runIds)) {
|
|
435
462
|
return;
|
|
436
463
|
}
|
|
437
|
-
const logger = client.logger.child({
|
|
438
|
-
"aiki.workflowName": workflowName,
|
|
439
|
-
"aiki.workflowVersionId": workflowVersionId,
|
|
440
|
-
"aiki.eventName": eventName
|
|
441
|
-
});
|
|
442
464
|
await client.api.workflowRun.multicastEventV1({
|
|
443
465
|
ids: runIds,
|
|
444
466
|
eventName,
|
|
445
467
|
data,
|
|
446
468
|
options
|
|
447
469
|
});
|
|
448
|
-
logger.info("Multicasted event to workflows", {
|
|
470
|
+
client.logger.info("Multicasted event to workflows", {
|
|
449
471
|
"aiki.workflowName": workflowName,
|
|
450
472
|
"aiki.workflowVersionId": workflowVersionId,
|
|
451
473
|
"aiki.workflowRunIds": runIds,
|
|
@@ -648,7 +670,7 @@ function createSleeper(handle, logger) {
|
|
|
648
670
|
if (!sleepState) {
|
|
649
671
|
try {
|
|
650
672
|
await handle[INTERNAL3].transitionState({ status: "sleeping", sleepName, durationMs });
|
|
651
|
-
logger.info("
|
|
673
|
+
logger.info("Going to sleeping", {
|
|
652
674
|
"aiki.sleepName": sleepName,
|
|
653
675
|
"aiki.durationMs": durationMs
|
|
654
676
|
});
|
|
@@ -692,11 +714,6 @@ function createSleeper(handle, logger) {
|
|
|
692
714
|
});
|
|
693
715
|
durationMs -= sleepState.durationMs;
|
|
694
716
|
} else {
|
|
695
|
-
logger.warn("Lower sleep duration encountered during replay. Already slept enough", {
|
|
696
|
-
"aiki.sleepName": sleepName,
|
|
697
|
-
"aiki.historicDurationMs": sleepState.durationMs,
|
|
698
|
-
"aiki.latestDurationMs": durationMs
|
|
699
|
-
});
|
|
700
717
|
return { cancelled: false };
|
|
701
718
|
}
|
|
702
719
|
try {
|
|
@@ -726,6 +743,7 @@ function getWorkflowRunPath(name, versionId, referenceId) {
|
|
|
726
743
|
// workflow-version.ts
|
|
727
744
|
import { INTERNAL as INTERNAL5 } from "@aikirun/types/symbols";
|
|
728
745
|
import { TaskFailedError } from "@aikirun/types/task";
|
|
746
|
+
import { SchemaValidationError as SchemaValidationError2 } from "@aikirun/types/validator";
|
|
729
747
|
import {
|
|
730
748
|
WorkflowRunConflictError as WorkflowRunConflictError5,
|
|
731
749
|
WorkflowRunFailedError as WorkflowRunFailedError2,
|
|
@@ -859,7 +877,17 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
|
|
|
859
877
|
}
|
|
860
878
|
async start(client, ...args) {
|
|
861
879
|
const inputRaw = isNonEmptyArray(args) ? args[0] : void 0;
|
|
862
|
-
|
|
880
|
+
let input = inputRaw;
|
|
881
|
+
const schema = this.params.schema?.input;
|
|
882
|
+
if (schema) {
|
|
883
|
+
const schemaValidation = schema["~standard"].validate(inputRaw);
|
|
884
|
+
const schemaValidationResult = schemaValidation instanceof Promise ? await schemaValidation : schemaValidation;
|
|
885
|
+
if (schemaValidationResult.issues) {
|
|
886
|
+
client.logger.error("Invalid workflow data", { "aiki.issues": schemaValidationResult.issues });
|
|
887
|
+
throw new SchemaValidationError2("Invalid workflow data", schemaValidationResult.issues);
|
|
888
|
+
}
|
|
889
|
+
input = schemaValidationResult.value;
|
|
890
|
+
}
|
|
863
891
|
const { run } = await client.api.workflowRun.createV1({
|
|
864
892
|
name: this.name,
|
|
865
893
|
versionId: this.versionId,
|
|
@@ -878,7 +906,7 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
|
|
|
878
906
|
parentRunHandle[INTERNAL5].assertExecutionAllowed();
|
|
879
907
|
const { client } = parentRunHandle[INTERNAL5];
|
|
880
908
|
const inputRaw = isNonEmptyArray(args) ? args[0] : void 0;
|
|
881
|
-
const input = await this.parse(parentRunHandle, this.params.schema?.input, inputRaw);
|
|
909
|
+
const input = await this.parse(parentRunHandle, this.params.schema?.input, inputRaw, parentRun.logger);
|
|
882
910
|
const inputHash = await hashInput(input);
|
|
883
911
|
const reference = this.params.opts?.reference;
|
|
884
912
|
const path = getWorkflowRunPath(this.name, this.versionId, reference?.id ?? inputHash);
|
|
@@ -893,7 +921,7 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
|
|
|
893
921
|
);
|
|
894
922
|
const { run: existingRun } = await client.api.workflowRun.getByIdV1({ id: existingRunInfo.id });
|
|
895
923
|
if (existingRun.state.status === "completed") {
|
|
896
|
-
await this.parse(parentRunHandle, this.params.schema?.output, existingRun.state.output);
|
|
924
|
+
await this.parse(parentRunHandle, this.params.schema?.output, existingRun.state.output, parentRun.logger);
|
|
897
925
|
}
|
|
898
926
|
const logger2 = parentRun.logger.child({
|
|
899
927
|
"aiki.childWorkflowName": existingRun.name,
|
|
@@ -980,7 +1008,7 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
|
|
|
980
1008
|
while (true) {
|
|
981
1009
|
try {
|
|
982
1010
|
const outputRaw = await this.params.handler(run, input, context);
|
|
983
|
-
const output = await this.parse(handle, this.params.schema?.output, outputRaw);
|
|
1011
|
+
const output = await this.parse(handle, this.params.schema?.output, outputRaw, run.logger);
|
|
984
1012
|
return output;
|
|
985
1013
|
} catch (error) {
|
|
986
1014
|
if (error instanceof WorkflowRunSuspendedError4 || error instanceof WorkflowRunFailedError2 || error instanceof WorkflowRunConflictError5) {
|
|
@@ -1007,9 +1035,8 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
|
|
|
1007
1035
|
for (const [key, value] of Object.entries(awaitingRetryState)) {
|
|
1008
1036
|
logMeta[`aiki.${key}`] = value;
|
|
1009
1037
|
}
|
|
1010
|
-
run.logger.info("Workflow
|
|
1038
|
+
run.logger.info("Workflow awaiting retry", {
|
|
1011
1039
|
"aiki.attempts": attempts,
|
|
1012
|
-
"aiki.delayMs": retryParams.delayMs,
|
|
1013
1040
|
...logMeta
|
|
1014
1041
|
});
|
|
1015
1042
|
throw new WorkflowRunSuspendedError4(run.id);
|
|
@@ -1030,20 +1057,25 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
|
|
|
1030
1057
|
throw error;
|
|
1031
1058
|
}
|
|
1032
1059
|
}
|
|
1033
|
-
async parse(handle, schema, data) {
|
|
1060
|
+
async parse(handle, schema, data, logger) {
|
|
1034
1061
|
if (!schema) {
|
|
1035
1062
|
return data;
|
|
1036
1063
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
status: "failed",
|
|
1042
|
-
cause: "self",
|
|
1043
|
-
error: createSerializableError(error)
|
|
1044
|
-
});
|
|
1045
|
-
throw new WorkflowRunFailedError2(handle.run.id, handle.run.attempts);
|
|
1064
|
+
const schemaValidation = schema["~standard"].validate(data);
|
|
1065
|
+
const schemaValidationResult = schemaValidation instanceof Promise ? await schemaValidation : schemaValidation;
|
|
1066
|
+
if (!schemaValidationResult.issues) {
|
|
1067
|
+
return schemaValidationResult.value;
|
|
1046
1068
|
}
|
|
1069
|
+
logger.error("Invalid workflow data", { "aiki.issues": schemaValidationResult.issues });
|
|
1070
|
+
await handle[INTERNAL5].transitionState({
|
|
1071
|
+
status: "failed",
|
|
1072
|
+
cause: "self",
|
|
1073
|
+
error: {
|
|
1074
|
+
name: "SchemaValidationError",
|
|
1075
|
+
message: JSON.stringify(schemaValidationResult.issues)
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
throw new WorkflowRunFailedError2(handle.run.id, handle.run.attempts);
|
|
1047
1079
|
}
|
|
1048
1080
|
createFailedState(error) {
|
|
1049
1081
|
if (error instanceof TaskFailedError) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikirun/workflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
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,8 @@
|
|
|
18
18
|
"build": "tsup"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@aikirun/types": "0.
|
|
21
|
+
"@aikirun/types": "0.10.1",
|
|
22
|
+
"@standard-schema/spec": "^1.1.0"
|
|
22
23
|
},
|
|
23
24
|
"publishConfig": {
|
|
24
25
|
"access": "public"
|