@aikirun/workflow 0.13.0 → 0.15.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 CHANGED
@@ -44,6 +44,22 @@ const handle = await onboardingWorkflowV1.start(aikiClient, {
44
44
  const result = await handle.waitForStatus("completed");
45
45
  ```
46
46
 
47
+ ## Scheduling
48
+
49
+ Run workflows on a schedule using cron expressions or intervals:
50
+
51
+ ```typescript
52
+ import { schedule } from "@aikirun/workflow";
53
+
54
+ const dailyReport = schedule({
55
+ name: "daily-report",
56
+ type: "cron",
57
+ expression: "0 9 * * *", // Every day at 9 AM
58
+ });
59
+
60
+ await dailyReport.activate(aikiClient, onboardingWorkflowV1, { email: "daily@example.com" });
61
+ ```
62
+
47
63
  ## Features
48
64
 
49
65
  - **Durable Execution** - Workflows survive crashes and restarts
@@ -53,6 +69,7 @@ const result = await handle.waitForStatus("completed");
53
69
  - **Child Workflows** - Compose workflows together
54
70
  - **Automatic Retries** - Configurable retry strategies
55
71
  - **Versioning** - Run multiple versions simultaneously
72
+ - **Scheduling** - Trigger workflows on cron or interval schedules
56
73
 
57
74
  ## Documentation
58
75
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
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, WorkflowOptions } from '@aikirun/types/workflow-run';
4
+ import { WorkflowRun, TerminalWorkflowRunStatus, WorkflowRunState, WorkflowRunId, 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';
@@ -10,6 +10,7 @@ import { Serializable } from '@aikirun/types/serializable';
10
10
  import { DistributiveOmit, RequireAtLeastOneProp } from '@aikirun/types/utils';
11
11
  import { TaskId } from '@aikirun/types/task';
12
12
  import { WorkflowRunStateRequest, WorkflowRunTransitionTaskStateRequestV1 } from '@aikirun/types/workflow-run-api';
13
+ import { OverlapPolicy, ScheduleName, ScheduleId } from '@aikirun/types/schedule';
13
14
 
14
15
  type NonEmptyArray<T> = [T, ...T[]];
15
16
 
@@ -30,11 +31,11 @@ type PathFromObjectInternal<T, IncludeArrayKeys extends boolean> = And<[
30
31
  type ExtractObjectType<T> = T extends object ? T : never;
31
32
  type TypeOfValueAtPath<T extends object, Path extends PathFromObject<T>> = Path extends keyof T ? T[Path] : Path extends `${infer First}.${infer Rest}` ? First extends keyof T ? undefined extends T[First] ? Rest extends PathFromObject<ExtractObjectType<T[First]>> ? TypeOfValueAtPath<ExtractObjectType<T[First]>, Rest> | undefined : never : Rest extends PathFromObject<ExtractObjectType<T[First]>> ? TypeOfValueAtPath<ExtractObjectType<T[First]>, Rest> : never : never : never;
32
33
 
33
- declare function workflowRunHandle<Input, Output, AppContext, TEventsDefinition extends EventsDefinition>(client: Client<AppContext>, id: WorkflowRunId, eventsDefinition?: TEventsDefinition, logger?: Logger): Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
34
- declare function workflowRunHandle<Input, Output, AppContext, TEventsDefinition extends EventsDefinition>(client: Client<AppContext>, run: WorkflowRun<Input, Output>, eventsDefinition?: TEventsDefinition, logger?: Logger): Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
35
- interface WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition extends EventsDefinition = EventsDefinition> {
34
+ declare function workflowRunHandle<Input, Output, AppContext, TEvents extends EventsDefinition>(client: Client<AppContext>, id: WorkflowRunId, eventsDefinition?: TEvents, logger?: Logger): Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
35
+ declare function workflowRunHandle<Input, Output, AppContext, TEvents extends EventsDefinition>(client: Client<AppContext>, run: WorkflowRun<Input, Output>, eventsDefinition?: TEvents, logger?: Logger): Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
36
+ interface WorkflowRunHandle<Input, Output, AppContext, TEvents extends EventsDefinition = EventsDefinition> {
36
37
  run: Readonly<WorkflowRun<Input, Output>>;
37
- events: EventSenders<TEventsDefinition>;
38
+ events: EventSenders<TEvents>;
38
39
  refresh: () => Promise<void>;
39
40
  /**
40
41
  * Waits for the child workflow run to reach a terminal status by polling.
@@ -145,16 +146,16 @@ interface EventDefinition<Data> {
145
146
  schema?: StandardSchemaV1<Data>;
146
147
  }
147
148
  type EventsDefinition = Record<string, EventDefinition<unknown>>;
148
- type EventData<TEventDefinition> = TEventDefinition extends EventDefinition<infer Data> ? Data : never;
149
- type EventWaiters<TEventsDefinition extends EventsDefinition> = {
150
- [K in keyof TEventsDefinition]: EventWaiter<EventData<TEventsDefinition[K]>>;
149
+ type EventData<TEvent> = TEvent extends EventDefinition<infer Data> ? Data : never;
150
+ type EventWaiters<TEvents extends EventsDefinition> = {
151
+ [K in keyof TEvents]: EventWaiter<EventData<TEvents[K]>>;
151
152
  };
152
153
  interface EventWaiter<Data> {
153
154
  wait(options?: EventWaitOptions<false>): Promise<EventWaitState<Data, false>>;
154
155
  wait(options: EventWaitOptions<true>): Promise<EventWaitState<Data, true>>;
155
156
  }
156
- type EventSenders<TEventsDefinition extends EventsDefinition> = {
157
- [K in keyof TEventsDefinition]: EventSender<EventData<TEventsDefinition[K]>>;
157
+ type EventSenders<TEvents extends EventsDefinition> = {
158
+ [K in keyof TEvents]: EventSender<EventData<TEvents[K]>>;
158
159
  };
159
160
  interface EventSender<Data> {
160
161
  with(): EventSenderBuilder<Data>;
@@ -164,8 +165,8 @@ interface EventSenderBuilder<Data> {
164
165
  opt<Path extends PathFromObject<EventSendOptions>>(path: Path, value: TypeOfValueAtPath<EventSendOptions, Path>): EventSenderBuilder<Data>;
165
166
  send: (...args: Data extends void ? [] : [Data]) => Promise<void>;
166
167
  }
167
- type EventMulticasters<TEventsDefinition extends EventsDefinition> = {
168
- [K in keyof TEventsDefinition]: EventMulticaster<EventData<TEventsDefinition[K]>>;
168
+ type EventMulticasters<TEvents extends EventsDefinition> = {
169
+ [K in keyof TEvents]: EventMulticaster<EventData<TEvents[K]>>;
169
170
  };
170
171
  interface EventMulticaster<Data> {
171
172
  with(): EventMulticasterBuilder<Data>;
@@ -175,26 +176,26 @@ interface EventMulticasterBuilder<Data> {
175
176
  opt<Path extends PathFromObject<EventSendOptions>>(path: Path, value: TypeOfValueAtPath<EventSendOptions, Path>): EventMulticasterBuilder<Data>;
176
177
  send: <AppContext>(client: Client<AppContext>, runId: string | string[], ...args: Data extends void ? [] : [Data]) => Promise<void>;
177
178
  }
178
- declare function createEventWaiters<TEventsDefinition extends EventsDefinition>(handle: WorkflowRunHandle<unknown, unknown, unknown, TEventsDefinition>, eventsDefinition: TEventsDefinition, logger: Logger): EventWaiters<TEventsDefinition>;
179
- declare function createEventSenders<TEventsDefinition extends EventsDefinition>(api: ApiClient, workflowRunId: string, eventsDefinition: TEventsDefinition, logger: Logger, onSend: (run: WorkflowRun<unknown, unknown>) => void): EventSenders<TEventsDefinition>;
179
+ 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, onSend: (run: WorkflowRun<unknown, unknown>) => void): EventSenders<TEvents>;
180
181
 
181
- interface WorkflowRunContext<Input, AppContext, TEventDefinition extends EventsDefinition> {
182
+ interface WorkflowRunContext<Input, AppContext, TEvents extends EventsDefinition> {
182
183
  id: WorkflowRunId;
183
184
  name: WorkflowName;
184
185
  versionId: WorkflowVersionId;
185
- options: WorkflowOptions;
186
+ options: WorkflowStartOptions;
186
187
  logger: Logger;
187
188
  sleep: (name: string, duration: Duration) => Promise<SleepResult>;
188
- events: EventWaiters<TEventDefinition>;
189
+ events: EventWaiters<TEvents>;
189
190
  [INTERNAL]: {
190
- handle: WorkflowRunHandle<Input, unknown, AppContext, TEventDefinition>;
191
+ handle: WorkflowRunHandle<Input, unknown, AppContext, TEvents>;
191
192
  options: {
192
193
  spinThresholdMs: number;
193
194
  };
194
195
  };
195
196
  }
196
197
 
197
- type ChildWorkflowRunHandle<Input, Output, AppContext, TEventsDefinition extends EventsDefinition = EventsDefinition> = Omit<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>, "waitForStatus"> & {
198
+ type ChildWorkflowRunHandle<Input, Output, AppContext, TEvents extends EventsDefinition = EventsDefinition> = Omit<WorkflowRunHandle<Input, Output, AppContext, TEvents>, "waitForStatus"> & {
198
199
  /**
199
200
  * Waits for the child workflow run to reach a terminal status.
200
201
  *
@@ -239,47 +240,44 @@ interface ChildWorkflowRunWaitOptions<Timed extends boolean> {
239
240
  timeout?: Timed extends true ? DurationObject : never;
240
241
  }
241
242
 
242
- interface WorkflowVersionParams<Input, Output, AppContext, TEventsDefinition extends EventsDefinition> {
243
- handler: (run: Readonly<WorkflowRunContext<Input, AppContext, TEventsDefinition>>, input: Input, context: AppContext) => Promise<Output>;
244
- events?: TEventsDefinition;
245
- opts?: WorkflowOptions;
243
+ interface WorkflowVersionParams<Input, Output, AppContext, TEvents extends EventsDefinition> {
244
+ handler: (run: Readonly<WorkflowRunContext<Input, AppContext, TEvents>>, input: Input, context: AppContext) => Promise<Output>;
245
+ events?: TEvents;
246
+ opts?: WorkflowDefinitionOptions;
246
247
  schema?: RequireAtLeastOneProp<{
247
248
  input?: StandardSchemaV1<Input>;
248
249
  output?: StandardSchemaV1<Output>;
249
250
  }>;
250
251
  }
251
- interface WorkflowVersion<Input, Output, AppContext, TEventsDefinition extends EventsDefinition = EventsDefinition> {
252
+ interface WorkflowVersion<Input, Output, AppContext, TEvents extends EventsDefinition = EventsDefinition> {
252
253
  name: WorkflowName;
253
254
  versionId: WorkflowVersionId;
254
- events: EventMulticasters<TEventsDefinition>;
255
- with(): WorkflowBuilder<Input, Output, AppContext, TEventsDefinition>;
256
- start: (client: Client<AppContext>, ...args: Input extends void ? [] : [Input]) => Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
257
- startAsChild: <ParentInput, ParentEventsDefinition extends EventsDefinition>(parentRun: WorkflowRunContext<ParentInput, AppContext, ParentEventsDefinition>, ...args: Input extends void ? [] : [Input]) => Promise<ChildWorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
258
- getHandleById: (client: Client<AppContext>, runId: string) => Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
259
- getHandleByReferenceId: (client: Client<AppContext>, referenceId: string) => Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
255
+ events: EventMulticasters<TEvents>;
256
+ with(): WorkflowBuilder<Input, Output, AppContext, TEvents>;
257
+ start: (client: Client<AppContext>, ...args: Input extends void ? [] : [Input]) => Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
258
+ startAsChild: <ParentInput, ParentEvents extends EventsDefinition>(parentRun: WorkflowRunContext<ParentInput, AppContext, ParentEvents>, ...args: Input extends void ? [] : [Input]) => Promise<ChildWorkflowRunHandle<Input, Output, AppContext, TEvents>>;
259
+ getHandleById: (client: Client<AppContext>, runId: string) => Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
260
+ getHandleByReferenceId: (client: Client<AppContext>, referenceId: string) => Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
260
261
  [INTERNAL]: {
261
- eventsDefinition: TEventsDefinition;
262
- handler: (run: WorkflowRunContext<Input, AppContext, TEventsDefinition>, input: Input, context: AppContext) => Promise<void>;
262
+ eventsDefinition: TEvents;
263
+ handler: (run: WorkflowRunContext<Input, AppContext, TEvents>, input: Input, context: AppContext) => Promise<void>;
263
264
  };
264
265
  }
265
- interface WorkflowBuilder<Input, Output, AppContext, TEventsDefinition extends EventsDefinition> {
266
- opt<Path extends PathFromObject<WorkflowOptions>>(path: Path, value: TypeOfValueAtPath<WorkflowOptions, Path>): WorkflowBuilder<Input, Output, AppContext, TEventsDefinition>;
267
- start: WorkflowVersion<Input, Output, AppContext, TEventsDefinition>["start"];
268
- startAsChild: WorkflowVersion<Input, Output, AppContext, TEventsDefinition>["startAsChild"];
269
- }
270
- declare class WorkflowVersionImpl<Input, Output, AppContext, TEventsDefinition extends EventsDefinition> implements WorkflowVersion<Input, Output, AppContext, TEventsDefinition> {
266
+ declare class WorkflowVersionImpl<Input, Output, AppContext, TEvents extends EventsDefinition> implements WorkflowVersion<Input, Output, AppContext, TEvents> {
271
267
  readonly name: WorkflowName;
272
268
  readonly versionId: WorkflowVersionId;
273
269
  private readonly params;
274
- readonly events: EventMulticasters<TEventsDefinition>;
275
- readonly [INTERNAL]: WorkflowVersion<Input, Output, AppContext, TEventsDefinition>[typeof INTERNAL];
276
- constructor(name: WorkflowName, versionId: WorkflowVersionId, params: WorkflowVersionParams<Input, Output, AppContext, TEventsDefinition>);
277
- with(): WorkflowBuilder<Input, Output, AppContext, TEventsDefinition>;
278
- start(client: Client<AppContext>, ...args: Input extends void ? [] : [Input]): Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
279
- startAsChild(parentRun: WorkflowRunContext<unknown, AppContext, EventsDefinition>, ...args: Input extends void ? [] : [Input]): Promise<ChildWorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
270
+ readonly events: EventMulticasters<TEvents>;
271
+ readonly [INTERNAL]: WorkflowVersion<Input, Output, AppContext, TEvents>[typeof INTERNAL];
272
+ constructor(name: WorkflowName, versionId: WorkflowVersionId, params: WorkflowVersionParams<Input, Output, AppContext, TEvents>);
273
+ with(): WorkflowBuilder<Input, Output, AppContext, TEvents>;
274
+ start(client: Client<AppContext>, ...args: Input extends void ? [] : [Input]): Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
275
+ startWithOpts(client: Client<AppContext>, startOpts: WorkflowStartOptions, ...args: Input extends void ? [] : [Input]): Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
276
+ startAsChild(parentRun: WorkflowRunContext<unknown, AppContext, EventsDefinition>, ...args: Input extends void ? [] : [Input]): Promise<ChildWorkflowRunHandle<Input, Output, AppContext, TEvents>>;
277
+ startAsChildWithOpts(parentRun: WorkflowRunContext<unknown, AppContext, EventsDefinition>, startOpts: WorkflowStartOptions, ...args: Input extends void ? [] : [Input]): Promise<ChildWorkflowRunHandle<Input, Output, AppContext, TEvents>>;
280
278
  private assertUniqueChildRunReferenceId;
281
- getHandleById(client: Client<AppContext>, runId: string): Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
282
- getHandleByReferenceId(client: Client<AppContext>, referenceId: string): Promise<WorkflowRunHandle<Input, Output, AppContext, TEventsDefinition>>;
279
+ getHandleById(client: Client<AppContext>, runId: string): Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
280
+ getHandleByReferenceId(client: Client<AppContext>, referenceId: string): Promise<WorkflowRunHandle<Input, Output, AppContext, TEvents>>;
283
281
  private handler;
284
282
  private tryExecuteWorkflow;
285
283
  private assertRetryAllowed;
@@ -287,6 +285,11 @@ declare class WorkflowVersionImpl<Input, Output, AppContext, TEventsDefinition e
287
285
  private createFailedState;
288
286
  private createAwaitingRetryState;
289
287
  }
288
+ interface WorkflowBuilder<Input, Output, AppContext, TEvents extends EventsDefinition> {
289
+ opt<Path extends PathFromObject<WorkflowStartOptions>>(path: Path, value: TypeOfValueAtPath<WorkflowStartOptions, Path>): WorkflowBuilder<Input, Output, AppContext, TEvents>;
290
+ start: WorkflowVersion<Input, Output, AppContext, TEvents>["start"];
291
+ startAsChild: WorkflowVersion<Input, Output, AppContext, TEvents>["startAsChild"];
292
+ }
290
293
 
291
294
  declare function workflowRegistry(): WorkflowRegistry;
292
295
  type Workflow$1 = WorkflowVersion<unknown, unknown, unknown>;
@@ -302,6 +305,33 @@ interface WorkflowRegistry {
302
305
 
303
306
  declare function createSleeper(handle: WorkflowRunHandle<unknown, unknown, unknown>, logger: Logger): (name: string, duration: Duration) => Promise<SleepResult>;
304
307
 
308
+ interface CronScheduleParams {
309
+ type: "cron";
310
+ expression: string;
311
+ timezone?: string;
312
+ overlapPolicy?: OverlapPolicy;
313
+ }
314
+ interface IntervalScheduleParams {
315
+ type: "interval";
316
+ every: DurationObject;
317
+ overlapPolicy?: OverlapPolicy;
318
+ }
319
+ type ScheduleParams = CronScheduleParams | IntervalScheduleParams;
320
+ interface ScheduleHandle {
321
+ id: ScheduleId;
322
+ name: ScheduleName;
323
+ pause(): Promise<void>;
324
+ resume(): Promise<void>;
325
+ delete(): Promise<void>;
326
+ }
327
+ type ScheduleDefinition = ScheduleParams & {
328
+ name: ScheduleName;
329
+ activate<Input, Output, AppContext, TEvents extends EventsDefinition>(client: Client<AppContext>, workflow: WorkflowVersion<Input, Output, AppContext, TEvents>, ...args: Input extends void ? [] : [Input]): Promise<ScheduleHandle>;
330
+ };
331
+ declare function schedule(params: {
332
+ name: string;
333
+ } & ScheduleParams): ScheduleDefinition;
334
+
305
335
  /**
306
336
  * Defines a durable workflow with versioning and multiple task execution.
307
337
  *
@@ -355,11 +385,11 @@ interface WorkflowParams {
355
385
  }
356
386
  interface Workflow {
357
387
  name: WorkflowName;
358
- v: <Input extends Serializable, Output extends Serializable, AppContext = null, TEventsDefinition extends EventsDefinition = Record<string, never>>(versionId: string, params: WorkflowVersionParams<Input, Output, AppContext, TEventsDefinition>) => WorkflowVersion<Input, Output, AppContext, TEventsDefinition>;
388
+ v: <Input extends Serializable, Output extends Serializable, AppContext = null, TEvents extends EventsDefinition = Record<string, never>>(versionId: string, params: WorkflowVersionParams<Input, Output, AppContext, TEvents>) => WorkflowVersion<Input, Output, AppContext, TEvents>;
359
389
  [INTERNAL]: {
360
390
  getAllVersions: () => WorkflowVersion<unknown, unknown, unknown>[];
361
391
  getVersion: (versionId: WorkflowVersionId) => WorkflowVersion<unknown, unknown, unknown> | undefined;
362
392
  };
363
393
  }
364
394
 
365
- export { type EventMulticaster, type EventMulticasters, type EventSender, type EventSenders, type EventWaiter, type EventWaiters, type Workflow, type WorkflowParams, type WorkflowRegistry, type WorkflowRunContext, type WorkflowRunHandle, type WorkflowRunWaitOptions, type WorkflowVersion, WorkflowVersionImpl, type WorkflowVersionParams, createEventSenders, createEventWaiters, createSleeper, event, workflow, workflowRegistry, workflowRunHandle };
395
+ 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 };
package/dist/index.js CHANGED
@@ -392,10 +392,9 @@ function createEventSender(api, workflowRunId, eventName, schema, logger, onSend
392
392
  send: (...args) => createEventSender(api, workflowRunId, eventName, schema, logger, onSend, optsBuilder.build()).send(...args)
393
393
  });
394
394
  async function send(...args) {
395
- const dataRaw = isNonEmptyArray(args) ? args[0] : void 0;
396
- let data = dataRaw;
395
+ let data = args[0];
397
396
  if (schema) {
398
- const schemaValidation = schema["~standard"].validate(dataRaw);
397
+ const schemaValidation = schema["~standard"].validate(data);
399
398
  const schemaValidationResult = schemaValidation instanceof Promise ? await schemaValidation : schemaValidation;
400
399
  if (schemaValidationResult.issues) {
401
400
  logger.error("Invalid event data", { "aiki.issues": schemaValidationResult.issues });
@@ -443,10 +442,9 @@ function createEventMulticaster(workflowName, workflowVersionId, eventName, sche
443
442
  )
444
443
  });
445
444
  async function send(client, runId, ...args) {
446
- const dataRaw = isNonEmptyArray(args) ? args[0] : void 0;
447
- let data = dataRaw;
445
+ let data = args[0];
448
446
  if (schema) {
449
- const schemaValidation = schema["~standard"].validate(dataRaw);
447
+ const schemaValidation = schema["~standard"].validate(data);
450
448
  const schemaValidationResult = schemaValidation instanceof Promise ? await schemaValidation : schemaValidation;
451
449
  if (schemaValidationResult.issues) {
452
450
  client.logger.error("Invalid event data", {
@@ -548,15 +546,15 @@ var WorkflowRunHandleImpl = class {
548
546
  await this.refresh();
549
547
  return this.run.state;
550
548
  };
551
- const isNeitherExpectedNorTerminal = (state) => Promise.resolve(state.status !== expectedStatus && !isTerminalWorkflowRunStatus(state.status));
549
+ const isNeitherExpectedNorTerminal = (state) => state.status !== expectedStatus && !isTerminalWorkflowRunStatus(state.status);
552
550
  if (!Number.isFinite(maxAttempts) && !options?.abortSignal) {
553
551
  const maybeResult2 = await withRetry(loadState, retryStrategy, {
554
- shouldRetryOnResult: isNeitherExpectedNorTerminal
552
+ shouldRetryOnResult: async (state) => isNeitherExpectedNorTerminal(state)
555
553
  }).run();
556
554
  if (maybeResult2.state === "timeout") {
557
555
  throw new Error("Something's wrong, this should've never timed out");
558
556
  }
559
- if (await isNeitherExpectedNorTerminal(maybeResult2.result)) {
557
+ if (maybeResult2.result.status !== expectedStatus) {
560
558
  return {
561
559
  success: false,
562
560
  cause: "run_terminated"
@@ -569,10 +567,13 @@ var WorkflowRunHandleImpl = class {
569
567
  }
570
568
  const maybeResult = options?.abortSignal ? await withRetry(loadState, retryStrategy, {
571
569
  abortSignal: options.abortSignal,
572
- shouldRetryOnResult: isNeitherExpectedNorTerminal
573
- }).run() : await withRetry(loadState, retryStrategy, { shouldRetryOnResult: isNeitherExpectedNorTerminal }).run();
570
+ shouldRetryOnResult: async (state) => isNeitherExpectedNorTerminal(state)
571
+ }).run() : await withRetry(loadState, retryStrategy, {
572
+ shouldRetryOnResult: async (state) => isNeitherExpectedNorTerminal(state)
573
+ }).run();
574
+ this.logger.info("Maybe result", { maybeResult });
574
575
  if (maybeResult.state === "completed") {
575
- if (await isNeitherExpectedNorTerminal(maybeResult.result)) {
576
+ if (maybeResult.result.status !== expectedStatus) {
576
577
  return {
577
578
  success: false,
578
579
  cause: "run_terminated"
@@ -734,11 +735,59 @@ function createSleeper(handle, logger) {
734
735
  };
735
736
  }
736
737
 
738
+ // schedule.ts
739
+ function schedule(params) {
740
+ const { name, ...scheduleParams } = params;
741
+ return {
742
+ name,
743
+ ...scheduleParams,
744
+ async activate(client, workflow2, ...args) {
745
+ const input = args[0];
746
+ let scheduleSpec;
747
+ if (scheduleParams.type === "interval") {
748
+ const { every, ...rest } = scheduleParams;
749
+ scheduleSpec = {
750
+ ...rest,
751
+ everyMs: toMilliseconds(every)
752
+ };
753
+ } else {
754
+ scheduleSpec = scheduleParams;
755
+ }
756
+ const { schedule: schedule2 } = await client.api.schedule.activateV1({
757
+ name,
758
+ workflowName: workflow2.name,
759
+ workflowVersionId: workflow2.versionId,
760
+ spec: scheduleSpec,
761
+ input
762
+ });
763
+ client.logger.info("Schedule activated", {
764
+ scheduleSpec,
765
+ workflowName: workflow2.name,
766
+ workflowVersionId: workflow2.versionId
767
+ });
768
+ const scheduleId = schedule2.id;
769
+ return {
770
+ id: scheduleId,
771
+ name,
772
+ pause: async () => {
773
+ await client.api.schedule.pauseV1({ id: scheduleId });
774
+ },
775
+ resume: async () => {
776
+ await client.api.schedule.resumeV1({ id: scheduleId });
777
+ },
778
+ delete: async () => {
779
+ await client.api.schedule.deleteV1({ id: scheduleId });
780
+ }
781
+ };
782
+ }
783
+ };
784
+ }
785
+
737
786
  // workflow.ts
738
787
  import { INTERNAL as INTERNAL6 } from "@aikirun/types/symbols";
739
788
 
740
- // ../../lib/path/index.ts
741
- function getWorkflowRunPath(name, versionId, referenceId) {
789
+ // ../../lib/address/index.ts
790
+ function getWorkflowRunAddress(name, versionId, referenceId) {
742
791
  return `${name}/${versionId}/${referenceId}`;
743
792
  }
744
793
 
@@ -777,7 +826,7 @@ function createStatusWaiter(handle, parentRun, logger) {
777
826
  let nextWaitIndex = 0;
778
827
  async function waitForStatus(expectedStatus, options) {
779
828
  const parentRunHandle = parentRun[INTERNAL4].handle;
780
- const waitResults = parentRunHandle.run.childWorkflowRuns[handle.run.path]?.statusWaitResults ?? [];
829
+ const waitResults = parentRunHandle.run.childWorkflowRuns[handle.run.address]?.statusWaitResults ?? [];
781
830
  const waitResult = waitResults[nextWaitIndex];
782
831
  if (waitResult) {
783
832
  nextWaitIndex++;
@@ -846,7 +895,7 @@ function createStatusWaiter(handle, parentRun, logger) {
846
895
  }
847
896
 
848
897
  // workflow-version.ts
849
- var WorkflowVersionImpl = class _WorkflowVersionImpl {
898
+ var WorkflowVersionImpl = class {
850
899
  constructor(name, versionId, params) {
851
900
  this.name = name;
852
901
  this.versionId = versionId;
@@ -861,28 +910,18 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
861
910
  events;
862
911
  [INTERNAL5];
863
912
  with() {
864
- const optsOverrider = objectOverrider(this.params.opts ?? {});
865
- const createBuilder = (optsBuilder) => {
866
- return {
867
- opt: (path, value) => createBuilder(optsBuilder.with(path, value)),
868
- start: (client, ...args) => new _WorkflowVersionImpl(this.name, this.versionId, {
869
- ...this.params,
870
- opts: optsBuilder.build()
871
- }).start(client, ...args),
872
- startAsChild: (parentRun, ...args) => new _WorkflowVersionImpl(this.name, this.versionId, {
873
- ...this.params,
874
- opts: optsBuilder.build()
875
- }).startAsChild(parentRun, ...args)
876
- };
877
- };
878
- return createBuilder(optsOverrider());
913
+ const startOpts = this.params.opts ?? {};
914
+ const startOptsOverrider = objectOverrider(startOpts);
915
+ return new WorkflowBuilderImpl(this, startOptsOverrider());
879
916
  }
880
917
  async start(client, ...args) {
881
- const inputRaw = isNonEmptyArray(args) ? args[0] : void 0;
882
- let input = inputRaw;
918
+ return this.startWithOpts(client, this.params.opts ?? {}, ...args);
919
+ }
920
+ async startWithOpts(client, startOpts, ...args) {
921
+ let input = args[0];
883
922
  const schema = this.params.schema?.input;
884
923
  if (schema) {
885
- const schemaValidation = schema["~standard"].validate(inputRaw);
924
+ const schemaValidation = schema["~standard"].validate(input);
886
925
  const schemaValidationResult = schemaValidation instanceof Promise ? await schemaValidation : schemaValidation;
887
926
  if (schemaValidationResult.issues) {
888
927
  client.logger.error("Invalid workflow data", { "aiki.issues": schemaValidationResult.issues });
@@ -894,7 +933,7 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
894
933
  name: this.name,
895
934
  versionId: this.versionId,
896
935
  input,
897
- options: this.params.opts
936
+ options: startOpts
898
937
  });
899
938
  client.logger.info("Created workflow", {
900
939
  "aiki.workflowName": this.name,
@@ -904,15 +943,18 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
904
943
  return workflowRunHandle(client, run, this[INTERNAL5].eventsDefinition);
905
944
  }
906
945
  async startAsChild(parentRun, ...args) {
946
+ return this.startAsChildWithOpts(parentRun, this.params.opts ?? {}, ...args);
947
+ }
948
+ async startAsChildWithOpts(parentRun, startOpts, ...args) {
907
949
  const parentRunHandle = parentRun[INTERNAL5].handle;
908
950
  parentRunHandle[INTERNAL5].assertExecutionAllowed();
909
951
  const { client } = parentRunHandle[INTERNAL5];
910
- const inputRaw = isNonEmptyArray(args) ? args[0] : void 0;
952
+ const inputRaw = args[0];
911
953
  const input = await this.parse(parentRunHandle, this.params.schema?.input, inputRaw, parentRun.logger);
912
954
  const inputHash = await hashInput(input);
913
- const reference = this.params.opts?.reference;
914
- const path = getWorkflowRunPath(this.name, this.versionId, reference?.id ?? inputHash);
915
- const existingRunInfo = parentRunHandle.run.childWorkflowRuns[path];
955
+ const reference = startOpts.reference;
956
+ const address = getWorkflowRunAddress(this.name, this.versionId, reference?.id ?? inputHash);
957
+ const existingRunInfo = parentRunHandle.run.childWorkflowRuns[address];
916
958
  if (existingRunInfo) {
917
959
  await this.assertUniqueChildRunReferenceId(
918
960
  parentRunHandle,
@@ -943,9 +985,9 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
943
985
  versionId: this.versionId,
944
986
  input,
945
987
  parentWorkflowRunId: parentRun.id,
946
- options: this.params.opts
988
+ options: startOpts
947
989
  });
948
- parentRunHandle.run.childWorkflowRuns[path] = {
990
+ parentRunHandle.run.childWorkflowRuns[address] = {
949
991
  id: newRun.id,
950
992
  name: newRun.name,
951
993
  versionId: newRun.versionId,
@@ -968,8 +1010,8 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
968
1010
  }
969
1011
  async assertUniqueChildRunReferenceId(parentRunHandle, existingRunInfo, inputHash, reference, logger) {
970
1012
  if (existingRunInfo.inputHash !== inputHash && reference) {
971
- const onConflict = reference.onConflict ?? "error";
972
- if (onConflict !== "error") {
1013
+ const conflictPolicy = reference.conflictPolicy ?? "error";
1014
+ if (conflictPolicy !== "error") {
973
1015
  return;
974
1016
  }
975
1017
  logger.error("Reference ID already used by another child workflow", {
@@ -1120,6 +1162,21 @@ var WorkflowVersionImpl = class _WorkflowVersionImpl {
1120
1162
  };
1121
1163
  }
1122
1164
  };
1165
+ var WorkflowBuilderImpl = class _WorkflowBuilderImpl {
1166
+ constructor(workflow2, startOptsBuilder) {
1167
+ this.workflow = workflow2;
1168
+ this.startOptsBuilder = startOptsBuilder;
1169
+ }
1170
+ opt(path, value) {
1171
+ return new _WorkflowBuilderImpl(this.workflow, this.startOptsBuilder.with(path, value));
1172
+ }
1173
+ start(client, ...args) {
1174
+ return this.workflow.startWithOpts(client, this.startOptsBuilder.build(), ...args);
1175
+ }
1176
+ startAsChild(parentRun, ...args) {
1177
+ return this.workflow.startAsChildWithOpts(parentRun, this.startOptsBuilder.build(), ...args);
1178
+ }
1179
+ };
1123
1180
 
1124
1181
  // workflow.ts
1125
1182
  function workflow(params) {
@@ -1160,6 +1217,7 @@ export {
1160
1217
  createEventWaiters,
1161
1218
  createSleeper,
1162
1219
  event,
1220
+ schedule,
1163
1221
  workflow,
1164
1222
  workflowRegistry,
1165
1223
  workflowRunHandle
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aikirun/workflow",
3
- "version": "0.13.0",
3
+ "version": "0.15.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.13.0",
21
+ "@aikirun/types": "0.15.0",
22
22
  "@standard-schema/spec": "^1.1.0"
23
23
  },
24
24
  "publishConfig": {