@rivetkit/workflow-engine 2.3.0-rc.9 → 2.3.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/src/index.ts CHANGED
@@ -13,6 +13,7 @@ export {
13
13
  } from "./context.js";
14
14
  // Driver
15
15
  export type { EngineDriver, KVEntry, KVWrite } from "./driver.js";
16
+ export { extractErrorInfo } from "./error-utils.js";
16
17
  // Errors
17
18
  export {
18
19
  CancelledError,
@@ -29,7 +30,6 @@ export {
29
30
  StepExhaustedError,
30
31
  StepFailedError,
31
32
  } from "./errors.js";
32
- export { extractErrorInfo } from "./error-utils.js";
33
33
 
34
34
  // Location utilities
35
35
  export {
@@ -82,9 +82,6 @@ export type {
82
82
  PathSegment,
83
83
  RaceEntry,
84
84
  RemovedEntry,
85
- WorkflowEntryMetadataSnapshot,
86
- WorkflowHistoryEntry,
87
- WorkflowHistorySnapshot,
88
85
  RollbackCheckpointEntry,
89
86
  RollbackContextInterface,
90
87
  RunWorkflowOptions,
@@ -102,20 +99,23 @@ export type {
102
99
  TryStepFailure,
103
100
  TryStepResult,
104
101
  WorkflowContextInterface,
102
+ WorkflowEntryMetadataSnapshot,
105
103
  WorkflowError,
106
104
  WorkflowErrorEvent,
107
105
  WorkflowErrorHandler,
108
106
  WorkflowFunction,
109
107
  WorkflowHandle,
110
- WorkflowRollbackErrorEvent,
108
+ WorkflowHistoryEntry,
109
+ WorkflowHistorySnapshot,
110
+ WorkflowMessageDriver,
111
111
  WorkflowQueue,
112
112
  WorkflowQueueMessage,
113
113
  WorkflowQueueNextBatchOptions,
114
114
  WorkflowQueueNextOptions,
115
- WorkflowMessageDriver,
116
115
  WorkflowResult,
117
- WorkflowRunMode,
116
+ WorkflowRollbackErrorEvent,
118
117
  WorkflowRunErrorEvent,
118
+ WorkflowRunMode,
119
119
  WorkflowState,
120
120
  WorkflowStepErrorEvent,
121
121
  } from "./types.js";
@@ -185,9 +185,9 @@ import type {
185
185
  RunWorkflowOptions,
186
186
  Storage,
187
187
  WorkflowErrorEvent,
188
- WorkflowHistorySnapshot,
189
188
  WorkflowFunction,
190
189
  WorkflowHandle,
190
+ WorkflowHistorySnapshot,
191
191
  WorkflowMessageDriver,
192
192
  WorkflowResult,
193
193
  WorkflowRunMode,
@@ -532,26 +532,46 @@ async function executeLiveWorkflow<TInput, TOutput>(
532
532
  const hasDeadline = result.sleepUntil !== undefined;
533
533
 
534
534
  if (hasMessages && hasDeadline) {
535
- // Wait for EITHER a message OR the deadline (for queue.next timeout)
535
+ // Wait for EITHER a message OR the deadline (for queue.next timeout).
536
+ // Scope both waiters to a per-iteration controller chained to the run
537
+ // signal so that whichever loses the race is cancelled immediately.
538
+ // Otherwise the loser (a message waiter or an armed sleep timer) stays
539
+ // registered on the long-lived run signal and leaks once per cycle.
540
+ const iterationAbort = new AbortController();
541
+ const onRunAbort = () => iterationAbort.abort();
542
+ if (abortController.signal.aborted) {
543
+ iterationAbort.abort();
544
+ } else {
545
+ abortController.signal.addEventListener("abort", onRunAbort, {
546
+ once: true,
547
+ });
548
+ }
549
+ const messagePromise = awaitWithEviction(
550
+ driver.waitForMessages(
551
+ result.waitingForMessages!,
552
+ iterationAbort.signal,
553
+ ),
554
+ iterationAbort.signal,
555
+ );
556
+ const sleepPromise = waitForSleep(
557
+ runtime,
558
+ result.sleepUntil!,
559
+ iterationAbort.signal,
560
+ );
561
+ // Swallow the loser's rejection once it is aborted below so it does
562
+ // not surface as an unhandled rejection.
563
+ messagePromise.catch(() => {});
564
+ sleepPromise.catch(() => {});
536
565
  try {
537
- const messagePromise = awaitWithEviction(
538
- driver.waitForMessages(
539
- result.waitingForMessages!,
540
- abortController.signal,
541
- ),
542
- abortController.signal,
543
- );
544
- const sleepPromise = waitForSleep(
545
- runtime,
546
- result.sleepUntil!,
547
- abortController.signal,
548
- );
549
566
  await Promise.race([messagePromise, sleepPromise]);
550
567
  } catch (error) {
551
568
  if (error instanceof EvictedError) {
552
569
  return lastResult;
553
570
  }
554
571
  throw error;
572
+ } finally {
573
+ iterationAbort.abort();
574
+ abortController.signal.removeEventListener("abort", onRunAbort);
555
575
  }
556
576
  continue;
557
577
  }
package/src/location.ts CHANGED
@@ -159,7 +159,7 @@ export function getChildEntries(
159
159
  const isChild =
160
160
  parentKey === ""
161
161
  ? true
162
- : key.startsWith(parentKey + "/") || key === parentKey;
162
+ : key.startsWith(`${parentKey}/`) || key === parentKey;
163
163
 
164
164
  if (isChild) {
165
165
  // Return the actual entry's location, not the parent location
package/src/types.ts CHANGED
@@ -399,6 +399,8 @@ export interface StepConfig<T> {
399
399
  retryBackoffMax?: number;
400
400
  /** Timeout in ms for step execution (default: 30000). Set to 0 to disable. */
401
401
  timeout?: number;
402
+ /** If true, step timeouts retry like any other error instead of failing immediately as critical. Default: false. */
403
+ retryOnTimeout?: boolean;
402
404
  }
403
405
 
404
406
  export type TryStepCatchKind =
@@ -455,7 +457,7 @@ export type LoopResult<S, T> =
455
457
  * `Loop.continue(undefined)`.
456
458
  */
457
459
  export type LoopIterationResult<S, T> = Promise<
458
- LoopResult<S, T> | (S extends undefined ? void : never)
460
+ LoopResult<S, T> | (S extends undefined ? undefined : never)
459
461
  >;
460
462
 
461
463
  /**