@rivetkit/workflow-engine 0.0.0-main.6151ab0 → 0.0.0-main.78336a1

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.
@@ -446,6 +446,23 @@ export function writeRemovedEntry(bc: bare.ByteCursor, x: RemovedEntry): void {
446
446
  write1(bc, x.originalName)
447
447
  }
448
448
 
449
+ export type VersionCheckEntry = {
450
+ readonly resolved: u32,
451
+ readonly latest: u32,
452
+ }
453
+
454
+ export function readVersionCheckEntry(bc: bare.ByteCursor): VersionCheckEntry {
455
+ return {
456
+ resolved: bare.readU32(bc),
457
+ latest: bare.readU32(bc),
458
+ }
459
+ }
460
+
461
+ export function writeVersionCheckEntry(bc: bare.ByteCursor, x: VersionCheckEntry): void {
462
+ bare.writeU32(bc, x.resolved)
463
+ bare.writeU32(bc, x.latest)
464
+ }
465
+
449
466
  export type EntryKind =
450
467
  | { readonly tag: "StepEntry", readonly val: StepEntry }
451
468
  | { readonly tag: "LoopEntry", readonly val: LoopEntry }
@@ -455,6 +472,7 @@ export type EntryKind =
455
472
  | { readonly tag: "JoinEntry", readonly val: JoinEntry }
456
473
  | { readonly tag: "RaceEntry", readonly val: RaceEntry }
457
474
  | { readonly tag: "RemovedEntry", readonly val: RemovedEntry }
475
+ | { readonly tag: "VersionCheckEntry", readonly val: VersionCheckEntry }
458
476
 
459
477
  export function readEntryKind(bc: bare.ByteCursor): EntryKind {
460
478
  const offset = bc.offset
@@ -476,6 +494,8 @@ export function readEntryKind(bc: bare.ByteCursor): EntryKind {
476
494
  return { tag: "RaceEntry", val: readRaceEntry(bc) }
477
495
  case 7:
478
496
  return { tag: "RemovedEntry", val: readRemovedEntry(bc) }
497
+ case 8:
498
+ return { tag: "VersionCheckEntry", val: readVersionCheckEntry(bc) }
479
499
  default: {
480
500
  bc.offset = offset
481
501
  throw new bare.BareError(offset, "invalid tag")
@@ -525,6 +545,11 @@ export function writeEntryKind(bc: bare.ByteCursor, x: EntryKind): void {
525
545
  writeRemovedEntry(bc, x.val)
526
546
  break
527
547
  }
548
+ case "VersionCheckEntry": {
549
+ bare.writeU8(bc, 8)
550
+ writeVersionCheckEntry(bc, x.val)
551
+ break
552
+ }
528
553
  }
529
554
  }
530
555
 
@@ -670,6 +670,16 @@ function writeRemovedEntry(bc, x) {
670
670
  bare.writeString(bc, x.originalType);
671
671
  write1(bc, x.originalName);
672
672
  }
673
+ function readVersionCheckEntry(bc) {
674
+ return {
675
+ resolved: bare.readU32(bc),
676
+ latest: bare.readU32(bc)
677
+ };
678
+ }
679
+ function writeVersionCheckEntry(bc, x) {
680
+ bare.writeU32(bc, x.resolved);
681
+ bare.writeU32(bc, x.latest);
682
+ }
673
683
  function readEntryKind(bc) {
674
684
  const offset = bc.offset;
675
685
  const tag = bare.readU8(bc);
@@ -690,6 +700,8 @@ function readEntryKind(bc) {
690
700
  return { tag: "RaceEntry", val: readRaceEntry(bc) };
691
701
  case 7:
692
702
  return { tag: "RemovedEntry", val: readRemovedEntry(bc) };
703
+ case 8:
704
+ return { tag: "VersionCheckEntry", val: readVersionCheckEntry(bc) };
693
705
  default: {
694
706
  bc.offset = offset;
695
707
  throw new bare.BareError(offset, "invalid tag");
@@ -738,6 +750,11 @@ function writeEntryKind(bc, x) {
738
750
  writeRemovedEntry(bc, x.val);
739
751
  break;
740
752
  }
753
+ case "VersionCheckEntry": {
754
+ bare.writeU8(bc, 8);
755
+ writeVersionCheckEntry(bc, x.val);
756
+ break;
757
+ }
741
758
  }
742
759
  }
743
760
  function readEntry(bc) {
@@ -1182,6 +1199,14 @@ function entryKindToBare(kind) {
1182
1199
  originalName: _nullishCoalesce(kind.data.originalName, () => ( null))
1183
1200
  }
1184
1201
  };
1202
+ case "version_check":
1203
+ return {
1204
+ tag: "VersionCheckEntry",
1205
+ val: {
1206
+ resolved: kind.data.resolved,
1207
+ latest: kind.data.latest
1208
+ }
1209
+ };
1185
1210
  }
1186
1211
  }
