@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.
- package/dist/schemas/v1.ts +25 -0
- package/dist/tsup/{chunk-4SWXLWKL.cjs → chunk-32422MOM.cjs} +266 -105
- package/dist/tsup/chunk-32422MOM.cjs.map +1 -0
- package/dist/tsup/{chunk-UMFB2AR3.js → chunk-TVRZA5O5.js} +266 -105
- package/dist/tsup/chunk-TVRZA5O5.js.map +1 -0
- package/dist/tsup/index.cjs +2 -2
- package/dist/tsup/index.cjs.map +1 -1
- package/dist/tsup/index.d.cts +56 -13
- package/dist/tsup/index.d.ts +56 -13
- package/dist/tsup/index.js +1 -1
- package/dist/tsup/testing.cjs +29 -23
- package/dist/tsup/testing.cjs.map +1 -1
- package/dist/tsup/testing.d.cts +1 -0
- package/dist/tsup/testing.d.ts +1 -0
- package/dist/tsup/testing.js +7 -1
- package/dist/tsup/testing.js.map +1 -1
- package/package.json +6 -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 +12 -0
- package/src/index.ts +41 -21
- package/src/location.ts +1 -1
- package/src/storage.ts +98 -17
- package/src/testing.ts +7 -0
- package/src/types.ts +27 -8
- package/dist/tsup/chunk-4SWXLWKL.cjs.map +0 -1
- package/dist/tsup/chunk-UMFB2AR3.js.map +0 -1
|
@@ -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,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
|
|
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
|
|
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
|
-
|
|
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
|
|
1577
|
-
|
|
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
|
|
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 _WorkflowContextImpl {
|
|
|
1927
1993
|
* Throws HistoryDivergedError if duplicate detected.
|
|
1928
1994
|
*/
|
|
1929
1995
|
checkDuplicateName(name) {
|
|
1930
|
-
const fullKey = locationToKey(this.storage, this.currentLocation)
|
|
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 _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
|
|
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 _WorkflowContextImpl {
|
|
|
1998
2064
|
this.abortController.abort(new EvictedError());
|
|
1999
2065
|
}
|
|
2000
2066
|
/**
|
|
2001
|
-
* Wait for
|
|
2002
|
-
*
|
|
2003
|
-
*
|
|
2004
|
-
*
|
|
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
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
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
|
-
|
|
2013
|
-
"abort",
|
|
2014
|
-
|
|
2015
|
-
|
|
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 _WorkflowContextImpl {
|
|
|
2165
2239
|
}
|
|
2166
2240
|
const maxRetries2 = config2.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
2167
2241
|
if (metadata2.attempts > maxRetries2) {
|
|
2168
|
-
const lastError =
|
|
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 _WorkflowContextImpl {
|
|
|
2254
2328
|
});
|
|
2255
2329
|
return output;
|
|
2256
2330
|
} catch (error) {
|
|
2257
|
-
if (
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
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
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
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 _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 _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 _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
|
|
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 _WorkflowContextImpl {
|
|
|
2481
2543
|
metadata.error = void 0;
|
|
2482
2544
|
metadata.dirty = true;
|
|
2483
2545
|
}
|
|
2484
|
-
const historyPruneInterval = config2.historyPruneInterval ?? DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL;
|
|
2485
|
-
const historySize = config2.historySize ?? historyPruneInterval;
|
|
2546
|
+
const historyPruneInterval = config2.historyPruneInterval ?? config2.commitInterval ?? config2.historyEvery ?? DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL;
|
|
2547
|
+
const historySize = 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 _WorkflowContextImpl {
|
|
|
2671
2733
|
return;
|
|
2672
2734
|
}
|
|
2673
2735
|
if (remaining < this.driver.workerPollInterval) {
|
|
2674
|
-
await
|
|
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 _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
|
};
|
|
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
|
}
|
|
@@ -4166,15 +4327,15 @@ export {
|
|
|
4166
4327
|
deleteEntriesWithPrefix,
|
|
4167
4328
|
getEntry,
|
|
4168
4329
|
setEntry,
|
|
4169
|
-
sleep,
|
|
4170
4330
|
DEFAULT_MAX_RETRIES,
|
|
4171
4331
|
DEFAULT_RETRY_BACKOFF_BASE,
|
|
4172
4332
|
DEFAULT_RETRY_BACKOFF_MAX,
|
|
4173
4333
|
DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL,
|
|
4174
4334
|
DEFAULT_STEP_TIMEOUT,
|
|
4175
4335
|
WorkflowContextImpl,
|
|
4336
|
+
sleep,
|
|
4176
4337
|
Loop,
|
|
4177
4338
|
runWorkflow,
|
|
4178
4339
|
replayWorkflowFromStep
|
|
4179
4340
|
};
|
|
4180
|
-
//# sourceMappingURL=chunk-
|
|
4341
|
+
//# sourceMappingURL=chunk-TVRZA5O5.js.map
|