@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 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?: Schema<Data>;
141
+ schema?: StandardSchemaV1<Data>;
142
142
  }
143
143
  interface EventDefinition<Data> {
144
144
  _type: Data;
145
- schema?: Schema<Data>;
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?: Schema<Input>;
248
- output?: Schema<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: WorkflowRunId) => Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
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: WorkflowRunId): Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
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
- try {
330
- data = schema ? schema.parse(event2.data) : event2.data;
331
- } catch (error) {
332
- logger.error("Invalid event data", { data: event2.data, error });
333
- await handle[INTERNAL].transitionState({
334
- status: "failed",
335
- cause: "self",
336
- error: createSerializableError(error)
337
- });
338
- throw new WorkflowRunFailedError(handle.run.id, handle.run.attempts);
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 data = isNonEmptyArray(args) ? args[0] : void 0;
393
+ const dataRaw = isNonEmptyArray(args) ? args[0] : void 0;
394
+ let data = dataRaw;
386
395
  if (schema) {
387
- schema.parse(data);
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 data = isNonEmptyArray(args) ? args[0] : void 0;
444
+ const dataRaw = isNonEmptyArray(args) ? args[0] : void 0;
445
+ let data = dataRaw;
430
446
  if (schema) {
431
- schema.parse(data);
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("Sleeping", {
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
- const input = this.params.schema?.input ? this.params.schema.input.parse(inputRaw) : inputRaw;
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 failed. Awaiting retry", {
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
- try {
1038
- return schema.parse(data);
1039
- } catch (error) {
1040
- await handle[INTERNAL5].transitionState({
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.9.2",
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.9.2"
21
+ "@aikirun/types": "0.10.1",
22
+ "@standard-schema/spec": "^1.1.0"
22
23
  },
23
24
  "publishConfig": {
24
25
  "access": "public"