1187
1212
  function entryKindFromBare(kind) {
@@ -1263,6 +1288,14 @@ function entryKindFromBare(kind) {
1263
1288
  originalName: _nullishCoalesce(kind.val.originalName, () => ( void 0))
1264
1289
  }
1265
1290
  };
1291
+ case "VersionCheckEntry":
1292
+ return {
1293
+ type: "version_check",
1294
+ data: {
1295
+ resolved: kind.val.resolved,
1296
+ latest: kind.val.latest
1297
+ }
1298
+ };
1266
1299
  default:
1267
1300
  throw new Error(
1268
1301
  `Unknown entry kind: ${kind.tag}`
@@ -1402,6 +1435,9 @@ function deserializeName(bytes) {
1402
1435
  }
1403
1436
 
1404
1437
  // src/storage.ts
1438
+ var MAX_KV_BATCH_ENTRIES = 128;
1439
+ var MAX_KV_BATCH_PAYLOAD_BYTES = 976 * 1024;
1440
+ var MAX_CONCURRENT_DELETES = 64;
1405
1441
  function createStorage() {
1406
1442
  return {
1407
1443
  nameRegistry: [],
@@ -1519,6 +1555,8 @@ async function loadMetadata(storage, driver, entryId) {
1519
1555
  }
1520
1556
  async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
1521
1557
  const writes = [];
1558
+ const dirtyEntries = [];
1559
+ const dirtyMetadata = [];
1522
1560
  let historyUpdated = false;
1523
1561
  for (let i = storage.flushedNameCount; i < storage.nameRegistry.length; i++) {
1524
1562
  const name = storage.nameRegistry[i];
@@ -1536,7 +1574,7 @@ async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
1536
1574
  key: buildHistoryKey(entry.location),
1537
1575
  value: serializeEntry(entry)
1538
1576
  });
1539
- entry.dirty = false;
1577
+ dirtyEntries.push(entry);
1540
1578
  historyUpdated = true;
1541
1579
  }
1542
1580
  }
@@ -1546,7 +1584,7 @@ async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
1546
1584
  key: buildEntryMetadataKey(id),
1547
1585
  value: serializeEntryMetadata(metadata)
1548
1586
  });
1549
- metadata.dirty = false;
1587
+ dirtyMetadata.push(metadata);
1550
1588
  historyUpdated = true;
1551
1589
  }
1552
1590
  }
@@ -1570,24 +1608,26 @@ async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
1570
1608
  });
1571
1609
  }
1572
1610
  if (writes.length > 0) {
1573
- await driver.batch(writes);
1611
+ if (driver.atomicBatch) {
1612
+ await driver.batch(writes);
1613
+ } else {
1614
+ for (const chunk of splitBatchWrites(writes)) {
1615
+ await driver.batch(chunk);
1616
+ }
1617
+ }
1574
1618
  }
