@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.
@@ -1664,30 +1664,6 @@ function setEntry(storage, location, entry) {
1664
1664
  storage.history.entries.set(key, entry);
1665
1665
  }
1666
1666
 
1667
- // src/utils.ts
1668
- function sleep(ms) {
1669
- return new Promise((resolve) => setTimeout(resolve, ms));
1670
- }
1671
- var TIMEOUT_MAX = 2147483647;
1672
- function setLongTimeout(listener, after) {
1673
- let timeout;
1674
- function start(remaining) {
1675
- if (remaining <= TIMEOUT_MAX) {
1676
- timeout = setTimeout(listener, remaining);
1677
- } else {
1678
- timeout = setTimeout(() => {
1679
- start(remaining - TIMEOUT_MAX);
1680
- }, TIMEOUT_MAX);
1681
- }
1682
- }
1683
- start(after);
1684
- return {
1685
- abort: () => {
1686
- if (timeout !== void 0) clearTimeout(timeout);
1687
- }
1688
- };
1689
- }
1690
-
1691
1667
  // src/context.ts
1692
1668
  var DEFAULT_MAX_RETRIES = 3;
1693
1669
  var DEFAULT_RETRY_BACKOFF_BASE = 100;
@@ -1963,7 +1939,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
1963
1939
  * Throws HistoryDivergedError if duplicate detected.
1964
1940
  */
