@rivetkit/workflow-engine 0.0.0-main.6151ab0 → 0.0.0-main.79e97f1
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/dist/schemas/v1.ts +25 -0
- package/dist/tsup/{chunk-UMFB2AR3.js → chunk-DTS4S7RT.js} +235 -91
- package/dist/tsup/chunk-DTS4S7RT.js.map +1 -0
- package/dist/tsup/{chunk-4SWXLWKL.cjs → chunk-KLHCG2KY.cjs} +235 -91
- package/dist/tsup/chunk-KLHCG2KY.cjs.map +1 -0
- package/dist/tsup/index.cjs +2 -2
- package/dist/tsup/index.d.cts +51 -13
- package/dist/tsup/index.d.ts +51 -13
- package/dist/tsup/index.js +1 -1
- package/dist/tsup/testing.cjs +23 -23
- package/dist/tsup/testing.js +1 -1
- package/package.json +1 -1
- package/schemas/serde.ts +17 -3
- package/schemas/v1.bare +10 -1
- package/schemas/versioned.ts +1 -0
- package/src/context.ts +152 -60
- package/src/driver.ts +6 -0
- package/src/index.ts +41 -21
- package/src/location.ts +1 -1
- package/src/storage.ts +54 -3
- package/src/types.ts +27 -8
- package/dist/tsup/chunk-4SWXLWKL.cjs.map +0 -1
- package/dist/tsup/chunk-UMFB2AR3.js.map +0 -1
package/dist/schemas/v1.ts
CHANGED
|
@@ -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: 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: 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,8 @@ 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;
|
|
1405
1440
|
function createStorage() {
|
|
1406
1441
|
return {
|
|
1407
1442
|
nameRegistry: [],
|
|
@@ -1519,6 +1554,8 @@ async function loadMetadata(storage, driver, entryId) {
|
|
|
1519
1554
|
}
|
|
1520
1555
|
async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
|
|
1521
1556
|
const writes = [];
|
|
1557
|
+
const dirtyEntries = [];
|
|
1558
|
+
const dirtyMetadata = [];
|
|
1522
1559
|
let historyUpdated = false;
|
|
1523
1560
|
for (let i = storage.flushedNameCount; i < storage.nameRegistry.length; i++) {
|
|
1524
1561
|
const name = storage.nameRegistry[i];
|
|
@@ -1536,7 +1573,7 @@ async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
|
|
|
1536
1573
|
key: buildHistoryKey(entry.location),
|
|
1537
1574
|
value: serializeEntry(entry)
|
|
1538
1575
|
});
|
|
1539
|
-
entry
|
|
1576
|
+
dirtyEntries.push(entry);
|
|
1540
1577
|
historyUpdated = true;
|
|
1541
1578
|
}
|
|
1542
1579
|
}
|
|
@@ -1546,7 +1583,7 @@ async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
|
|
|
1546
1583
|
key: buildEntryMetadataKey(id),
|
|
1547
1584
|
value: serializeEntryMetadata(metadata)
|
|
1548
1585
|
});
|
|
1549
|
-
metadata
|
|
1586
|
+
dirtyMetadata.push(metadata);
|
|
1550
1587
|
historyUpdated = true;
|
|
1551
1588
|
}
|
|
1552
1589
|
}
|
|
@@ -1570,7 +1607,13 @@ async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
|
|
|
1570
1607
|
});
|
|
1571
1608
|
}
|
|
1572
1609
|
if (writes.length > 0) {
|
|
1573
|
-
|
|
1610
|
+
if (driver.atomicBatch) {
|
|
1611
|
+
await driver.batch(writes);
|
|
1612
|
+
} else {
|
|
1613
|
+
for (const chunk of splitBatchWrites(writes)) {
|
|
1614
|
+
await driver.batch(chunk);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1574
1617
|
}
|
|
1575
1618
|
if (pendingDeletions) {
|
|
1576
1619
|
const deleteOps = [];
|
|
@@ -1588,6 +1631,12 @@ async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
|
|
|
1588
1631
|
historyUpdated = true;
|
|
1589
1632
|
}
|
|
1590
1633
|
}
|
|
1634
|
+
for (const entry of dirtyEntries) {
|
|
1635
|
+
entry.dirty = false;
|
|
1636
|
+
}
|
|
1637
|
+
for (const metadata of dirtyMetadata) {
|
|
1638
|
+
metadata.dirty = false;
|
|
1639
|
+
}
|
|
1591
1640
|
storage.flushedNameCount = storage.nameRegistry.length;
|
|
1592
1641
|
storage.flushedState = storage.state;
|
|
1593
1642
|
storage.flushedOutput = storage.output;
|
|
@@ -1596,6 +1645,30 @@ async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
|
|
|
1596
1645
|
onHistoryUpdated();
|
|
1597
1646
|
}
|
|
1598
1647
|
}
|
|
1648
|
+
function splitBatchWrites(writes) {
|
|
1649
|
+
const chunks = [];
|
|
1650
|
+
let chunk = [];
|
|
1651
|
+
let chunkBytes = 0;
|
|
1652
|
+
for (const write of writes) {
|
|
1653
|
+
const writeBytes = write.key.byteLength + write.value.byteLength;
|
|
1654
|
+
if (writeBytes > MAX_KV_BATCH_PAYLOAD_BYTES) {
|
|
1655
|
+
throw new Error(
|
|
1656
|
+
`KV batch write is ${writeBytes} bytes, exceeding the ${MAX_KV_BATCH_PAYLOAD_BYTES} byte limit`
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1659
|
+
if (chunk.length >= MAX_KV_BATCH_ENTRIES || chunk.length > 0 && chunkBytes + writeBytes > MAX_KV_BATCH_PAYLOAD_BYTES) {
|
|
1660
|
+
chunks.push(chunk);
|
|
1661
|
+
chunk = [];
|
|
1662
|
+
chunkBytes = 0;
|
|
1663
|
+
}
|
|
1664
|
+
chunk.push(write);
|
|
1665
|
+
chunkBytes += writeBytes;
|
|
1666
|
+
}
|
|
1667
|
+
if (chunk.length > 0) {
|
|
1668
|
+
chunks.push(chunk);
|
|
1669
|
+
}
|
|
1670
|
+
return chunks;
|
|
1671
|
+
}
|
|
1599
1672
|
async function deleteEntriesWithPrefix(storage, driver, prefixLocation, onHistoryUpdated) {
|
|
1600
1673
|
const deletions = collectDeletionsForPrefix(storage, prefixLocation);
|
|
1601
1674
|
await driver.deletePrefix(deletions.prefixes[0]);
|
|
@@ -1628,30 +1701,6 @@ function setEntry(storage, location, entry) {
|
|
|
1628
1701
|
storage.history.entries.set(key, entry);
|
|
1629
1702
|
}
|
|
1630
1703
|
|
|
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
1704
|
// src/context.ts
|
|
1656
1705
|
var DEFAULT_MAX_RETRIES = 3;
|
|
1657
1706
|
var DEFAULT_RETRY_BACKOFF_BASE = 100;
|
|
@@ -1927,7 +1976,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1927
1976
|
* Throws HistoryDivergedError if duplicate detected.
|
|
1928
1977
|
*/
|
|
1929
1978
|
checkDuplicateName(name) {
|
|
1930
|
-
const fullKey = locationToKey(this.storage, this.currentLocation)
|
|
1979
|
+
const fullKey = `${locationToKey(this.storage, this.currentLocation)}/${name}`;
|
|
1931
1980
|
if (this.usedNamesInExecution.has(fullKey)) {
|
|
1932
1981
|
throw new HistoryDivergedError(
|
|
1933
1982
|
`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 +2030,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1981
2030
|
validateComplete() {
|
|
1982
2031
|
const prefix = locationToKey(this.storage, this.currentLocation);
|
|
1983
2032
|
for (const key of this.storage.history.entries.keys()) {
|
|
1984
|
-
const isUnderPrefix = prefix === "" ? true : key.startsWith(prefix
|
|
2033
|
+
const isUnderPrefix = prefix === "" ? true : key.startsWith(`${prefix}/`) || key === prefix;
|
|
1985
2034
|
if (isUnderPrefix) {
|
|
1986
2035
|
if (!this.visitedKeys.has(key)) {
|
|
1987
2036
|
throw new HistoryDivergedError(
|
|
@@ -1998,25 +2047,33 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1998
2047
|
this.abortController.abort(new EvictedError());
|
|
1999
2048
|
}
|
|
2000
2049
|
/**
|
|
2001
|
-
* Wait for
|
|
2002
|
-
*
|
|
2003
|
-
*
|
|
2004
|
-
*
|
|
2050
|
+
* Wait for `ms`, rejecting early with EvictedError if the workflow is
|
|
2051
|
+
* evicted. Both the timer and the abort listener are torn down on either
|
|
2052
|
+
* outcome, so a completed sleep never leaves a dangling listener on the
|
|
2053
|
+
* long-lived run abort signal.
|
|
2005
2054
|
*/
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2055
|
+
async sleepOrEvict(ms) {
|
|
2056
|
+
if (this.abortSignal.aborted) {
|
|
2057
|
+
throw new EvictedError();
|
|
2058
|
+
}
|
|
2059
|
+
let timer;
|
|
2060
|
+
let onAbort;
|
|
2061
|
+
try {
|
|
2062
|
+
await new Promise((resolve, reject) => {
|
|
2063
|
+
timer = setTimeout(resolve, ms);
|
|
2064
|
+
onAbort = () => reject(new EvictedError());
|
|
2065
|
+
this.abortSignal.addEventListener("abort", onAbort, {
|
|
2066
|
+
once: true
|
|
2067
|
+
});
|
|
2068
|
+
});
|
|
2069
|
+
} finally {
|
|
2070
|
+
if (timer !== void 0) {
|
|
2071
|
+
clearTimeout(timer);
|
|
2011
2072
|
}
|
|
2012
|
-
|
|
2013
|
-
"abort",
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
},
|
|
2017
|
-
{ once: true }
|
|
2018
|
-
);
|
|
2019
|
-
});
|
|
2073
|
+
if (onAbort) {
|
|
2074
|
+
this.abortSignal.removeEventListener("abort", onAbort);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2020
2077
|
}
|
|
2021
2078
|
// === Step ===
|
|
2022
2079
|
async step(nameOrConfig, run) {
|
|
@@ -2165,7 +2222,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
2165
2222
|
}
|
|
2166
2223
|
const maxRetries2 = config2.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
2167
2224
|
if (metadata2.attempts > maxRetries2) {
|
|
2168
|
-
const lastError =
|
|
2225
|
+
const lastError = metadata2.error;
|
|
2169
2226
|
const exhaustedError = new StepExhaustedError(
|
|
2170
2227
|
config2.name,
|
|
2171
2228
|
lastError
|
|
@@ -2254,37 +2311,28 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
2254
2311
|
});
|
|
2255
2312
|
return output;
|
|
2256
2313
|
} catch (error) {
|
|
2257
|
-
if (
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2314
|
+
if (entry.kind.type === "step") {
|
|
2315
|
+
entry.kind.data.error = String(error);
|
|
2316
|
+
}
|
|
2317
|
+
entry.dirty = true;
|
|
2318
|
+
if (error instanceof StepTimeoutError && !config2.retryOnTimeout) {
|
|
2262
2319
|
metadata.status = "exhausted";
|
|
2263
2320
|
metadata.error = String(error);
|
|
2264
|
-
await this.flushStorage();
|
|
2265
2321
|
await this.notifyStepError(config2, metadata.attempts, error, {
|
|
2266
2322
|
willRetry: false
|
|
2267
2323
|
});
|
|
2268
2324
|
throw markErrorReported(
|
|
2269
|
-
attachTryStepFailure(
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
error: extractErrorInfo(error)
|
|
2276
|
-
}
|
|
2277
|
-
)
|
|
2325
|
+
attachTryStepFailure(new CriticalError(error.message), {
|
|
2326
|
+
kind: "timeout",
|
|
2327
|
+
stepName: config2.name,
|
|
2328
|
+
attempts: metadata.attempts,
|
|
2329
|
+
error: extractErrorInfo(error)
|
|
2330
|
+
})
|
|
2278
2331
|
);
|
|
2279
2332
|
}
|
|
2280
2333
|
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
2334
|
metadata.status = "exhausted";
|
|
2286
2335
|
metadata.error = String(error);
|
|
2287
|
-
await this.flushStorage();
|
|
2288
2336
|
await this.notifyStepError(config2, metadata.attempts, error, {
|
|
2289
2337
|
willRetry: false
|
|
2290
2338
|
});
|
|
@@ -2297,14 +2345,9 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
2297
2345
|
})
|
|
2298
2346
|
);
|
|
2299
2347
|
}
|
|
2300
|
-
if (entry.kind.type === "step") {
|
|
2301
|
-
entry.kind.data.error = String(error);
|
|
2302
|
-
}
|
|
2303
|
-
entry.dirty = true;
|
|
2304
2348
|
const willRetry = metadata.attempts <= maxRetries;
|
|
2305
2349
|
metadata.status = willRetry ? "failed" : "exhausted";
|
|
2306
2350
|
metadata.error = String(error);
|
|
2307
|
-
await this.flushStorage();
|
|
2308
2351
|
if (willRetry) {
|
|
2309
2352
|
const retryDelay = calculateBackoff(
|
|
2310
2353
|
metadata.attempts,
|
|
@@ -2328,7 +2371,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
2328
2371
|
attachTryStepFailure(
|
|
2329
2372
|
new StepExhaustedError(config2.name, String(error)),
|
|
2330
2373
|
{
|
|
2331
|
-
kind: "exhausted",
|
|
2374
|
+
kind: error instanceof StepTimeoutError ? "timeout" : "exhausted",
|
|
2332
2375
|
stepName: config2.name,
|
|
2333
2376
|
attempts: metadata.attempts,
|
|
2334
2377
|
error: extractErrorInfo(error)
|
|
@@ -2346,7 +2389,9 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
2346
2389
|
*
|
|
2347
2390
|
* Note: This does NOT cancel the underlying operation. JavaScript Promises
|
|
2348
2391
|
* cannot be cancelled once started. When a timeout occurs:
|
|
2349
|
-
* - The step is
|
|
2392
|
+
* - The step is rejected with StepTimeoutError. By default this is treated
|
|
2393
|
+
* as a critical failure with no retry. Set retryOnTimeout: true on the
|
|
2394
|
+
* step config to retry timeouts like any other error.
|
|
2350
2395
|
* - The underlying async operation continues running in the background
|
|
2351
2396
|
* - Any side effects from the operation may still occur
|
|
2352
2397
|
*
|
|
@@ -2481,8 +2526,8 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
2481
2526
|
metadata.error = void 0;
|
|
2482
2527
|
metadata.dirty = true;
|
|
2483
2528
|
}
|
|
2484
|
-
const historyPruneInterval = config2.historyPruneInterval ?? DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL;
|
|
2485
|
-
const historySize = config2.historySize ?? historyPruneInterval;
|
|
2529
|
+
const historyPruneInterval = config2.historyPruneInterval ?? config2.commitInterval ?? config2.historyEvery ?? DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL;
|
|
2530
|
+
const historySize = config2.historySize ?? config2.historyKeep ?? historyPruneInterval;
|
|
2486
2531
|
let lastPrunedUpTo = 0;
|
|
2487
2532
|
let deferredFlush = null;
|
|
2488
2533
|
while (true) {
|
|
@@ -2671,7 +2716,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
2671
2716
|
return;
|
|
2672
2717
|
}
|
|
2673
2718
|
if (remaining < this.driver.workerPollInterval) {
|
|
2674
|
-
await
|
|
2719
|
+
await this.sleepOrEvict(remaining);
|
|
2675
2720
|
this.checkEvicted();
|
|
2676
2721
|
if (entry.kind.type === "sleep") {
|
|
2677
2722
|
entry.kind.data.state = "completed";
|
|
@@ -3480,8 +3525,91 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
3480
3525
|
setEntry(this.storage, location, entry);
|
|
3481
3526
|
await this.flushStorage();
|
|
3482
3527
|
}
|
|
3528
|
+
// === Version ===
|
|
3529
|
+
async getVersion(name, latest) {
|
|
3530
|
+
this.assertNotInProgress();
|
|
3531
|
+
this.checkEvicted();
|
|
3532
|
+
this.entryInProgress = true;
|
|
3533
|
+
try {
|
|
3534
|
+
return await this.executeGetVersion(name, latest);
|
|
3535
|
+
} finally {
|
|
3536
|
+
this.entryInProgress = false;
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
async executeGetVersion(name, latest) {
|
|
3540
|
+
if (!Number.isInteger(latest) || latest < 1) {
|
|
3541
|
+
throw new Error(
|
|
3542
|
+
`getVersion("${name}", ${latest}): latest must be an integer >= 1`
|
|
3543
|
+
);
|
|
3544
|
+
}
|
|
3545
|
+
this.checkDuplicateName(name);
|
|
3546
|
+
const location = appendName(this.storage, this.currentLocation, name);
|
|
3547
|
+
const key = locationToKey(this.storage, location);
|
|
3548
|
+
const existing = this.storage.history.entries.get(key);
|
|
3549
|
+
this.markVisited(key);
|
|
3550
|
+
this.stopRollbackIfMissing(existing);
|
|
3551
|
+
if (existing) {
|
|
3552
|
+
if (existing.kind.type !== "version_check") {
|
|
3553
|
+
throw new HistoryDivergedError(
|
|
3554
|
+
`Expected version_check at ${key}, found ${existing.kind.type}`
|
|
3555
|
+
);
|
|
3556
|
+
}
|
|
3557
|
+
return existing.kind.data.resolved;
|
|
3558
|
+
}
|
|
3559
|
+
const resolved = this.hasUnvisitedUnderCurrentScope(key) ? 1 : latest;
|
|
3560
|
+
const entry = createEntry(location, {
|
|
3561
|
+
type: "version_check",
|
|
3562
|
+
data: { resolved, latest }
|
|
3563
|
+
});
|
|
3564
|
+
setEntry(this.storage, location, entry);
|
|
3565
|
+
await this.flushStorage();
|
|
3566
|
+
return resolved;
|
|
3567
|
+
}
|
|
3568
|
+
/**
|
|
3569
|
+
* Returns true if any history entry under the current location scope
|
|
3570
|
+
* (other than `excludeKey`) has not yet been visited this run. Used by
|
|
3571
|
+
* getVersion to detect an old in-flight instance whose scope was already
|
|
3572
|
+
* executed by code that predates a version gate.
|
|
3573
|
+
*/
|
|
3574
|
+
hasUnvisitedUnderCurrentScope(excludeKey) {
|
|
3575
|
+
const prefix = locationToKey(this.storage, this.currentLocation);
|
|
3576
|
+
for (const key of this.storage.history.entries.keys()) {
|
|
3577
|
+
if (key === excludeKey) {
|
|
3578
|
+
continue;
|
|
3579
|
+
}
|
|
3580
|
+
const isUnderPrefix = prefix === "" ? true : key.startsWith(`${prefix}/`) || key === prefix;
|
|
3581
|
+
if (isUnderPrefix && !this.visitedKeys.has(key)) {
|
|
3582
|
+
return true;
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
return false;
|
|
3586
|
+
}
|
|
3483
3587
|
};
|
|
3484
3588
|
|
|
3589
|
+
// src/utils.ts
|
|
3590
|
+
function sleep(ms) {
|
|
3591
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3592
|
+
}
|
|
3593
|
+
var TIMEOUT_MAX = 2147483647;
|
|
3594
|
+
function setLongTimeout(listener, after) {
|
|
3595
|
+
let timeout;
|
|
3596
|
+
function start(remaining) {
|
|
3597
|
+
if (remaining <= TIMEOUT_MAX) {
|
|
3598
|
+
timeout = setTimeout(listener, remaining);
|
|
3599
|
+
} else {
|
|
3600
|
+
timeout = setTimeout(() => {
|
|
3601
|
+
start(remaining - TIMEOUT_MAX);
|
|
3602
|
+
}, TIMEOUT_MAX);
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
start(after);
|
|
3606
|
+
return {
|
|
3607
|
+
abort: () => {
|
|
3608
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
3609
|
+
}
|
|
3610
|
+
};
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3485
3613
|
// src/index.ts
|
|
3486
3614
|
var Loop = {
|
|
3487
3615
|
continue: (state) => ({
|
|
@@ -3705,25 +3833,41 @@ async function executeLiveWorkflow(workflowId, workflowFn, input, driver, messag
|
|
|
3705
3833
|
const hasMessages = result.waitingForMessages !== void 0;
|
|
3706
3834
|
const hasDeadline = result.sleepUntil !== void 0;
|
|
3707
3835
|
if (hasMessages && hasDeadline) {
|
|
3836
|
+
const iterationAbort = new AbortController();
|
|
3837
|
+
const onRunAbort = () => iterationAbort.abort();
|
|
3838
|
+
if (abortController.signal.aborted) {
|
|
3839
|
+
iterationAbort.abort();
|
|
3840
|
+
} else {
|
|
3841
|
+
abortController.signal.addEventListener("abort", onRunAbort, {
|
|
3842
|
+
once: true
|
|
3843
|
+
});
|
|
3844
|
+
}
|
|
3845
|
+
const messagePromise = awaitWithEviction(
|
|
3846
|
+
driver.waitForMessages(
|
|
3847
|
+
result.waitingForMessages,
|
|
3848
|
+
iterationAbort.signal
|
|
3849
|
+
),
|
|
3850
|
+
iterationAbort.signal
|
|
3851
|
+
);
|
|
3852
|
+
const sleepPromise = waitForSleep(
|
|
3853
|
+
runtime,
|
|
3854
|
+
result.sleepUntil,
|
|
3855
|
+
iterationAbort.signal
|
|
3856
|
+
);
|
|
3857
|
+
messagePromise.catch(() => {
|
|
3858
|
+
});
|
|
3859
|
+
sleepPromise.catch(() => {
|
|
3860
|
+
});
|
|
3708
3861
|
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
3862
|
await Promise.race([messagePromise, sleepPromise]);
|
|
3722
3863
|
} catch (error) {
|
|
3723
3864
|
if (error instanceof EvictedError) {
|
|
3724
3865
|
return lastResult;
|
|
3725
3866
|
}
|
|
3726
3867
|
throw error;
|
|
3868
|
+
} finally {
|
|
3869
|
+
iterationAbort.abort();
|
|
3870
|
+
abortController.signal.removeEventListener("abort", onRunAbort);
|
|
3727
3871
|
}
|
|
3728
3872
|
continue;
|
|
3729
3873
|
}
|
|
@@ -4166,15 +4310,15 @@ export {
|
|
|
4166
4310
|
deleteEntriesWithPrefix,
|
|
4167
4311
|
getEntry,
|
|
4168
4312
|
setEntry,
|
|
4169
|
-
sleep,
|
|
4170
4313
|
DEFAULT_MAX_RETRIES,
|
|
4171
4314
|
DEFAULT_RETRY_BACKOFF_BASE,
|
|
4172
4315
|
DEFAULT_RETRY_BACKOFF_MAX,
|
|
4173
4316
|
DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL,
|
|
4174
4317
|
DEFAULT_STEP_TIMEOUT,
|
|
4175
4318
|
WorkflowContextImpl,
|
|
4319
|
+
sleep,
|
|
4176
4320
|
Loop,
|
|
4177
4321
|
runWorkflow,
|
|
4178
4322
|
replayWorkflowFromStep
|
|
4179
4323
|
};
|
|
4180
|
-
//# sourceMappingURL=chunk-
|
|
4324
|
+
//# sourceMappingURL=chunk-DTS4S7RT.js.map
|