@classytic/streamline 1.0.0 → 2.0.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.
@@ -0,0 +1,224 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-95iHPtFO.mjs";
2
+
3
+ //#region src/utils/logger.ts
4
+ var Logger = class {
5
+ minLevel = "info";
6
+ setLevel(level) {
7
+ this.minLevel = level;
8
+ }
9
+ debug(message, context) {
10
+ this.log("debug", message, context);
11
+ }
12
+ info(message, context) {
13
+ this.log("info", message, context);
14
+ }
15
+ warn(message, context) {
16
+ this.log("warn", message, context);
17
+ }
18
+ error(message, error, context) {
19
+ const errorContext = error instanceof Error ? {
20
+ error: {
21
+ message: error.message,
22
+ stack: error.stack
23
+ },
24
+ ...context
25
+ } : {
26
+ error,
27
+ ...context
28
+ };
29
+ this.log("error", message, errorContext);
30
+ }
31
+ log(level, message, context) {
32
+ if (!this.shouldLog(level)) return;
33
+ const logEntry = {
34
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
35
+ level: level.toUpperCase(),
36
+ message,
37
+ ...context
38
+ };
39
+ this.getLogFunction(level)(JSON.stringify(logEntry));
40
+ }
41
+ shouldLog(level) {
42
+ const levels = [
43
+ "debug",
44
+ "info",
45
+ "warn",
46
+ "error"
47
+ ];
48
+ return levels.indexOf(level) >= levels.indexOf(this.minLevel);
49
+ }
50
+ getLogFunction(level) {
51
+ switch (level) {
52
+ case "error": return console.error;
53
+ case "warn": return console.warn;
54
+ case "info": return console.info;
55
+ default: return console.debug;
56
+ }
57
+ }
58
+ };
59
+ const logger = new Logger();
60
+ if (process.env.NODE_ENV === "development") logger.setLevel("debug");
61
+
62
+ //#endregion
63
+ //#region src/execution/context.ts
64
+ var context_exports = /* @__PURE__ */ __exportAll({
65
+ GotoSignal: () => GotoSignal,
66
+ StepContextImpl: () => StepContextImpl,
67
+ WaitSignal: () => WaitSignal
68
+ });
69
+ var WaitSignal = class extends Error {
70
+ constructor(type, reason, data) {
71
+ super(reason);
72
+ this.type = type;
73
+ this.reason = reason;
74
+ this.data = data;
75
+ this.name = "WaitSignal";
76
+ }
77
+ };
78
+ /**
79
+ * Signal thrown by ctx.goto() to jump execution to a different step.
80
+ * Caught by the engine to update currentStepId.
81
+ */
82
+ var GotoSignal = class extends Error {
83
+ constructor(targetStepId) {
84
+ super(`goto:${targetStepId}`);
85
+ this.targetStepId = targetStepId;
86
+ this.name = "GotoSignal";
87
+ }
88
+ };
89
+ var StepContextImpl = class {
90
+ signal;
91
+ constructor(runId, stepId, context, input, attempt, run, repository, eventBus, signal, signalStore) {
92
+ this.runId = runId;
93
+ this.stepId = stepId;
94
+ this.context = context;
95
+ this.input = input;
96
+ this.attempt = attempt;
97
+ this.run = run;
98
+ this.repository = repository;
99
+ this.eventBus = eventBus;
100
+ this.signalStore = signalStore;
101
+ this.signal = signal ?? new AbortController().signal;
102
+ }
103
+ async set(key, value) {
104
+ if (this.signal.aborted) throw new Error(`Cannot update context: workflow ${this.runId} has been cancelled`);
105
+ this.context[key] = value;
106
+ this.run.context[key] = value;
107
+ if ((await this.repository.updateOne({
108
+ _id: this.runId,
109
+ status: { $ne: "cancelled" }
110
+ }, { $set: {
111
+ [`context.${String(key)}`]: value,
112
+ updatedAt: /* @__PURE__ */ new Date()
113
+ } }, { bypassTenant: true })).modifiedCount === 0) throw new Error(`Cannot update context: workflow ${this.runId} may have been cancelled`);
114
+ }
115
+ getOutput(stepId) {
116
+ return this.run.steps.find((s) => s.stepId === stepId)?.output;
117
+ }
118
+ async wait(reason, data) {
119
+ throw new WaitSignal("human", reason, data);
120
+ }
121
+ async waitFor(eventName, reason) {
122
+ throw new WaitSignal("event", reason || `Waiting for ${eventName}`, { eventName });
123
+ }
124
+ async sleep(ms) {
125
+ const resumeAt = new Date(Date.now() + ms);
126
+ throw new WaitSignal("timer", `Sleep ${ms}ms`, { resumeAt });
127
+ }
128
+ async heartbeat() {
129
+ if (this.signal.aborted) return;
130
+ try {
131
+ await this.repository.updateOne({
132
+ _id: this.runId,
133
+ status: { $ne: "cancelled" }
134
+ }, { lastHeartbeat: /* @__PURE__ */ new Date() }, { bypassTenant: true });
135
+ } catch {}
136
+ }
137
+ emit(eventName, data) {
138
+ const payload = {
139
+ runId: this.runId,
140
+ stepId: this.stepId,
141
+ data
142
+ };
143
+ this.eventBus.emit(eventName, payload);
144
+ this.signalStore?.publish(`streamline:event:${eventName}`, payload);
145
+ }
146
+ log(message, data) {
147
+ logger.info(message, {
148
+ runId: this.runId,
149
+ stepId: this.stepId,
150
+ attempt: this.attempt,
151
+ ...data !== void 0 && { data }
152
+ });
153
+ }
154
+ async startChildWorkflow(workflowId, input) {
155
+ throw new WaitSignal("childWorkflow", `Waiting for child workflow: ${workflowId}`, {
156
+ childWorkflowId: workflowId,
157
+ childInput: input,
158
+ parentRunId: this.runId,
159
+ parentStepId: this.stepId
160
+ });
161
+ }
162
+ async goto(targetStepId) {
163
+ throw new GotoSignal(targetStepId);
164
+ }
165
+ async scatter(tasks, options) {
166
+ const taskIds = Object.keys(tasks);
167
+ const concurrency = options?.concurrency ?? Infinity;
168
+ const checkpoint = this.getCheckpoint() ?? {};
169
+ const results = {};
170
+ for (const id of taskIds) {
171
+ const saved = checkpoint[id];
172
+ if (saved?.done) results[id] = saved.value;
173
+ }
174
+ const pending = taskIds.filter((id) => !checkpoint[id]?.done);
175
+ if (pending.length === 0) return results;
176
+ const executing = /* @__PURE__ */ new Set();
177
+ for (const id of pending) {
178
+ if (this.signal.aborted) break;
179
+ const taskFn = tasks[id];
180
+ let promise;
181
+ promise = (async () => {
182
+ try {
183
+ const value = await taskFn();
184
+ results[id] = value;
185
+ checkpoint[id] = {
186
+ done: true,
187
+ value
188
+ };
189
+ } catch (err) {
190
+ throw err;
191
+ } finally {
192
+ executing.delete(promise);
193
+ await this.checkpoint(checkpoint);
194
+ }
195
+ })();
196
+ executing.add(promise);
197
+ if (executing.size >= concurrency) await Promise.race(executing).catch(() => {});
198
+ }
199
+ const failures = (await Promise.allSettled(executing)).filter((r) => r.status === "rejected");
200
+ if (failures.length > 0) throw failures[0].reason;
201
+ return results;
202
+ }
203
+ async checkpoint(value) {
204
+ if (this.signal.aborted) return;
205
+ const stepIndex = this.run.steps.findIndex((s) => s.stepId === this.stepId);
206
+ if (stepIndex === -1) return;
207
+ await this.repository.updateOne({
208
+ _id: this.runId,
209
+ status: { $ne: "cancelled" }
210
+ }, { $set: {
211
+ [`steps.${stepIndex}.output`]: { __checkpoint: value },
212
+ updatedAt: /* @__PURE__ */ new Date(),
213
+ lastHeartbeat: /* @__PURE__ */ new Date()
214
+ } }, { bypassTenant: true });
215
+ const step = this.run.steps[stepIndex];
216
+ if (step) step.output = { __checkpoint: value };
217
+ }
218
+ getCheckpoint() {
219
+ return (this.run.steps.find((s) => s.stepId === this.stepId)?.output)?.__checkpoint;
220
+ }
221
+ };
222
+
223
+ //#endregion
224
+ export { logger as a, context_exports as i, StepContextImpl as n, WaitSignal as r, GotoSignal as t };
@@ -1,20 +1,5 @@
1
- //#region \0rolldown/runtime.js
2
- var __defProp = Object.defineProperty;
3
- var __exportAll = (all, no_symbols) => {
4
- let target = {};
5
- for (var name in all) {
6
- __defProp(target, name, {
7
- get: all[name],
8
- enumerable: true
9
- });
10
- }
11
- if (!no_symbols) {
12
- __defProp(target, Symbol.toStringTag, { value: "Module" });
13
- }
14
- return target;
15
- };
1
+ import { t as __exportAll } from "./rolldown-runtime-95iHPtFO.mjs";
16
2
 