1575
1619
  if (pendingDeletions) {
1576
- const deleteOps = [];
1577
- for (const prefix of pendingDeletions.prefixes) {
1578
- deleteOps.push(driver.deletePrefix(prefix));
1579
- }
1580
- for (const range of pendingDeletions.ranges) {
1581
- deleteOps.push(driver.deleteRange(range.start, range.end));
1582
- }
1583
- for (const key of pendingDeletions.keys) {
1584
- deleteOps.push(driver.delete(key));
1585
- }
1586
- if (deleteOps.length > 0) {
1587
- await Promise.all(deleteOps);
1620
+ const didChange = await runDeletes(driver, pendingDeletions);
1621
+ if (didChange) {
1588
1622
  historyUpdated = true;
1589
1623
  }
1590
1624
  }
1625
+ for (const entry of dirtyEntries) {
1626
+ entry.dirty = false;
1627
+ }
1628
+ for (const metadata of dirtyMetadata) {
1629
+ metadata.dirty = false;
1630
+ }
1591
1631
  storage.flushedNameCount = storage.nameRegistry.length;
1592
1632
  storage.flushedState = storage.state;
1593
1633
  storage.flushedOutput = storage.output;
@@ -1596,10 +1636,60 @@ async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
1596
1636
  onHistoryUpdated();
1597
1637
  }
1598
1638
  }