1965
1941
  checkDuplicateName(name) {
1966
- const fullKey = locationToKey(this.storage, this.currentLocation) + "/" + name;
1942
+ const fullKey = `${locationToKey(this.storage, this.currentLocation)}/${name}`;
1967
1943
  if (this.usedNamesInExecution.has(fullKey)) {
1968
1944
  throw new HistoryDivergedError(
1969
1945
  `Duplicate entry name "${name}" at location "${locationToKey(this.storage, this.currentLocation)}". Each step/loop/sleep/queue.next/join/race must have a unique name within its scope.`
@@ -2017,7 +1993,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
2017
1993
  validateComplete() {
2018
1994
  const prefix = locationToKey(this.storage, this.currentLocation);
2019
1995
  for (const key of this.storage.history.entries.keys()) {
2020
- const isUnderPrefix = prefix === "" ? true : key.startsWith(prefix + "/") || key === prefix;
1996
+ const isUnderPrefix = prefix === "" ? true : key.startsWith(`${prefix}/`) || key === prefix;
2021
1997
  if (isUnderPrefix) {
2022
1998
  if (!this.visitedKeys.has(key)) {
2023
1999
  throw new HistoryDivergedError(
@@ -2034,25 +2010,33 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
2034
2010
  this.abortController.abort(new EvictedError());
2035
2011
  }
2036
2012
  /**
2037
- * Wait for eviction message.
2038
- *
2039
- * The event listener uses { once: true } to auto-remove after firing,
2040
- * preventing memory leaks if this method is called multiple times.
2013
+ * Wait for `ms`, rejecting early with EvictedError if the workflow is
2014
+ * evicted. Both the timer and the abort listener are torn down on either
2015
+ * outcome, so a completed sleep never leaves a dangling listener on the
2016
+ * long-lived run abort signal.
2041
2017
  */
2042
- waitForEviction() {
2043
- return new Promise((_, reject) => {
2044
- if (this.abortSignal.aborted) {
2045
- reject(new EvictedError());
2046
- return;
2018
+ async sleepOrEvict(ms) {
2019
+ if (this.abortSignal.aborted) {
2020
+ throw new EvictedError();
2021
+ }
2022
+ let timer;
2023
+ let onAbort;
2024
+ try {
2025
+ await new Promise((resolve, reject) => {
2026
+ timer = setTimeout(resolve, ms);
2027
+ onAbort = () => reject(new EvictedError());
2028
+ this.abortSignal.addEventListener("abort", onAbort, {
2029
+ once: true
2030
+ });
2031
+ });
2032
+ } finally {
2033
+ if (timer !== void 0) {
2034
+ clearTimeout(timer);
2047
2035
  }
2048
- this.abortSignal.addEventListener(
2049
- "abort",
2050
- () => {
2051
- reject(new EvictedError());
2052
- },
2053
- { once: true }
2054
- );
2055
- });
2036
+ if (onAbort) {
2037
+ this.abortSignal.removeEventListener("abort", onAbort);
2038
+ }
2039
+ }
2056
2040
  }
2057
2041
  // === Step ===
2058
2042
  async step(nameOrConfig, run) {
@@ -2201,7 +2185,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
2201
2185
  }
2202
2186
  const maxRetries2 = config2.maxRetries ?? DEFAULT_MAX_RETRIES;
2203
2187
  if (metadata2.attempts > maxRetries2) {
2204
- const lastError = stepData.error ?? metadata2.error;
2188
+ const lastError = metadata2.error;
2205
2189
  const exhaustedError = new StepExhaustedError(
2206
2190
  config2.name,
2207
2191
  lastError
@@ -2290,14 +2274,13 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
2290
2274
  });
2291
2275
  return output;
2292
2276
  } catch (error) {
2293
- if (error instanceof StepTimeoutError) {
2294
- if (entry.kind.type === "step") {
2295
- entry.kind.data.error = String(error);
2296
- }
2297
- entry.dirty = true;
2277
+ if (entry.kind.type === "step") {
2278
+ entry.kind.data.error = String(error);
2279
+ }
2280
+ entry.dirty = true;
2281
+ if (error instanceof StepTimeoutError && !config2.retryOnTimeout) {
2298
2282
  metadata.status = "exhausted";
2299
2283
  metadata.error = String(error);
2300
- await this.flushStorage();
2301
2284
  await this.notifyStepError(config2, metadata.attempts, error, {
2302
2285
  willRetry: false
2303
2286
  });
@@ -2311,13 +2294,8 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
2311
2294
  );
2312
2295
  }
2313
2296
  if (error instanceof CriticalError || error instanceof RollbackError) {
2314
- if (entry.kind.type === "step") {
2315
- entry.kind.data.error = String(error);
2316
- }
2317
- entry.dirty = true;
2318
2297
  metadata.status = "exhausted";
2319
2298
  metadata.error = String(error);
2320
- await this.flushStorage();
2321
2299
  await this.notifyStepError(config2, metadata.attempts, error, {
2322
2300
  willRetry: false
2323
2301
  });
@@ -2330,14 +2308,9 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
2330
2308
  })
2331
2309
  );
2332
2310
  }
2333
- if (entry.kind.type === "step") {
2334
- entry.kind.data.error = String(error);
2335
- }
2336
- entry.dirty = true;
2337
2311
  const willRetry = metadata.attempts <= maxRetries;
2338
2312
  metadata.status = willRetry ? "failed" : "exhausted";
2339
2313
  metadata.error = String(error);
2340
- await this.flushStorage();
2341
2314
  if (willRetry) {
2342
2315
  const retryDelay = calculateBackoff(
2343
2316
  metadata.attempts,
@@ -2361,7 +2334,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
2361
2334
  attachTryStepFailure(
2362
2335
  new StepExhaustedError(config2.name, String(error)),
2363
2336
  {
2364
- kind: "exhausted",
2337
+ kind: error instanceof StepTimeoutError ? "timeout" : "exhausted",
2365
2338
  stepName: config2.name,
2366
2339
  attempts: metadata.attempts,
2367
2340
  error: extractErrorInfo(error)
@@ -2379,7 +2352,9 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
2379
2352
  *
2380
2353
  * Note: This does NOT cancel the underlying operation. JavaScript Promises
2381
2354
  * cannot be cancelled once started. When a timeout occurs:
2382
- * - The step is marked as failed with StepTimeoutError
2355
+ * - The step is rejected with StepTimeoutError. By default this is treated
2356
+ * as a critical failure with no retry. Set retryOnTimeout: true on the
2357
+ * step config to retry timeouts like any other error.
2383
2358
  * - The underlying async operation continues running in the background
2384
2359
  * - Any side effects from the operation may still occur
2385
2360
  *
@@ -2704,7 +2679,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
2704
2679
  return;
2705
2680
  }
2706
2681
  if (remaining < this.driver.workerPollInterval) {
2707
- await Promise.race([sleep(remaining), this.waitForEviction()]);
2682
+ await this.sleepOrEvict(remaining);
2708
2683
  this.checkEvicted();
2709
2684
  if (entry.kind.type === "sleep") {
2710
2685
  entry.kind.data.state = "completed";
@@ -3515,6 +3490,30 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
3515
3490
  }
3516
3491
  };
3517
3492
 
3493
+ // src/utils.ts
3494
+ function sleep(ms) {
3495
+ return new Promise((resolve) => setTimeout(resolve, ms));
3496
+ }
3497
+ var TIMEOUT_MAX = 2147483647;
3498
+ function setLongTimeout(listener, after) {
3499
+ let timeout;
3500
+ function start(remaining) {
3501
+ if (remaining <= TIMEOUT_MAX) {
3502
+ timeout = setTimeout(listener, remaining);
3503
+ } else {
3504
+ timeout = setTimeout(() => {
3505
+ start(remaining - TIMEOUT_MAX);
3506
+ }, TIMEOUT_MAX);
3507
+ }
3508
+ }
3509
+ start(after);
3510
+ return {
3511
+ abort: () => {
3512
+ if (timeout !== void 0) clearTimeout(timeout);
3513
+ }
3514
+ };
3515
+ }
3516
+
3518
3517
  // src/index.ts
3519
3518
  var Loop = {
3520
3519
  continue: (state) => ({
@@ -3738,25 +3737,41 @@ async function executeLiveWorkflow(workflowId, workflowFn, input, driver, messag
3738
3737
  const hasMessages = result.waitingForMessages !== void 0;
3739
3738
  const hasDeadline = result.sleepUntil !== void 0;
3740
3739
  if (hasMessages && hasDeadline) {
3740
+ const iterationAbort = new AbortController();
3741
+ const onRunAbort = () => iterationAbort.abort();
3742
+ if (abortController.signal.aborted) {
3743
+ iterationAbort.abort();
3744
+ } else {
3745
+ abortController.signal.addEventListener("abort", onRunAbort, {
3746
+ once: true
3747
+ });
3748
+ }
3749
+ const messagePromise = awaitWithEviction(
3750
+ driver.waitForMessages(
3751
+ result.waitingForMessages,
3752
+ iterationAbort.signal
3753
+ ),
3754
+ iterationAbort.signal
3755
+ );
3756
+ const sleepPromise = waitForSleep(
3757
+ runtime,
3758
+ result.sleepUntil,
3759
+ iterationAbort.signal
3760
+ );
3761
+ messagePromise.catch(() => {
3762
+ });
3763
+ sleepPromise.catch(() => {
3764
+ });
3741
3765
  try {
3742
- const messagePromise = awaitWithEviction(
3743
- driver.waitForMessages(
3744
- result.waitingForMessages,
3745
- abortController.signal
3746
- ),
3747
- abortController.signal
3748
- );
3749
- const sleepPromise = waitForSleep(
3750
- runtime,
3751
- result.sleepUntil,
3752
- abortController.signal
3753
- );
3754
3766
  await Promise.race([messagePromise, sleepPromise]);
3755
3767
  } catch (error) {
3756
3768
  if (error instanceof EvictedError) {
3757
3769
  return lastResult;
3758
3770
  }
3759
3771
  throw error;
3772
+ } finally {
3773
+ iterationAbort.abort();
3774
+ abortController.signal.removeEventListener("abort", onRunAbort);
3760
3775
  }
3761
3776
  continue;
3762
3777
  }
@@ -4199,15 +4214,15 @@ export {
4199
4214
  deleteEntriesWithPrefix,
4200
4215
  getEntry,
4201
4216
  setEntry,
4202
- sleep,
4203
4217
  DEFAULT_MAX_RETRIES,
4204
4218
  DEFAULT_RETRY_BACKOFF_BASE,
4205
4219
  DEFAULT_RETRY_BACKOFF_MAX,
4206
4220
  DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL,
4207
4221
  DEFAULT_STEP_TIMEOUT,
4208
4222
  WorkflowContextImpl,
4223
+ sleep,
4209
4224
  Loop,
4210
4225
  runWorkflow,
4211
4226
  replayWorkflowFromStep
4212
4227
  };
4213
- //# sourceMappingURL=chunk-CIDHCIH7.js.map
4228
+ //# sourceMappingURL=chunk-KA2T56AJ.js.map