17
- //#endregion
18
3
  //#region src/utils/errors.ts
19
4
  var errors_exports = /* @__PURE__ */ __exportAll({
20
5
  DataCorruptionError: () => DataCorruptionError,
@@ -89,4 +89,4 @@ declare class WorkflowEventBus extends EventEmitter {
89
89
  */
90
90
  declare const globalEventBus: WorkflowEventBus;
91
91
  //#endregion
92
- export { WorkflowEventName as n, globalEventBus as r, WorkflowEventBus as t };
92
+ export { StepEventPayload as a, WorkflowCompletedPayload as c, WorkflowFailedPayload as d, WorkflowResumedPayload as f, StepCompletedPayload as i, WorkflowEventBus as l, EngineErrorPayload as n, StepFailedPayload as o, globalEventBus as p, EventPayloadMap as r, StepRetryPayload as s, BaseEventPayload as t, WorkflowEventName as u };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { _ as WorkflowHandlers, a as SchedulingInfo, c as StepError, d as StepState, f as StepStatus, g as WorkflowEventPayload, h as WorkflowDefinition, i as RunStatus, l as StepHandler, m as WaitingFor, n as InferHandlersContext, o as Step, p as TypedHandlers, r as RecurrencePattern, s as StepContext, t as InferContext, u as StepIds, v as WorkflowRun } from "./types-DG85_LzF.mjs";
2
- import { n as WorkflowEventName, r as globalEventBus, t as WorkflowEventBus } from "./events-C0sZINZq.mjs";
1
+ import { _ as WorkflowHandlers, a as SchedulingInfo, c as StepError, d as StepState, f as StepStatus, g as WorkflowEventPayload, h as WorkflowDefinition, i as RunStatus, l as StepHandler, m as WaitingFor, n as InferHandlersContext, o as Step, p as TypedHandlers, r as RecurrencePattern, s as StepContext, t as InferContext, u as StepIds, v as WorkflowRun } from "./types-DjgzSrNY.mjs";
2
+ import { a as StepEventPayload, c as WorkflowCompletedPayload, d as WorkflowFailedPayload, f as WorkflowResumedPayload, i as StepCompletedPayload, l as WorkflowEventBus, n as EngineErrorPayload, o as StepFailedPayload, p as globalEventBus, r as EventPayloadMap, s as StepRetryPayload, t as BaseEventPayload, u as WorkflowEventName } from "./events-DC0ddZZ9.mjs";
3
3
  import { FilterQuery, Plugin, Repository } from "@classytic/mongokit";
4
4
  import mongoose from "mongoose";
5
5
 
@@ -206,6 +206,12 @@ interface SmartSchedulerConfig {
206
206
  staleCheckInterval: number;
207
207
  /** Threshold for stale workflow detection */
208
208
  staleThreshold: number;
209
+ /**
210
+ * Max workflows executing simultaneously.
211
+ * When the limit is reached, the scheduler skips processing new workflows until slots free up.
212
+ * @default Infinity (no limit — current behavior)
213
+ */
214
+ maxConcurrentExecutions: number;
209
215
  }
210
216
  declare class SmartScheduler {
211
217
  private repository;
@@ -347,6 +353,38 @@ declare class WorkflowCache {
347
353
  }
348
354
  //#endregion
349
355
  //#region src/core/container.d.ts
356
+ /**
357
+ * Pluggable signal store for durable cross-process event delivery.
358
+ *
359
+ * Default: in-memory (process-local). For durable signals across workers,
360
+ * plug in Redis, Kafka, BullMQ, or any pub/sub backend.
361
+ *
362
+ * Streamline never depends on these — users bring their own adapter.
363
+ *
364
+ * @example Redis adapter (user-provided)
365
+ * ```typescript
366
+ * import Redis from 'ioredis';
367
+ *
368
+ * const redis = new Redis();
369
+ * const signalStore: SignalStore = {
370
+ * publish: (channel, data) => redis.publish(channel, JSON.stringify(data)),
371
+ * subscribe: (channel, handler) => {
372
+ * const sub = redis.duplicate();
373
+ * sub.subscribe(channel);
374
+ * sub.on('message', (ch, msg) => handler(JSON.parse(msg)));
375
+ * return () => { sub.unsubscribe(channel); sub.disconnect(); };
376
+ * },
377
+ * };
378
+ *
379
+ * const container = createContainer({ signalStore });
380
+ * ```
381
+ */
382
+ interface SignalStore {
383
+ /** Publish a signal to a named channel */
384
+ publish(channel: string, data: unknown): Promise<void> | void;
385
+ /** Subscribe to a channel. Returns an unsubscribe function. */
386
+ subscribe(channel: string, handler: (data: unknown) => void): (() => void) | Promise<() => void>;
387
+ }
350
388
  /**
351
389
  * Container holding all shared dependencies for a workflow engine instance.
352
390
  *
@@ -369,6 +407,8 @@ interface StreamlineContainer {
369
407
  eventBus: WorkflowEventBus;
370
408
  /** In-memory cache for active workflows */
371
409
  cache: WorkflowCache;
410
+ /** Pluggable signal store for durable cross-process event delivery */
411
+ signalStore: SignalStore;
372
412
  }
373
413
  /**
374
414
  * Options for creating a container
@@ -393,6 +433,12 @@ interface ContainerOptions {
393
433
  * If undefined: creates a new isolated cache
394
434
  */
395
435
  cache?: WorkflowCache;
436
+ /**
437
+ * Custom signal store for durable cross-process event delivery.
438
+ * - If SignalStore: uses the provided instance (e.g., Redis, Kafka, BullMQ adapter)
439
+ * - If undefined: uses default in-memory store (process-local)
440
+ */
441
+ signalStore?: SignalStore;
396
442
  }
397
443
  /**
398
444
  * Create a new container with configurable dependencies.
@@ -448,11 +494,27 @@ declare class HookRegistry {
448
494
  }
449
495
  /** Global hook registry instance */
450
496
  declare const hookRegistry: HookRegistry;
497
+ /**
498
+ * Global registry mapping workflowId → engine.
499
+ * Populated by createWorkflow(). Enables ctx.startChildWorkflow() to find
500
+ * and start child workflows by ID without the caller needing a reference.
501
+ */
502
+ declare class WorkflowRegistryGlobal {
503
+ private engines;
504
+ register(workflowId: string, engine: WorkflowEngine<unknown>): void;
505
+ getEngine(workflowId: string): WorkflowEngine<unknown> | undefined;
506
+ }
507
+ declare const workflowRegistry: WorkflowRegistryGlobal;
451
508
  interface WorkflowEngineOptions {
452
509
  /** Auto-execute workflow after start (default: true) */
453
510
  autoExecute?: boolean;
454
511
  /** Custom scheduler configuration */
455
512
  scheduler?: Partial<SmartSchedulerConfig>;
513
+ /**
514
+ * Compensation handlers for saga pattern rollback.
515
+ * Keyed by stepId. Called in reverse order when a later step fails.
516
+ */
517
+ compensationHandlers?: WorkflowHandlers<unknown>;
456
518
  }
457
519
  /**
458
520
  * Core workflow execution engine.
@@ -509,6 +571,11 @@ declare class WorkflowEngine<TContext = Record<string, unknown>> {
509
571
  * @returns The updated workflow run
510
572
  */
511
573
  execute(runId: string): Promise<WorkflowRun<TContext>>;
574
+ /**
575
+ * Run saga compensation handlers for completed steps in reverse order.
576
+ * Called automatically when a workflow fails and compensation handlers are registered.
577
+ */
578
+ private runCompensation;
512
579
  private getOrThrow;
513
580
  private shouldContinueExecution;
514
581
  private executeNextStep;
@@ -607,8 +674,74 @@ declare class WorkflowEngine<TContext = Record<string, unknown>> {
607
674
  }
608
675
  //#endregion
609
676
  //#region src/workflow/define.d.ts
677
+ /**
678
+ * Per-step configuration for overriding timeout, retries, and conditions.
679
+ *
680
+ * `TContext` flows from `WorkflowConfig` — no manual annotation needed.
681
+ *
682
+ * @example
683
+ * ```typescript
684
+ * const workflow = createWorkflow('pipeline', {
685
+ * steps: {
686
+ * clone: {
687
+ * handler: async (ctx) => { ... },
688
+ * timeout: 120_000,
689
+ * retries: 5,
690
+ * },
691
+ * review: {
692
+ * handler: async (ctx) => { ... },
693
+ * timeout: 300_000,
694
+ * skipIf: (ctx) => ctx.autoApproved, // ctx is your TContext
695
+ * },
696
+ * },
697
+ * });
698
+ * ```
699
+ */
700
+ interface StepConfig<TOutput = unknown, TContext = Record<string, unknown>> {
701
+ handler: StepHandler<TOutput, TContext>;
702
+ timeout?: number;
703
+ retries?: number;
704
+ condition?: (context: TContext, run: WorkflowRun) => boolean | Promise<boolean>;
705
+ skipIf?: (context: TContext) => boolean | Promise<boolean>;
706
+ runIf?: (context: TContext) => boolean | Promise<boolean>;
707
+ /**
708
+ * Compensation handler (saga pattern rollback).
709
+ *
710
+ * Called in reverse order when a later step fails permanently.
711
+ * Only runs for steps that completed successfully (status: 'done').
712
+ *
713
+ * @example
714
+ * ```typescript
715
+ * charge: {
716
+ * handler: async (ctx) => stripe.charges.create({ amount: 100 }),
717
+ * onCompensate: async (ctx) => {
718
+ * const chargeId = ctx.getOutput<{ id: string }>('charge')?.id;
719
+ * await stripe.refunds.create({ charge: chargeId });
720
+ * },
721
+ * },
722
+ * ```
723
+ */
724
+ onCompensate?: StepHandler<unknown, TContext>;
725
+ }
726
+ /**
727
+ * Configuration for `createWorkflow()`.
728
+ *
729
+ * Steps can be plain async handlers or `StepConfig` objects with per-step
730
+ * timeout, retries, and conditions. Mix freely — `TContext` infers everywhere.
731
+ *
732
+ * @example
733
+ * ```typescript
734
+ * const config: WorkflowConfig<MyContext> = {
735
+ * steps: {
736
+ * fast: async (ctx) => ctx.context.value * 2,
737
+ * slow: { handler: async (ctx) => heavyWork(), timeout: 120_000 },
738
+ * },
739
+ * context: (input) => ({ value: input.n }),
740
+ * };
741
+ * ```
742
+ */
610
743
  interface WorkflowConfig<TContext, TInput = unknown> {
611
- steps: Record<string, StepHandler<unknown, TContext>>;
744
+ steps: Record<string, StepHandler<unknown, TContext> | StepConfig<unknown, TContext>>;
612
745
  context?: (input: TInput) => TContext;
613
746
  version?: string;
614
747
  defaults?: {
@@ -619,13 +752,25 @@ interface WorkflowConfig<TContext, TInput = unknown> {
619
752
  /** Optional custom container for dependency injection */
620
753
  container?: StreamlineContainer;
621
754
  }
622
- /** Options for waitFor method */
755
+ /** Options for `Workflow.waitFor()` */
623
756
  interface WaitForOptions {
624
- /** Poll interval in ms (default: 1000) */
757
+ /** Poll interval in ms @default 1000 */
625
758
  pollInterval?: number;
626
- /** Maximum time to wait in ms (default: no timeout) */
759
+ /** Maximum time to wait in ms @default undefined (no timeout) */
627
760
  timeout?: number;
628
761
  }
762
+ /**
763
+ * A running workflow instance returned by `createWorkflow()`.
764
+ *
765
+ * Exported so consumers can type variables without the `ReturnType<>` workaround.
766
+ *
767
+ * @example
768
+ * ```typescript
769
+ * import { createWorkflow, type Workflow } from '@classytic/streamline';
770
+ *
771
+ * export const myWorkflow: Workflow<MyCtx, MyInput> = createWorkflow('my', { ... });
772
+ * ```
773
+ */
629
774
  interface Workflow<TContext, TInput = unknown> {
630
775
  start: (input: TInput, meta?: Record<string, unknown>) => Promise<WorkflowRun<TContext>>;
631
776
  get: (runId: string) => Promise<WorkflowRun<TContext> | null>;
@@ -634,22 +779,6 @@ interface Workflow<TContext, TInput = unknown> {
634
779
  cancel: (runId: string) => Promise<WorkflowRun<TContext>>;
635
780
  pause: (runId: string) => Promise<WorkflowRun<TContext>>;
636
781
  rewindTo: (runId: string, stepId: string) => Promise<WorkflowRun<TContext>>;
637
- /**
638
- * Wait for a workflow to complete (reach a terminal state).
639
- * Polls the workflow status until it's done, failed, or cancelled.
640
- *
641
- * @param runId - Workflow run ID to wait for
642
- * @param options - Poll interval and timeout settings
643
- * @returns The completed workflow run
644
- * @throws {Error} If timeout is exceeded or workflow not found
645
- *
646
- * @example
647
- * ```typescript
648
- * const run = await workflow.start({ data: 'test' });
649
- * const completed = await workflow.waitFor(run._id);
650
- * console.log(completed.status); // 'done' | 'failed' | 'cancelled'
651
- * ```
652
- */
653
782
  waitFor: (runId: string, options?: WaitForOptions) => Promise<WorkflowRun<TContext>>;
654
783
  shutdown: () => void;
655
784
  definition: WorkflowDefinition<TContext>;
@@ -675,12 +804,17 @@ interface Workflow<TContext, TInput = unknown> {
675
804
  * await orderProcess.start({ id: '123', email: 'user@example.com' });
676
805
  * ```
677
806
  *
678
- * @example Custom container for testing
807
+ * @example Per-step configuration
679
808
  * ```typescript
680
- * const container = createContainer();
681
- * const workflow = createWorkflow('test-workflow', {
682
- * steps: { ... },
683
- * container
809
+ * const pipeline = createWorkflow('ci-pipeline', {
810
+ * steps: {
811
+ * clone: { handler: async (ctx) => { ... }, timeout: 120_000 },
812
+ * build: { handler: async (ctx) => { ... }, retries: 5 },
813
+ * deploy: {
814
+ * handler: async (ctx) => { ... },
815
+ * skipIf: (ctx) => !ctx.shouldDeploy,
816
+ * },
817
+ * },
684
818
  * });
685
819
  * ```
686
820
  */
@@ -714,37 +848,24 @@ interface HookResult {
714
848
  * Create a hook that pauses workflow until external input.
715
849
  * The token includes a crypto-random suffix for security.
716
850
  *
717
- * IMPORTANT: Pass the returned token to ctx.wait() to enable token validation:
718
- * ```typescript
719
- * const hook = createHook(ctx, 'approval');
720
- * return ctx.wait(hook.token, { hookToken: hook.token }); // Token stored for validation
721
- * ```
722
- *
723
851
  * @example
724
852
  * ```typescript
725
- * // In step handler
726
- * async function waitForApproval(ctx) {
727
- * const hook = createHook(ctx, 'waiting-for-approval');
728
- * console.log('Resume with token:', hook.token);
729
- * return ctx.wait(hook.token, { hookToken: hook.token }); // Token validated on resume
730
- * }
731
- *
732
- * // From API route
733
- * await resumeHook('token-123', { approved: true });
853
+ * const hook = createHook(ctx, 'waiting-for-approval');
854
+ * return ctx.wait(hook.token, { hookToken: hook.token });
734
855
  * ```
735
856
  */
736
857
  declare function createHook(ctx: StepContext, reason: string, options?: HookOptions): HookResult;
737
858
  /**
738
859
  * Resume a paused workflow by hook token.
739
860
  *
740
- * Security: If the workflow was paused with a hookToken in waitingFor.data,
741
- * this function validates the token before resuming.
861
+ * **Durable**: Works across process restarts and multi-worker deployments.
862
+ * - Fast path: Uses in-memory hookRegistry if the engine is in this process.
863
+ * - Fallback: Looks up the workflow in MongoDB and resumes via atomic DB operations.
742
864
  *
743
- * Multi-worker support: Falls back to DB lookup if engine not in local registry.
865
+ * Security: Validates the token against the stored hookToken if present.
744
866
  *
745
867
  * @example
746
868
  * ```typescript
747
- * // API route handler
748
869
  * app.post('/hooks/:token', async (req, res) => {
749
870
  * const result = await resumeHook(req.params.token, req.body);
750
871
  * res.json({ success: true, runId: result.runId, status: result.run.status });
@@ -760,7 +881,6 @@ declare function resumeHook(token: string, payload: unknown): Promise<{
760
881
  *
761
882
  * @example
762
883
  * ```typescript
763
- * // Slack bot - same channel always gets same token
764
884
  * const token = hookToken('slack', channelId);
765
885
  * const hook = createHook(ctx, 'slack-message', { token });
766
886
  * ```
@@ -769,10 +889,18 @@ declare function hookToken(...parts: string[]): string;
769
889
  //#endregion
770
890
  //#region src/execution/context.d.ts
771
891
  declare class WaitSignal extends Error {
772
- type: 'human' | 'webhook' | 'timer' | 'event';
892
+ type: 'human' | 'webhook' | 'timer' | 'event' | 'childWorkflow';
773
893
  reason: string;
774
894
  data?: unknown | undefined;
775
- constructor(type: 'human' | 'webhook' | 'timer' | 'event', reason: string, data?: unknown | undefined);
895
+ constructor(type: 'human' | 'webhook' | 'timer' | 'event' | 'childWorkflow', reason: string, data?: unknown | undefined);
896
+ }
897
+ /**
898
+ * Signal thrown by ctx.goto() to jump execution to a different step.
899
+ * Caught by the engine to update currentStepId.
900
+ */
901
+ declare class GotoSignal extends Error {
902
+ targetStepId: string;
903
+ constructor(targetStepId: string);
776
904
  }
777
905
  //#endregion
778
906
  //#region src/storage/run.model.d.ts
@@ -1016,7 +1144,7 @@ declare function getWorkflowProgress(run: WorkflowRun): WorkflowProgress;
1016
1144
  declare function getStepUIStates(run: WorkflowRun): StepUIState[];
1017
1145
  declare function getWaitingInfo(run: WorkflowRun): {
1018
1146
  stepId: string;
1019
- type: "human" | "webhook" | "timer" | "event";
1147
+ type: "human" | "webhook" | "timer" | "event" | "childWorkflow";
1020
1148
  reason: string;
1021
1149
  resumeAt: Date | undefined;
1022
1150
  eventName: string | undefined;
@@ -1586,4 +1714,4 @@ declare class MaxRetriesExceededError extends WorkflowError {
1586
1714
  constructor(stepId: string, attempts: number, runId?: string);
1587
1715
  }
1588
1716
  //#endregion
1589
- export { COMPUTED, type CacheHealthStatus, CommonQueries, type ConditionalStep, type ContainerOptions, DataCorruptionError, ErrorCode, type ErrorCode as ErrorCodeType, type ExecuteParallelOptions, type GetScheduledWorkflowsOptions, InferContext, InferHandlersContext, InvalidStateError, MaxRetriesExceededError, RUN_STATUS, RUN_STATUS_VALUES, RecurrencePattern, RunStatus, STEP_STATUS, STEP_STATUS_VALUES, type ScheduleWorkflowOptions, type SchedulerStats, SchedulingInfo, SchedulingService, type SchedulingServiceConfig, type SmartSchedulerConfig, Step, type StepContext, StepError, StepHandler, StepIds, StepNotFoundError, StepState, StepStatus, type StepTimeline, StepTimeoutError, type StepUIState, type StreamlineContainer, type TenantFilterOptions, type TimezoneCalculationResult, TimezoneHandler, TypedHandlers, WaitSignal, WaitingFor, WorkflowCache, WorkflowDefinition, type WorkflowDefinitionDoc, WorkflowDefinitionModel, WorkflowEngine, type WorkflowEngineOptions, WorkflowError, WorkflowEventBus, type WorkflowEventName, WorkflowEventPayload, WorkflowHandlers, WorkflowNotFoundError, type WorkflowProgress, WorkflowQueryBuilder, type WorkflowRepositoryConfig, type WorkflowRun, WorkflowRunModel, type WorkflowRunRepository, canRewindTo, conditions, createCondition, createContainer, createHook, createWorkflow, createWorkflowRepository, deriveRunStatus, executeParallel, getExecutionPath, getStepTimeline, getStepUIStates, getWaitingInfo, getWorkflowProgress, globalEventBus, hookRegistry, hookToken, isConditionalStep, isRunStatus, isStepStatus, isStreamlineContainer, isTerminalState, isValidRunTransition, isValidStepTransition, resumeHook, shouldSkipStep, singleTenantPlugin, tenantFilterPlugin, timezoneHandler, workflowDefinitionRepository, workflowRunRepository };
1717
+ export { type BaseEventPayload, COMPUTED, type CacheHealthStatus, CommonQueries, type ConditionalStep, type ContainerOptions, DataCorruptionError, type EngineErrorPayload, ErrorCode, type ErrorCode as ErrorCodeType, type EventPayloadMap, type ExecuteParallelOptions, type GetScheduledWorkflowsOptions, GotoSignal, type HookOptions, type HookResult, InferContext, InferHandlersContext, InvalidStateError, MaxRetriesExceededError, RUN_STATUS, RUN_STATUS_VALUES, RecurrencePattern, RunStatus, STEP_STATUS, STEP_STATUS_VALUES, type ScheduleWorkflowOptions, type SchedulerStats, SchedulingInfo, SchedulingService, type SchedulingServiceConfig, type SignalStore, type SmartSchedulerConfig, Step, type StepCompletedPayload, type StepConfig, type StepContext, StepError, type StepEventPayload, type StepFailedPayload, StepHandler, StepIds, StepNotFoundError, type StepRetryPayload, StepState, StepStatus, type StepTimeline, StepTimeoutError, type StepUIState, type StreamlineContainer, type TenantFilterOptions, type TimezoneCalculationResult, TimezoneHandler, TypedHandlers, type WaitForOptions, WaitSignal, WaitingFor, type Workflow, WorkflowCache, type WorkflowCompletedPayload, type WorkflowConfig, WorkflowDefinition, type WorkflowDefinitionDoc, WorkflowDefinitionModel, WorkflowEngine, type WorkflowEngineOptions, WorkflowError, WorkflowEventBus, type WorkflowEventName, WorkflowEventPayload, type WorkflowFailedPayload, WorkflowHandlers, WorkflowNotFoundError, type WorkflowProgress, WorkflowQueryBuilder, type WorkflowRepositoryConfig, type WorkflowResumedPayload, type WorkflowRun, WorkflowRunModel, type WorkflowRunRepository, canRewindTo, conditions, createCondition, createContainer, createHook, createWorkflow, createWorkflowRepository, deriveRunStatus, executeParallel, getExecutionPath, getStepTimeline, getStepUIStates, getWaitingInfo, getWorkflowProgress, globalEventBus, hookRegistry, hookToken, isConditionalStep, isRunStatus, isStepStatus, isStreamlineContainer, isTerminalState, isValidRunTransition, isValidStepTransition, resumeHook, shouldSkipStep, singleTenantPlugin, tenantFilterPlugin, timezoneHandler, workflowDefinitionRepository, workflowRegistry, workflowRunRepository };