1639
+ function splitBatchWrites(writes) {
1640
+ const chunks = [];
1641
+ let chunk = [];
1642
+ let chunkBytes = 0;
1643
+ for (const write of writes) {
1644
+ const writeBytes = write.key.byteLength + write.value.byteLength;
1645
+ if (writeBytes > MAX_KV_BATCH_PAYLOAD_BYTES) {
1646
+ throw new Error(
1647
+ `KV batch write is ${writeBytes} bytes, exceeding the ${MAX_KV_BATCH_PAYLOAD_BYTES} byte limit`
1648
+ );
1649
+ }
1650
+ if (chunk.length >= MAX_KV_BATCH_ENTRIES || chunk.length > 0 && chunkBytes + writeBytes > MAX_KV_BATCH_PAYLOAD_BYTES) {
1651
+ chunks.push(chunk);
1652
+ chunk = [];
1653
+ chunkBytes = 0;
1654
+ }
1655
+ chunk.push(write);
1656
+ chunkBytes += writeBytes;
1657
+ }
1658
+ if (chunk.length > 0) {
1659
+ chunks.push(chunk);
1660
+ }
1661
+ return chunks;
1662
+ }
1663
+ function splitBatchDeletes(keys) {
1664
+ const chunks = [];
1665
+ for (let i = 0; i < keys.length; i += MAX_KV_BATCH_ENTRIES) {
1666
+ chunks.push(keys.slice(i, i + MAX_KV_BATCH_ENTRIES));
1667
+ }
1668
+ return chunks;
1669
+ }
1670
+ async function runDeletes(driver, deletions) {
1671
+ const ops = [
1672
+ ...deletions.prefixes.map((prefix) => () => driver.deletePrefix(prefix)),
1673
+ ...deletions.ranges.map(
1674
+ (range) => () => driver.deleteRange(range.start, range.end)
1675
+ ),
1676
+ ...splitBatchDeletes(deletions.keys).map(
1677
+ (chunk) => () => driver.batchDelete(chunk)
1678
+ )
1679
+ ];
1680
+ if (ops.length === 0) {
1681
+ return false;
1682
+ }
1683
+ for (let i = 0; i < ops.length; i += MAX_CONCURRENT_DELETES) {
1684
+ await Promise.all(
1685
+ ops.slice(i, i + MAX_CONCURRENT_DELETES).map((op) => op())
1686
+ );
1687
+ }
1688
+ return true;
1689
+ }
1599
1690
  async function deleteEntriesWithPrefix(storage, driver, prefixLocation, onHistoryUpdated) {
1600
1691
  const deletions = collectDeletionsForPrefix(storage, prefixLocation);
1601
- await driver.deletePrefix(deletions.prefixes[0]);
1602
- await Promise.all(deletions.keys.map((key) => driver.delete(key)));
1692
+ await runDeletes(driver, deletions);
1603
1693
  if (deletions.keys.length > 0 && onHistoryUpdated) {
1604
1694
  onHistoryUpdated();
1605
1695
  }
@@ -1628,30 +1718,6 @@ function setEntry(storage, location, entry) {
1628
1718
  storage.history.entries.set(key, entry);
1629
1719
  }
1630
1720
 
1631
- // src/utils.ts
1632
- function sleep(ms) {
1633
- return new Promise((resolve) => setTimeout(resolve, ms));
1634
- }
1635
- var TIMEOUT_MAX = 2147483647;
1636
- function setLongTimeout(listener, after) {
1637
- let timeout;
1638
- function start(remaining) {
1639
- if (remaining <= TIMEOUT_MAX) {
1640
- timeout = setTimeout(listener, remaining);
1641
- } else {
1642
- timeout = setTimeout(() => {
1643
- start(remaining - TIMEOUT_MAX);
1644
- }, TIMEOUT_MAX);
1645
- }
1646
- }
1647
- start(after);
1648
- return {
1649
- abort: () => {
1650
- if (timeout !== void 0) clearTimeout(timeout);
1651
- }
1652
- };
1653
- }
1654
-
1655
1721
  // src/context.ts
1656
1722
  var DEFAULT_MAX_RETRIES = 3;
1657
1723
  var DEFAULT_RETRY_BACKOFF_BASE = 100;
@@ -1927,7 +1993,7 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
1927
1993
  * Throws HistoryDivergedError if duplicate detected.
1928
1994
  */
1929
1995
  checkDuplicateName(name) {
1930
- const fullKey = locationToKey(this.storage, this.currentLocation) + "/" + name;
1996
+ const fullKey = `${locationToKey(this.storage, this.currentLocation)}/${name}`;
1931
1997
  if (this.usedNamesInExecution.has(fullKey)) {
1932
1998
  throw new HistoryDivergedError(
1933
1999
  `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.`
@@ -1981,7 +2047,7 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
1981
2047
  validateComplete() {
1982
2048
  const prefix = locationToKey(this.storage, this.currentLocation);
1983
2049
  for (const key of this.storage.history.entries.keys()) {
1984
- const isUnderPrefix = prefix === "" ? true : key.startsWith(prefix + "/") || key === prefix;
2050
+ const isUnderPrefix = prefix === "" ? true : key.startsWith(`${prefix}/`) || key === prefix;
1985
2051
  if (isUnderPrefix) {
1986
2052
  if (!this.visitedKeys.has(key)) {
1987
2053
  throw new HistoryDivergedError(
@@ -1998,25 +2064,33 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
1998
2064
  this.abortController.abort(new EvictedError());
1999
2065
  }
2000
2066
  /**
2001
- * Wait for eviction message.
2002
- *
2003
- * The event listener uses { once: true } to auto-remove after firing,
2004
- * preventing memory leaks if this method is called multiple times.
2067
+ * Wait for `ms`, rejecting early with EvictedError if the workflow is
2068
+ * evicted. Both the timer and the abort listener are torn down on either
2069
+ * outcome, so a completed sleep never leaves a dangling listener on the
2070
+ * long-lived run abort signal.
2005
2071
  */
2006
- waitForEviction() {
2007
- return new Promise((_, reject) => {
2008
- if (this.abortSignal.aborted) {
2009
- reject(new EvictedError());
2010
- return;
2072
+ async sleepOrEvict(ms) {
2073
+ if (this.abortSignal.aborted) {
2074
+ throw new EvictedError();
2075
+ }
2076
+ let timer;
2077
+ let onAbort;
2078
+ try {
2079
+ await new Promise((resolve, reject) => {
2080
+ timer = setTimeout(resolve, ms);
2081
+ onAbort = () => reject(new EvictedError());
2082
+ this.abortSignal.addEventListener("abort", onAbort, {
2083
+ once: true
2084
+ });
2085
+ });
2086
+ } finally {
2087
+ if (timer !== void 0) {
2088
+ clearTimeout(timer);
2011
2089
  }
2012
- this.abortSignal.addEventListener(
2013
- "abort",
2014
- () => {
2015
- reject(new EvictedError());
2016
- },
2017
- { once: true }
2018
- );
2019
- });
2090
+ if (onAbort) {
2091
+ this.abortSignal.removeEventListener("abort", onAbort);
2092
+ }
2093
+ }
2020
2094
  }
2021
2095
  // === Step ===
2022
2096
  async step(nameOrConfig, run) {
@@ -2165,7 +2239,7 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
2165
2239
  }
2166
2240
  const maxRetries2 = _nullishCoalesce(config2.maxRetries, () => ( DEFAULT_MAX_RETRIES));
2167
2241
  if (metadata2.attempts > maxRetries2) {
2168
- const lastError = _nullishCoalesce(stepData.error, () => ( metadata2.error));
2242
+ const lastError = metadata2.error;
2169
2243
  const exhaustedError = new StepExhaustedError(
2170
2244
  config2.name,
2171
2245
  lastError
@@ -2254,37 +2328,28 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
2254
2328
  });
2255
2329
  return output;
2256
2330
  } catch (error) {
2257
- if (error instanceof StepTimeoutError) {
2258
- if (entry.kind.type === "step") {
2259
- entry.kind.data.error = String(error);
2260
- }
2261
- entry.dirty = true;
2331
+ if (entry.kind.type === "step") {
2332
+ entry.kind.data.error = String(error);
2333
+ }
2334
+ entry.dirty = true;
2335
+ if (error instanceof StepTimeoutError && !config2.retryOnTimeout) {
2262
2336
  metadata.status = "exhausted";
2263
2337
  metadata.error = String(error);
2264
- await this.flushStorage();
2265
2338
  await this.notifyStepError(config2, metadata.attempts, error, {
2266
2339
  willRetry: false
2267
2340
  });
2268
2341
  throw markErrorReported(
2269
- attachTryStepFailure(
2270
- new CriticalError(error.message),
2271
- {
2272
- kind: "timeout",
2273
- stepName: config2.name,
2274
- attempts: metadata.attempts,
2275
- error: extractErrorInfo(error)
2276
- }
2277
- )
2342
+ attachTryStepFailure(new CriticalError(error.message), {
2343
+ kind: "timeout",
2344
+ stepName: config2.name,
2345
+ attempts: metadata.attempts,
2346
+ error: extractErrorInfo(error)
2347
+ })
2278
2348
  );
2279
2349
  }
2280
2350
  if (error instanceof CriticalError || error instanceof RollbackError) {
2281
- if (entry.kind.type === "step") {
2282
- entry.kind.data.error = String(error);
2283
- }
2284
- entry.dirty = true;
2285
2351
  metadata.status = "exhausted";
2286
2352
  metadata.error = String(error);
2287
- await this.flushStorage();
2288
2353
  await this.notifyStepError(config2, metadata.attempts, error, {
2289
2354
  willRetry: false
2290
2355
  });
@@ -2297,14 +2362,9 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
2297
2362
  })
2298
2363
  );
2299
2364
  }
2300
- if (entry.kind.type === "step") {
2301
- entry.kind.data.error = String(error);
2302
- }
2303
- entry.dirty = true;
2304
2365
  const willRetry = metadata.attempts <= maxRetries;
2305
2366
  metadata.status = willRetry ? "failed" : "exhausted";
2306
2367
  metadata.error = String(error);
2307
- await this.flushStorage();
2308
2368
  if (willRetry) {
2309
2369
  const retryDelay = calculateBackoff(
2310
2370
  metadata.attempts,
@@ -2328,7 +2388,7 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
2328
2388
  attachTryStepFailure(
2329
2389
  new StepExhaustedError(config2.name, String(error)),
2330
2390
  {
2331
- kind: "exhausted",
2391
+ kind: error instanceof StepTimeoutError ? "timeout" : "exhausted",
2332
2392
  stepName: config2.name,
2333
2393
  attempts: metadata.attempts,
2334
2394
  error: extractErrorInfo(error)
@@ -2346,7 +2406,9 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
2346
2406
  *
2347
2407
  * Note: This does NOT cancel the underlying operation. JavaScript Promises
2348
2408
  * cannot be cancelled once started. When a timeout occurs:
2349
- * - The step is marked as failed with StepTimeoutError
2409
+ * - The step is rejected with StepTimeoutError. By default this is treated
2410
+ * as a critical failure with no retry. Set retryOnTimeout: true on the
2411
+ * step config to retry timeouts like any other error.
2350
2412
  * - The underlying async operation continues running in the background
2351
2413
  * - Any side effects from the operation may still occur
2352
2414
  *
@@ -2481,8 +2543,8 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
2481
2543
  metadata.error = void 0;
2482
2544
  metadata.dirty = true;
2483
2545
  }
2484
- const historyPruneInterval = _nullishCoalesce(config2.historyPruneInterval, () => ( DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL));
2485
- const historySize = _nullishCoalesce(config2.historySize, () => ( historyPruneInterval));
2546
+ const historyPruneInterval = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(config2.historyPruneInterval, () => ( config2.commitInterval)), () => ( config2.historyEvery)), () => ( DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL));
2547
+ const historySize = _nullishCoalesce(_nullishCoalesce(config2.historySize, () => ( config2.historyKeep)), () => ( historyPruneInterval));
2486
2548
  let lastPrunedUpTo = 0;
2487
2549
  let deferredFlush = null;
2488
2550
  while (true) {
@@ -2671,7 +2733,7 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
2671
2733
  return;
2672
2734
  }
2673
2735
  if (remaining < this.driver.workerPollInterval) {
2674
- await Promise.race([sleep(remaining), this.waitForEviction()]);
2736
+ await this.sleepOrEvict(remaining);
2675
2737
  this.checkEvicted();
2676
2738
  if (entry.kind.type === "sleep") {
2677
2739
  entry.kind.data.state = "completed";
@@ -3480,8 +3542,91 @@ var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
3480
3542
  setEntry(this.storage, location, entry);
3481
3543
  await this.flushStorage();
3482
3544
  }
3545
+ // === Version ===
3546
+ async getVersion(name, latest) {
3547
+ this.assertNotInProgress();
3548
+ this.checkEvicted();
3549
+ this.entryInProgress = true;
3550
+ try {
3551
+ return await this.executeGetVersion(name, latest);
3552
+ } finally {
3553
+ this.entryInProgress = false;
3554
+ }
3555
+ }
3556
+ async executeGetVersion(name, latest) {
3557
+ if (!Number.isInteger(latest) || latest < 1) {
3558
+ throw new Error(
3559
+ `getVersion("${name}", ${latest}): latest must be an integer >= 1`
3560
+ );
3561
+ }
3562
+ this.checkDuplicateName(name);
3563
+ const location = appendName(this.storage, this.currentLocation, name);
3564
+ const key = locationToKey(this.storage, location);
3565
+ const existing = this.storage.history.entries.get(key);
3566
+ this.markVisited(key);
3567
+ this.stopRollbackIfMissing(existing);
3568
+ if (existing) {
3569
+ if (existing.kind.type !== "version_check") {
3570
+ throw new HistoryDivergedError(
3571
+ `Expected version_check at ${key}, found ${existing.kind.type}`
3572
+ );
3573
+ }
3574
+ return existing.kind.data.resolved;
3575
+ }
3576
+ const resolved = this.hasUnvisitedUnderCurrentScope(key) ? 1 : latest;
3577
+ const entry = createEntry(location, {
3578
+ type: "version_check",
3579
+ data: { resolved, latest }
3580
+ });
3581
+ setEntry(this.storage, location, entry);
3582
+ await this.flushStorage();
3583
+ return resolved;
3584
+ }
3585
+ /**
3586
+ * Returns true if any history entry under the current location scope
3587
+ * (other than `excludeKey`) has not yet been visited this run. Used by
3588
+ * getVersion to detect an old in-flight instance whose scope was already
3589
+ * executed by code that predates a version gate.
3590
+ */
3591
+ hasUnvisitedUnderCurrentScope(excludeKey) {
3592
+ const prefix = locationToKey(this.storage, this.currentLocation);
3593
+ for (const key of this.storage.history.entries.keys()) {
3594
+ if (key === excludeKey) {
3595
+ continue;
3596
+ }
3597
+ const isUnderPrefix = prefix === "" ? true : key.startsWith(`${prefix}/`) || key === prefix;
3598
+ if (isUnderPrefix && !this.visitedKeys.has(key)) {
3599
+ return true;
3600
+ }
3601
+ }
3602
+ return false;
3603
+ }
3483
3604
  }, _class);
3484
3605
 
3606
+ // src/utils.ts
3607
+ function sleep(ms) {
3608
+ return new Promise((resolve) => setTimeout(resolve, ms));
3609
+ }
3610
+ var TIMEOUT_MAX = 2147483647;
3611
+ function setLongTimeout(listener, after) {
3612
+ let timeout;
3613
+ function start(remaining) {
3614
+ if (remaining <= TIMEOUT_MAX) {
3615
+ timeout = setTimeout(listener, remaining);
3616
+ } else {
3617
+ timeout = setTimeout(() => {
3618
+ start(remaining - TIMEOUT_MAX);
3619
+ }, TIMEOUT_MAX);
3620
+ }
3621
+ }
3622
+ start(after);
3623
+ return {
3624
+ abort: () => {
3625
+ if (timeout !== void 0) clearTimeout(timeout);
3626
+ }
3627
+ };
3628
+ }
3629
+
3485
3630
  // src/index.ts
3486
3631
  var Loop = {
3487
3632
  continue: (state) => ({
@@ -3705,25 +3850,41 @@ async function executeLiveWorkflow(workflowId, workflowFn, input, driver, messag
3705
3850
  const hasMessages = result.waitingForMessages !== void 0;
3706
3851
  const hasDeadline = result.sleepUntil !== void 0;
3707
3852
  if (hasMessages && hasDeadline) {
3853
+ const iterationAbort = new AbortController();
3854
+ const onRunAbort = () => iterationAbort.abort();
3855
+ if (abortController.signal.aborted) {
3856
+ iterationAbort.abort();
3857
+ } else {
3858
+ abortController.signal.addEventListener("abort", onRunAbort, {
3859
+ once: true
3860
+ });
3861
+ }
3862
+ const messagePromise = awaitWithEviction(
3863
+ driver.waitForMessages(
3864
+ result.waitingForMessages,
3865
+ iterationAbort.signal
3866
+ ),
3867
+ iterationAbort.signal
3868
+ );
3869
+ const sleepPromise = waitForSleep(
3870
+ runtime,
3871
+ result.sleepUntil,
3872
+ iterationAbort.signal
3873
+ );
3874
+ messagePromise.catch(() => {
3875
+ });
3876
+ sleepPromise.catch(() => {
3877
+ });
3708
3878
  try {
3709
- const messagePromise = awaitWithEviction(
3710
- driver.waitForMessages(
3711
- result.waitingForMessages,
3712
- abortController.signal
3713
- ),
3714
- abortController.signal
3715
- );
3716
- const sleepPromise = waitForSleep(
3717
- runtime,
3718
- result.sleepUntil,
3719
- abortController.signal
3720
- );
3721
3879
  await Promise.race([messagePromise, sleepPromise]);
3722
3880
  } catch (error) {
3723
3881
  if (error instanceof EvictedError) {
3724
3882
  return lastResult;
3725
3883
  }
3726
3884
  throw error;
3885
+ } finally {
3886
+ iterationAbort.abort();
3887
+ abortController.signal.removeEventListener("abort", onRunAbort);
3727
3888
  }
3728
3889
  continue;
3729
3890
  }
@@ -4176,5 +4337,5 @@ async function executeWorkflow(workflowId, workflowFn, input, driver, messageDri
4176
4337
 
4177
4338
 
4178
4339
 
4179
- exports.extractErrorInfo = extractErrorInfo; exports.CriticalError = CriticalError; exports.RollbackError = RollbackError; exports.RollbackCheckpointError = RollbackCheckpointError; exports.SleepError = SleepError; exports.MessageWaitError = MessageWaitError; exports.EvictedError = EvictedError; exports.HistoryDivergedError = HistoryDivergedError; exports.StepExhaustedError = StepExhaustedError; exports.StepFailedError = StepFailedError; exports.JoinError = JoinError; exports.RaceError = RaceError; exports.CancelledError = CancelledError; exports.EntryInProgressError = EntryInProgressError; exports.keyStartsWith = keyStartsWith; exports.compareKeys = compareKeys; exports.keyToHex = keyToHex; exports.isLoopIterationMarker = isLoopIterationMarker; exports.registerName = registerName; exports.resolveName = resolveName; exports.locationToKey = locationToKey; exports.appendName = appendName; exports.appendLoopIteration = appendLoopIteration; exports.emptyLocation = emptyLocation; exports.parentLocation = parentLocation; exports.isLocationPrefix = isLocationPrefix; exports.locationsEqual = locationsEqual; exports.createStorage = createStorage; exports.createHistorySnapshot = createHistorySnapshot; exports.generateId = generateId; exports.createEntry = createEntry; exports.getOrCreateMetadata = getOrCreateMetadata; exports.loadStorage = loadStorage; exports.loadMetadata = loadMetadata; exports.flush = flush; exports.deleteEntriesWithPrefix = deleteEntriesWithPrefix; exports.getEntry = getEntry; exports.setEntry = setEntry; exports.sleep = sleep; exports.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES; exports.DEFAULT_RETRY_BACKOFF_BASE = DEFAULT_RETRY_BACKOFF_BASE; exports.DEFAULT_RETRY_BACKOFF_MAX = DEFAULT_RETRY_BACKOFF_MAX; exports.DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL = DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL; exports.DEFAULT_STEP_TIMEOUT = DEFAULT_STEP_TIMEOUT; exports.WorkflowContextImpl = WorkflowContextImpl; exports.Loop = Loop; exports.runWorkflow = runWorkflow; exports.replayWorkflowFromStep = replayWorkflowFromStep;
4180
- //# sourceMappingURL=chunk-4SWXLWKL.cjs.map
4340
+ exports.extractErrorInfo = extractErrorInfo; exports.CriticalError = CriticalError; exports.RollbackError = RollbackError; exports.RollbackCheckpointError = RollbackCheckpointError; exports.SleepError = SleepError; exports.MessageWaitError = MessageWaitError; exports.EvictedError = EvictedError; exports.HistoryDivergedError = HistoryDivergedError; exports.StepExhaustedError = StepExhaustedError; exports.StepFailedError = StepFailedError; exports.JoinError = JoinError; exports.RaceError = RaceError; exports.CancelledError = CancelledError; exports.EntryInProgressError = EntryInProgressError; exports.keyStartsWith = keyStartsWith; exports.compareKeys = compareKeys; exports.keyToHex = keyToHex; exports.isLoopIterationMarker = isLoopIterationMarker; exports.registerName = registerName; exports.resolveName = resolveName; exports.locationToKey = locationToKey; exports.appendName = appendName; exports.appendLoopIteration = appendLoopIteration; exports.emptyLocation = emptyLocation; exports.parentLocation = parentLocation; exports.isLocationPrefix = isLocationPrefix; exports.locationsEqual = locationsEqual; exports.createStorage = createStorage; exports.createHistorySnapshot = createHistorySnapshot; exports.generateId = generateId; exports.createEntry = createEntry; exports.getOrCreateMetadata = getOrCreateMetadata; exports.loadStorage = loadStorage; exports.loadMetadata = loadMetadata; exports.flush = flush; exports.deleteEntriesWithPrefix = deleteEntriesWithPrefix; exports.getEntry = getEntry; exports.setEntry = setEntry; exports.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES; exports.DEFAULT_RETRY_BACKOFF_BASE = DEFAULT_RETRY_BACKOFF_BASE; exports.DEFAULT_RETRY_BACKOFF_MAX = DEFAULT_RETRY_BACKOFF_MAX; exports.DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL = DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL; exports.DEFAULT_STEP_TIMEOUT = DEFAULT_STEP_TIMEOUT; exports.WorkflowContextImpl = WorkflowContextImpl; exports.sleep = sleep; exports.Loop = Loop; exports.runWorkflow = runWorkflow; exports.replayWorkflowFromStep = replayWorkflowFromStep;
4341
+ //# sourceMappingURL=chunk-32422MOM.cjs.map