@rivetkit/workflow-engine 2.1.5 → 2.1.6-rc.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/dist/tsup/{chunk-JTLDEP6X.js → chunk-MMWB37UG.js} +449 -209
- package/dist/tsup/chunk-MMWB37UG.js.map +1 -0
- package/dist/tsup/{chunk-KQO2TD7T.cjs → chunk-SHICGAKC.cjs} +448 -208
- package/dist/tsup/chunk-SHICGAKC.cjs.map +1 -0
- package/dist/tsup/index.cjs +2 -4
- package/dist/tsup/index.cjs.map +1 -1
- package/dist/tsup/index.d.cts +85 -25
- package/dist/tsup/index.d.ts +85 -25
- package/dist/tsup/index.js +5 -7
- package/dist/tsup/testing.cjs +35 -25
- package/dist/tsup/testing.cjs.map +1 -1
- package/dist/tsup/testing.d.cts +2 -1
- package/dist/tsup/testing.d.ts +2 -1
- package/dist/tsup/testing.js +19 -9
- package/dist/tsup/testing.js.map +1 -1
- package/package.json +1 -1
- package/src/context.ts +298 -114
- package/src/driver.ts +5 -0
- package/src/error-utils.ts +87 -0
- package/src/errors.ts +1 -0
- package/src/index.ts +84 -55
- package/src/keys.ts +26 -0
- package/src/location.ts +4 -1
- package/src/storage.ts +62 -21
- package/src/testing.ts +25 -4
- package/src/types.ts +48 -11
- package/dist/tsup/chunk-JTLDEP6X.js.map +0 -1
- package/dist/tsup/chunk-KQO2TD7T.cjs.map +0 -1
|
@@ -1,3 +1,53 @@
|
|
|
1
|
+
// src/error-utils.ts
|
|
2
|
+
var WORKFLOW_ERROR_REPORTED_SYMBOL = /* @__PURE__ */ Symbol("workflow.error.reported");
|
|
3
|
+
function extractErrorInfo(error) {
|
|
4
|
+
if (error instanceof Error) {
|
|
5
|
+
const result = {
|
|
6
|
+
name: error.name,
|
|
7
|
+
message: error.message,
|
|
8
|
+
stack: error.stack
|
|
9
|
+
};
|
|
10
|
+
const metadata = {};
|
|
11
|
+
for (const key of Object.keys(error)) {
|
|
12
|
+
if (key !== "name" && key !== "message" && key !== "stack") {
|
|
13
|
+
const value = error[key];
|
|
14
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
15
|
+
metadata[key] = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (Object.keys(metadata).length > 0) {
|
|
20
|
+
result.metadata = metadata;
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
name: "Error",
|
|
26
|
+
message: String(error)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function markErrorReported(error) {
|
|
30
|
+
error[WORKFLOW_ERROR_REPORTED_SYMBOL] = true;
|
|
31
|
+
return error;
|
|
32
|
+
}
|
|
33
|
+
function isErrorReported(error) {
|
|
34
|
+
if (!(error instanceof Error)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return Boolean(
|
|
38
|
+
error[WORKFLOW_ERROR_REPORTED_SYMBOL]
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
function getErrorEventTag(event) {
|
|
42
|
+
if ("step" in event) {
|
|
43
|
+
return "step";
|
|
44
|
+
}
|
|
45
|
+
if ("rollback" in event) {
|
|
46
|
+
return "rollback";
|
|
47
|
+
}
|
|
48
|
+
return "workflow";
|
|
49
|
+
}
|
|
50
|
+
|
|
1
51
|
// src/errors.ts
|
|
2
52
|
var CriticalError = class extends Error {
|
|
3
53
|
constructor(message) {
|
|
@@ -63,11 +113,12 @@ var StepExhaustedError = class extends Error {
|
|
|
63
113
|
}
|
|
64
114
|
};
|
|
65
115
|
var StepFailedError = class extends Error {
|
|
66
|
-
constructor(stepName, originalError, attempts) {
|
|
116
|
+
constructor(stepName, originalError, attempts, retryAt) {
|
|
67
117
|
super(`Step "${stepName}" failed (attempt ${attempts})`);
|
|
68
118
|
this.stepName = stepName;
|
|
69
119
|
this.originalError = originalError;
|
|
70
120
|
this.attempts = attempts;
|
|
121
|
+
this.retryAt = retryAt;
|
|
71
122
|
this.name = "StepFailedError";
|
|
72
123
|
this.cause = originalError;
|
|
73
124
|
}
|
|
@@ -101,6 +152,126 @@ var EntryInProgressError = class extends Error {
|
|
|
101
152
|
}
|
|
102
153
|
};
|
|
103
154
|
|
|
155
|
+
// src/keys.ts
|
|
156
|
+
import * as tuple from "fdb-tuple";
|
|
157
|
+
var KEY_PREFIX = {
|
|
158
|
+
NAMES: 1,
|
|
159
|
+
// Name registry: [1, index]
|
|
160
|
+
HISTORY: 2,
|
|
161
|
+
// History entries: [2, ...locationSegments]
|
|
162
|
+
WORKFLOW: 3,
|
|
163
|
+
// Workflow metadata: [3, field]
|
|
164
|
+
ENTRY_METADATA: 4
|
|
165
|
+
// Entry metadata: [4, entryId]
|
|
166
|
+
};
|
|
167
|
+
var WORKFLOW_FIELD = {
|
|
168
|
+
STATE: 1,
|
|
169
|
+
OUTPUT: 2,
|
|
170
|
+
ERROR: 3,
|
|
171
|
+
INPUT: 4
|
|
172
|
+
};
|
|
173
|
+
function segmentToTuple(segment) {
|
|
174
|
+
if (typeof segment === "number") {
|
|
175
|
+
return segment;
|
|
176
|
+
}
|
|
177
|
+
return [segment.loop, segment.iteration];
|
|
178
|
+
}
|
|
179
|
+
function locationToTupleElements(location) {
|
|
180
|
+
return location.map(segmentToTuple);
|
|
181
|
+
}
|
|
182
|
+
function bufferToUint8Array(buf) {
|
|
183
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
184
|
+
}
|
|
185
|
+
function uint8ArrayToBuffer(arr) {
|
|
186
|
+
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
187
|
+
}
|
|
188
|
+
function pack2(items) {
|
|
189
|
+
const buf = tuple.pack(items);
|
|
190
|
+
return bufferToUint8Array(buf);
|
|
191
|
+
}
|
|
192
|
+
function unpack2(data) {
|
|
193
|
+
const buf = uint8ArrayToBuffer(data);
|
|
194
|
+
return tuple.unpack(buf);
|
|
195
|
+
}
|
|
196
|
+
function buildNameKey(index) {
|
|
197
|
+
return pack2([KEY_PREFIX.NAMES, index]);
|
|
198
|
+
}
|
|
199
|
+
function buildNamePrefix() {
|
|
200
|
+
return pack2([KEY_PREFIX.NAMES]);
|
|
201
|
+
}
|
|
202
|
+
function buildHistoryKey(location) {
|
|
203
|
+
return pack2([KEY_PREFIX.HISTORY, ...locationToTupleElements(location)]);
|
|
204
|
+
}
|
|
205
|
+
function buildHistoryPrefix(location) {
|
|
206
|
+
return pack2([KEY_PREFIX.HISTORY, ...locationToTupleElements(location)]);
|
|
207
|
+
}
|
|
208
|
+
function buildLoopIterationRange(loopLocation, loopSegment, fromIteration, toIteration) {
|
|
209
|
+
const loopLocationSegments = locationToTupleElements(loopLocation);
|
|
210
|
+
return {
|
|
211
|
+
start: pack2([
|
|
212
|
+
KEY_PREFIX.HISTORY,
|
|
213
|
+
...loopLocationSegments,
|
|
214
|
+
[loopSegment, fromIteration]
|
|
215
|
+
]),
|
|
216
|
+
end: pack2([
|
|
217
|
+
KEY_PREFIX.HISTORY,
|
|
218
|
+
...loopLocationSegments,
|
|
219
|
+
[loopSegment, toIteration]
|
|
220
|
+
])
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function buildHistoryPrefixAll() {
|
|
224
|
+
return pack2([KEY_PREFIX.HISTORY]);
|
|
225
|
+
}
|
|
226
|
+
function buildWorkflowStateKey() {
|
|
227
|
+
return pack2([KEY_PREFIX.WORKFLOW, WORKFLOW_FIELD.STATE]);
|
|
228
|
+
}
|
|
229
|
+
function buildWorkflowOutputKey() {
|
|
230
|
+
return pack2([KEY_PREFIX.WORKFLOW, WORKFLOW_FIELD.OUTPUT]);
|
|
231
|
+
}
|
|
232
|
+
function buildWorkflowErrorKey() {
|
|
233
|
+
return pack2([KEY_PREFIX.WORKFLOW, WORKFLOW_FIELD.ERROR]);
|
|
234
|
+
}
|
|
235
|
+
function buildWorkflowInputKey() {
|
|
236
|
+
return pack2([KEY_PREFIX.WORKFLOW, WORKFLOW_FIELD.INPUT]);
|
|
237
|
+
}
|
|
238
|
+
function buildEntryMetadataKey(entryId) {
|
|
239
|
+
return pack2([KEY_PREFIX.ENTRY_METADATA, entryId]);
|
|
240
|
+
}
|
|
241
|
+
function buildEntryMetadataPrefix() {
|
|
242
|
+
return pack2([KEY_PREFIX.ENTRY_METADATA]);
|
|
243
|
+
}
|
|
244
|
+
function parseNameKey(key) {
|
|
245
|
+
const elements = unpack2(key);
|
|
246
|
+
if (elements.length !== 2 || elements[0] !== KEY_PREFIX.NAMES) {
|
|
247
|
+
throw new Error("Invalid name key");
|
|
248
|
+
}
|
|
249
|
+
return elements[1];
|
|
250
|
+
}
|
|
251
|
+
function keyStartsWith(key, prefix) {
|
|
252
|
+
if (key.length < prefix.length) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
256
|
+
if (key[i] !== prefix[i]) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
function compareKeys(a, b) {
|
|
263
|
+
const minLen = Math.min(a.length, b.length);
|
|
264
|
+
for (let i = 0; i < minLen; i++) {
|
|
265
|
+
if (a[i] !== b[i]) {
|
|
266
|
+
return a[i] - b[i];
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return a.length - b.length;
|
|
270
|
+
}
|
|
271
|
+
function keyToHex(key) {
|
|
272
|
+
return Array.from(key).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
273
|
+
}
|
|
274
|
+
|
|
104
275
|
// src/location.ts
|
|
105
276
|
function isLoopIterationMarker(segment) {
|
|
106
277
|
return typeof segment === "object" && "loop" in segment;
|
|
@@ -1223,111 +1394,6 @@ function deserializeName(bytes) {
|
|
|
1223
1394
|
return decoder.decode(bytes);
|
|
1224
1395
|
}
|
|
1225
1396
|
|
|
1226
|
-
// src/keys.ts
|
|
1227
|
-
import * as tuple from "fdb-tuple";
|
|
1228
|
-
var KEY_PREFIX = {
|
|
1229
|
-
NAMES: 1,
|
|
1230
|
-
// Name registry: [1, index]
|
|
1231
|
-
HISTORY: 2,
|
|
1232
|
-
// History entries: [2, ...locationSegments]
|
|
1233
|
-
WORKFLOW: 3,
|
|
1234
|
-
// Workflow metadata: [3, field]
|
|
1235
|
-
ENTRY_METADATA: 4
|
|
1236
|
-
// Entry metadata: [4, entryId]
|
|
1237
|
-
};
|
|
1238
|
-
var WORKFLOW_FIELD = {
|
|
1239
|
-
STATE: 1,
|
|
1240
|
-
OUTPUT: 2,
|
|
1241
|
-
ERROR: 3,
|
|
1242
|
-
INPUT: 4
|
|
1243
|
-
};
|
|
1244
|
-
function segmentToTuple(segment) {
|
|
1245
|
-
if (typeof segment === "number") {
|
|
1246
|
-
return segment;
|
|
1247
|
-
}
|
|
1248
|
-
return [segment.loop, segment.iteration];
|
|
1249
|
-
}
|
|
1250
|
-
function locationToTupleElements(location) {
|
|
1251
|
-
return location.map(segmentToTuple);
|
|
1252
|
-
}
|
|
1253
|
-
function bufferToUint8Array(buf) {
|
|
1254
|
-
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
1255
|
-
}
|
|
1256
|
-
function uint8ArrayToBuffer(arr) {
|
|
1257
|
-
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
1258
|
-
}
|
|
1259
|
-
function pack2(items) {
|
|
1260
|
-
const buf = tuple.pack(items);
|
|
1261
|
-
return bufferToUint8Array(buf);
|
|
1262
|
-
}
|
|
1263
|
-
function unpack2(data) {
|
|
1264
|
-
const buf = uint8ArrayToBuffer(data);
|
|
1265
|
-
return tuple.unpack(buf);
|
|
1266
|
-
}
|
|
1267
|
-
function buildNameKey(index) {
|
|
1268
|
-
return pack2([KEY_PREFIX.NAMES, index]);
|
|
1269
|
-
}
|
|
1270
|
-
function buildNamePrefix() {
|
|
1271
|
-
return pack2([KEY_PREFIX.NAMES]);
|
|
1272
|
-
}
|
|
1273
|
-
function buildHistoryKey(location) {
|
|
1274
|
-
return pack2([KEY_PREFIX.HISTORY, ...locationToTupleElements(location)]);
|
|
1275
|
-
}
|
|
1276
|
-
function buildHistoryPrefix(location) {
|
|
1277
|
-
return pack2([KEY_PREFIX.HISTORY, ...locationToTupleElements(location)]);
|
|
1278
|
-
}
|
|
1279
|
-
function buildHistoryPrefixAll() {
|
|
1280
|
-
return pack2([KEY_PREFIX.HISTORY]);
|
|
1281
|
-
}
|
|
1282
|
-
function buildWorkflowStateKey() {
|
|
1283
|
-
return pack2([KEY_PREFIX.WORKFLOW, WORKFLOW_FIELD.STATE]);
|
|
1284
|
-
}
|
|
1285
|
-
function buildWorkflowOutputKey() {
|
|
1286
|
-
return pack2([KEY_PREFIX.WORKFLOW, WORKFLOW_FIELD.OUTPUT]);
|
|
1287
|
-
}
|
|
1288
|
-
function buildWorkflowErrorKey() {
|
|
1289
|
-
return pack2([KEY_PREFIX.WORKFLOW, WORKFLOW_FIELD.ERROR]);
|
|
1290
|
-
}
|
|
1291
|
-
function buildWorkflowInputKey() {
|
|
1292
|
-
return pack2([KEY_PREFIX.WORKFLOW, WORKFLOW_FIELD.INPUT]);
|
|
1293
|
-
}
|
|
1294
|
-
function buildEntryMetadataKey(entryId) {
|
|
1295
|
-
return pack2([KEY_PREFIX.ENTRY_METADATA, entryId]);
|
|
1296
|
-
}
|
|
1297
|
-
function buildEntryMetadataPrefix() {
|
|
1298
|
-
return pack2([KEY_PREFIX.ENTRY_METADATA]);
|
|
1299
|
-
}
|
|
1300
|
-
function parseNameKey(key) {
|
|
1301
|
-
const elements = unpack2(key);
|
|
1302
|
-
if (elements.length !== 2 || elements[0] !== KEY_PREFIX.NAMES) {
|
|
1303
|
-
throw new Error("Invalid name key");
|
|
1304
|
-
}
|
|
1305
|
-
return elements[1];
|
|
1306
|
-
}
|
|
1307
|
-
function keyStartsWith(key, prefix) {
|
|
1308
|
-
if (key.length < prefix.length) {
|
|
1309
|
-
return false;
|
|
1310
|
-
}
|
|
1311
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
1312
|
-
if (key[i] !== prefix[i]) {
|
|
1313
|
-
return false;
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
return true;
|
|
1317
|
-
}
|
|
1318
|
-
function compareKeys(a, b) {
|
|
1319
|
-
const minLen = Math.min(a.length, b.length);
|
|
1320
|
-
for (let i = 0; i < minLen; i++) {
|
|
1321
|
-
if (a[i] !== b[i]) {
|
|
1322
|
-
return a[i] - b[i];
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
return a.length - b.length;
|
|
1326
|
-
}
|
|
1327
|
-
function keyToHex(key) {
|
|
1328
|
-
return Array.from(key).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
1397
|
// src/storage.ts
|
|
1332
1398
|
function createStorage() {
|
|
1333
1399
|
return {
|
|
@@ -1437,7 +1503,7 @@ async function loadMetadata(storage, driver, entryId) {
|
|
|
1437
1503
|
}
|
|
1438
1504
|
return getOrCreateMetadata(storage, entryId);
|
|
1439
1505
|
}
|
|
1440
|
-
async function flush(storage, driver, onHistoryUpdated) {
|
|
1506
|
+
async function flush(storage, driver, onHistoryUpdated, pendingDeletions) {
|
|
1441
1507
|
const writes = [];
|
|
1442
1508
|
let historyUpdated = false;
|
|
1443
1509
|
for (let i = storage.flushedNameCount; i < storage.nameRegistry.length; i++) {
|
|
@@ -1492,6 +1558,22 @@ async function flush(storage, driver, onHistoryUpdated) {
|
|
|
1492
1558
|
if (writes.length > 0) {
|
|
1493
1559
|
await driver.batch(writes);
|
|
1494
1560
|
}
|
|
1561
|
+
if (pendingDeletions) {
|
|
1562
|
+
const deleteOps = [];
|
|
1563
|
+
for (const prefix of pendingDeletions.prefixes) {
|
|
1564
|
+
deleteOps.push(driver.deletePrefix(prefix));
|
|
1565
|
+
}
|
|
1566
|
+
for (const range of pendingDeletions.ranges) {
|
|
1567
|
+
deleteOps.push(driver.deleteRange(range.start, range.end));
|
|
1568
|
+
}
|
|
1569
|
+
for (const key of pendingDeletions.keys) {
|
|
1570
|
+
deleteOps.push(driver.delete(key));
|
|
1571
|
+
}
|
|
1572
|
+
if (deleteOps.length > 0) {
|
|
1573
|
+
await Promise.all(deleteOps);
|
|
1574
|
+
historyUpdated = true;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1495
1577
|
storage.flushedNameCount = storage.nameRegistry.length;
|
|
1496
1578
|
storage.flushedState = storage.state;
|
|
1497
1579
|
storage.flushedOutput = storage.output;
|
|
@@ -1501,21 +1583,27 @@ async function flush(storage, driver, onHistoryUpdated) {
|
|
|
1501
1583
|
}
|
|
1502
1584
|
}
|
|
1503
1585
|
async function deleteEntriesWithPrefix(storage, driver, prefixLocation, onHistoryUpdated) {
|
|
1504
|
-
const
|
|
1586
|
+
const deletions = collectDeletionsForPrefix(storage, prefixLocation);
|
|
1587
|
+
await driver.deletePrefix(deletions.prefixes[0]);
|
|
1588
|
+
await Promise.all(deletions.keys.map((key) => driver.delete(key)));
|
|
1589
|
+
if (deletions.keys.length > 0 && onHistoryUpdated) {
|
|
1590
|
+
onHistoryUpdated();
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
function collectDeletionsForPrefix(storage, prefixLocation) {
|
|
1594
|
+
const pending = {
|
|
1595
|
+
prefixes: [buildHistoryPrefix(prefixLocation)],
|
|
1596
|
+
keys: [],
|
|
1597
|
+
ranges: []
|
|
1598
|
+
};
|
|
1505
1599
|
for (const [key, entry] of storage.history.entries) {
|
|
1506
1600
|
if (isLocationPrefix(prefixLocation, entry.location)) {
|
|
1507
|
-
|
|
1601
|
+
pending.keys.push(buildEntryMetadataKey(entry.id));
|
|
1508
1602
|
storage.entryMetadata.delete(entry.id);
|
|
1509
1603
|
storage.history.entries.delete(key);
|
|
1510
1604
|
}
|
|
1511
1605
|
}
|
|
1512
|
-
|
|
1513
|
-
await Promise.all(
|
|
1514
|
-
entryIds.map((id) => driver.delete(buildEntryMetadataKey(id)))
|
|
1515
|
-
);
|
|
1516
|
-
if (entryIds.length > 0 && onHistoryUpdated) {
|
|
1517
|
-
onHistoryUpdated();
|
|
1518
|
-
}
|
|
1606
|
+
return pending;
|
|
1519
1607
|
}
|
|
1520
1608
|
function getEntry(storage, location) {
|
|
1521
1609
|
const key = locationToKey(storage, location);
|
|
@@ -1554,9 +1642,7 @@ function setLongTimeout(listener, after) {
|
|
|
1554
1642
|
var DEFAULT_MAX_RETRIES = 3;
|
|
1555
1643
|
var DEFAULT_RETRY_BACKOFF_BASE = 100;
|
|
1556
1644
|
var DEFAULT_RETRY_BACKOFF_MAX = 3e4;
|
|
1557
|
-
var
|
|
1558
|
-
var DEFAULT_LOOP_HISTORY_EVERY = 20;
|
|
1559
|
-
var DEFAULT_LOOP_HISTORY_KEEP = 20;
|
|
1645
|
+
var DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL = 20;
|
|
1560
1646
|
var DEFAULT_STEP_TIMEOUT = 3e4;
|
|
1561
1647
|
var QUEUE_HISTORY_MESSAGE_MARKER = "__rivetWorkflowQueueMessage";
|
|
1562
1648
|
function calculateBackoff(attempts, base, max) {
|
|
@@ -1571,7 +1657,7 @@ var StepTimeoutError = class extends Error {
|
|
|
1571
1657
|
}
|
|
1572
1658
|
};
|
|
1573
1659
|
var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
1574
|
-
constructor(workflowId, storage, driver, messageDriver, location = emptyLocation(), abortController, mode = "forward", rollbackActions, rollbackCheckpointSet = false, historyNotifier, logger) {
|
|
1660
|
+
constructor(workflowId, storage, driver, messageDriver, location = emptyLocation(), abortController, mode = "forward", rollbackActions, rollbackCheckpointSet = false, historyNotifier, onError, logger, visitedKeys) {
|
|
1575
1661
|
this.workflowId = workflowId;
|
|
1576
1662
|
this.storage = storage;
|
|
1577
1663
|
this.driver = driver;
|
|
@@ -1582,12 +1668,14 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1582
1668
|
this.rollbackActions = rollbackActions;
|
|
1583
1669
|
this.rollbackCheckpointSet = rollbackCheckpointSet;
|
|
1584
1670
|
this.historyNotifier = historyNotifier;
|
|
1671
|
+
this.onError = onError;
|
|
1585
1672
|
this.logger = logger;
|
|
1673
|
+
this.visitedKeys = visitedKeys ?? /* @__PURE__ */ new Set();
|
|
1586
1674
|
}
|
|
1587
1675
|
entryInProgress = false;
|
|
1588
1676
|
abortController;
|
|
1589
1677
|
currentLocation;
|
|
1590
|
-
visitedKeys
|
|
1678
|
+
visitedKeys;
|
|
1591
1679
|
mode;
|
|
1592
1680
|
rollbackActions;
|
|
1593
1681
|
rollbackCheckpointSet;
|
|
@@ -1595,6 +1683,7 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1595
1683
|
usedNamesInExecution = /* @__PURE__ */ new Set();
|
|
1596
1684
|
pendingCompletableMessageIds = /* @__PURE__ */ new Set();
|
|
1597
1685
|
historyNotifier;
|
|
1686
|
+
onError;
|
|
1598
1687
|
logger;
|
|
1599
1688
|
get abortSignal() {
|
|
1600
1689
|
return this.abortController.signal;
|
|
@@ -1637,7 +1726,9 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1637
1726
|
this.rollbackActions,
|
|
1638
1727
|
this.rollbackCheckpointSet,
|
|
1639
1728
|
this.historyNotifier,
|
|
1640
|
-
this.
|
|
1729
|
+
this.onError,
|
|
1730
|
+
this.logger,
|
|
1731
|
+
this.visitedKeys
|
|
1641
1732
|
);
|
|
1642
1733
|
}
|
|
1643
1734
|
/**
|
|
@@ -1647,6 +1738,36 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1647
1738
|
if (!this.logger) return;
|
|
1648
1739
|
this.logger[level](data);
|
|
1649
1740
|
}
|
|
1741
|
+
async notifyError(event) {
|
|
1742
|
+
if (!this.onError) {
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
try {
|
|
1746
|
+
await this.onError(event);
|
|
1747
|
+
} catch (error) {
|
|
1748
|
+
this.log("warn", {
|
|
1749
|
+
msg: "workflow error hook failed",
|
|
1750
|
+
hookEventType: getErrorEventTag(event),
|
|
1751
|
+
error: extractErrorInfo(error)
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
async notifyStepError(config2, attempt, error, opts) {
|
|
1756
|
+
const maxRetries = config2.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
1757
|
+
await this.notifyError({
|
|
1758
|
+
step: {
|
|
1759
|
+
workflowId: this.workflowId,
|
|
1760
|
+
stepName: config2.name,
|
|
1761
|
+
attempt,
|
|
1762
|
+
maxRetries,
|
|
1763
|
+
remainingRetries: Math.max(0, maxRetries - (attempt - 1)),
|
|
1764
|
+
willRetry: opts.willRetry,
|
|
1765
|
+
retryDelay: opts.retryDelay,
|
|
1766
|
+
retryAt: opts.retryAt,
|
|
1767
|
+
error: extractErrorInfo(error)
|
|
1768
|
+
}
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1650
1771
|
/**
|
|
1651
1772
|
* Mark a key as visited.
|
|
1652
1773
|
*/
|
|
@@ -1790,10 +1911,24 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1790
1911
|
if (metadata2.status === "completed" || stepData.output !== void 0) {
|
|
1791
1912
|
return stepData.output;
|
|
1792
1913
|
}
|
|
1793
|
-
const
|
|
1794
|
-
if (metadata2.attempts
|
|
1914
|
+
const maxRetries2 = config2.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
1915
|
+
if (metadata2.attempts > maxRetries2) {
|
|
1795
1916
|
const lastError = stepData.error ?? metadata2.error;
|
|
1796
|
-
|
|
1917
|
+
const exhaustedError = markErrorReported(
|
|
1918
|
+
new StepExhaustedError(config2.name, lastError)
|
|
1919
|
+
);
|
|
1920
|
+
if (metadata2.status !== "exhausted") {
|
|
1921
|
+
metadata2.status = "exhausted";
|
|
1922
|
+
metadata2.dirty = true;
|
|
1923
|
+
await this.flushStorage();
|
|
1924
|
+
await this.notifyStepError(
|
|
1925
|
+
config2,
|
|
1926
|
+
metadata2.attempts,
|
|
1927
|
+
exhaustedError,
|
|
1928
|
+
{ willRetry: false }
|
|
1929
|
+
);
|
|
1930
|
+
}
|
|
1931
|
+
throw exhaustedError;
|
|
1797
1932
|
}
|
|
1798
1933
|
const backoffDelay = calculateBackoff(
|
|
1799
1934
|
metadata2.attempts,
|
|
@@ -1808,7 +1943,11 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1808
1943
|
}
|
|
1809
1944
|
const entry = existing ?? createEntry(location, { type: "step", data: {} });
|
|
1810
1945
|
if (!existing) {
|
|
1811
|
-
this.log("debug", {
|
|
1946
|
+
this.log("debug", {
|
|
1947
|
+
msg: "executing new step",
|
|
1948
|
+
step: config2.name,
|
|
1949
|
+
key
|
|
1950
|
+
});
|
|
1812
1951
|
const nameIndex = registerName(this.storage, config2.name);
|
|
1813
1952
|
entry.location = [...location];
|
|
1814
1953
|
entry.location[entry.location.length - 1] = nameIndex;
|
|
@@ -1817,6 +1956,9 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1817
1956
|
this.log("debug", { msg: "retrying step", step: config2.name, key });
|
|
1818
1957
|
}
|
|
1819
1958
|
const metadata = getOrCreateMetadata(this.storage, entry.id);
|
|
1959
|
+
const maxRetries = config2.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
1960
|
+
const retryBackoffBase = config2.retryBackoffBase ?? DEFAULT_RETRY_BACKOFF_BASE;
|
|
1961
|
+
const retryBackoffMax = config2.retryBackoffMax ?? DEFAULT_RETRY_BACKOFF_MAX;
|
|
1820
1962
|
metadata.status = "running";
|
|
1821
1963
|
metadata.attempts++;
|
|
1822
1964
|
metadata.lastAttemptAt = Date.now();
|
|
@@ -1836,10 +1978,18 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1836
1978
|
metadata.error = void 0;
|
|
1837
1979
|
metadata.completedAt = Date.now();
|
|
1838
1980
|
if (!config2.ephemeral) {
|
|
1839
|
-
this.log("debug", {
|
|
1981
|
+
this.log("debug", {
|
|
1982
|
+
msg: "flushing step",
|
|
1983
|
+
step: config2.name,
|
|
1984
|
+
key
|
|
1985
|
+
});
|
|
1840
1986
|
await this.flushStorage();
|
|
1841
1987
|
}
|
|
1842
|
-
this.log("debug", {
|
|
1988
|
+
this.log("debug", {
|
|
1989
|
+
msg: "step completed",
|
|
1990
|
+
step: config2.name,
|
|
1991
|
+
key
|
|
1992
|
+
});
|
|
1843
1993
|
return output;
|
|
1844
1994
|
} catch (error) {
|
|
1845
1995
|
if (error instanceof StepTimeoutError) {
|
|
@@ -1850,7 +2000,10 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1850
2000
|
metadata.status = "exhausted";
|
|
1851
2001
|
metadata.error = String(error);
|
|
1852
2002
|
await this.flushStorage();
|
|
1853
|
-
|
|
2003
|
+
await this.notifyStepError(config2, metadata.attempts, error, {
|
|
2004
|
+
willRetry: false
|
|
2005
|
+
});
|
|
2006
|
+
throw markErrorReported(new CriticalError(error.message));
|
|
1854
2007
|
}
|
|
1855
2008
|
if (error instanceof CriticalError || error instanceof RollbackError) {
|
|
1856
2009
|
if (entry.kind.type === "step") {
|
|
@@ -1860,16 +2013,53 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1860
2013
|
metadata.status = "exhausted";
|
|
1861
2014
|
metadata.error = String(error);
|
|
1862
2015
|
await this.flushStorage();
|
|
1863
|
-
|
|
2016
|
+
await this.notifyStepError(config2, metadata.attempts, error, {
|
|
2017
|
+
willRetry: false
|
|
2018
|
+
});
|
|
2019
|
+
throw markErrorReported(error);
|
|
1864
2020
|
}
|
|
1865
2021
|
if (entry.kind.type === "step") {
|
|
1866
2022
|
entry.kind.data.error = String(error);
|
|
1867
2023
|
}
|
|
1868
2024
|
entry.dirty = true;
|
|
1869
|
-
metadata.
|
|
2025
|
+
const willRetry = metadata.attempts <= maxRetries;
|
|
2026
|
+
metadata.status = willRetry ? "failed" : "exhausted";
|
|
1870
2027
|
metadata.error = String(error);
|
|
1871
2028
|
await this.flushStorage();
|
|
1872
|
-
|
|
2029
|
+
if (willRetry) {
|
|
2030
|
+
const retryDelay = calculateBackoff(
|
|
2031
|
+
metadata.attempts,
|
|
2032
|
+
retryBackoffBase,
|
|
2033
|
+
retryBackoffMax
|
|
2034
|
+
);
|
|
2035
|
+
const retryAt = metadata.lastAttemptAt + retryDelay;
|
|
2036
|
+
await this.notifyStepError(
|
|
2037
|
+
config2,
|
|
2038
|
+
metadata.attempts,
|
|
2039
|
+
error,
|
|
2040
|
+
{
|
|
2041
|
+
willRetry: true,
|
|
2042
|
+
retryDelay,
|
|
2043
|
+
retryAt
|
|
2044
|
+
}
|
|
2045
|
+
);
|
|
2046
|
+
throw new StepFailedError(
|
|
2047
|
+
config2.name,
|
|
2048
|
+
error,
|
|
2049
|
+
metadata.attempts,
|
|
2050
|
+
retryAt
|
|
2051
|
+
);
|
|
2052
|
+
}
|
|
2053
|
+
const exhaustedError = markErrorReported(
|
|
2054
|
+
new StepExhaustedError(config2.name, String(error))
|
|
2055
|
+
);
|
|
2056
|
+
await this.notifyStepError(
|
|
2057
|
+
config2,
|
|
2058
|
+
metadata.attempts,
|
|
2059
|
+
error,
|
|
2060
|
+
{ willRetry: false }
|
|
2061
|
+
);
|
|
2062
|
+
throw exhaustedError;
|
|
1873
2063
|
}
|
|
1874
2064
|
}
|
|
1875
2065
|
/**
|
|
@@ -1970,7 +2160,11 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
1970
2160
|
);
|
|
1971
2161
|
}
|
|
1972
2162
|
const loopData = existing.kind.data;
|
|
1973
|
-
metadata = await loadMetadata(
|
|
2163
|
+
metadata = await loadMetadata(
|
|
2164
|
+
this.storage,
|
|
2165
|
+
this.driver,
|
|
2166
|
+
existing.id
|
|
2167
|
+
);
|
|
1974
2168
|
if (rollbackMode) {
|
|
1975
2169
|
if (loopData.output !== void 0) {
|
|
1976
2170
|
return loopData.output;
|
|
@@ -2008,10 +2202,15 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
2008
2202
|
metadata.error = void 0;
|
|
2009
2203
|
metadata.dirty = true;
|
|
2010
2204
|
}
|
|
2011
|
-
const
|
|
2012
|
-
const
|
|
2013
|
-
|
|
2205
|
+
const historyPruneInterval = config2.historyPruneInterval ?? DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL;
|
|
2206
|
+
const historySize = config2.historySize ?? historyPruneInterval;
|
|
2207
|
+
let lastPrunedUpTo = 0;
|
|
2208
|
+
let deferredFlush = null;
|
|
2014
2209
|
while (true) {
|
|
2210
|
+
if (deferredFlush) {
|
|
2211
|
+
await deferredFlush;
|
|
2212
|
+
deferredFlush = null;
|
|
2213
|
+
}
|
|
2015
2214
|
if (rollbackMode && rollbackSingleIteration) {
|
|
2016
2215
|
if (rollbackIterationRan) {
|
|
2017
2216
|
return rollbackOutput;
|
|
@@ -2046,13 +2245,13 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
2046
2245
|
metadata.completedAt = Date.now();
|
|
2047
2246
|
metadata.dirty = true;
|
|
2048
2247
|
}
|
|
2049
|
-
|
|
2050
|
-
await this.forgetOldIterations(
|
|
2248
|
+
const deletions = this.collectLoopPruning(
|
|
2051
2249
|
location,
|
|
2052
2250
|
iteration + 1,
|
|
2053
|
-
|
|
2054
|
-
|
|
2251
|
+
historySize,
|
|
2252
|
+
lastPrunedUpTo
|
|
2055
2253
|
);
|
|
2254
|
+
await this.flushStorageWithDeletions(deletions);
|
|
2056
2255
|
if (rollbackMode && rollbackSingleIteration) {
|
|
2057
2256
|
rollbackOutput = result.value;
|
|
2058
2257
|
rollbackIterationRan = true;
|
|
@@ -2064,60 +2263,75 @@ var WorkflowContextImpl = class _WorkflowContextImpl {
|
|
|
2064
2263
|
state = result.state;
|
|
2065
2264
|
}
|
|
2066
2265
|
iteration++;
|
|
2067
|
-
if (
|
|
2266
|
+
if (!rollbackMode) {
|
|
2068
2267
|
if (entry.kind.type === "loop") {
|
|
2069
2268
|
entry.kind.data.state = state;
|
|
2070
2269
|
entry.kind.data.iteration = iteration;
|
|
2071
2270
|
}
|
|
2072
2271
|
entry.dirty = true;
|
|
2073
|
-
|
|
2074
|
-
|
|
2272
|
+
}
|
|
2273
|
+
if (iteration % historyPruneInterval === 0) {
|
|
2274
|
+
const deletions = this.collectLoopPruning(
|
|
2075
2275
|
location,
|
|
2076
2276
|
iteration,
|
|
2077
|
-
|
|
2078
|
-
|
|
2277
|
+
historySize,
|
|
2278
|
+
lastPrunedUpTo
|
|
2079
2279
|
);
|
|
2280
|
+
lastPrunedUpTo = Math.max(0, iteration - historySize);
|
|
2281
|
+
deferredFlush = this.flushStorageWithDeletions(deletions);
|
|
2080
2282
|
}
|
|
2081
2283
|
}
|
|
2082
2284
|
}
|
|
2083
2285
|
/**
|
|
2084
|
-
*
|
|
2085
|
-
*
|
|
2086
|
-
* Loop locations always end with a NameIndex (number) because loops are
|
|
2087
|
-
* created via appendName(). Even for nested loops, the innermost loop's
|
|
2088
|
-
* location ends with its name index:
|
|
2089
|
-
*
|
|
2090
|
-
* ctx.loop("outer") → location: [outerIndex]
|
|
2091
|
-
* iteration 0 → location: [{ loop: outerIndex, iteration: 0 }]
|
|
2092
|
-
* ctx.loop("inner") → location: [{ loop: outerIndex, iteration: 0 }, innerIndex]
|
|
2286
|
+
* Collect pending deletions for loop history pruning.
|
|
2093
2287
|
*
|
|
2094
|
-
*
|
|
2095
|
-
*
|
|
2288
|
+
* Only deletes iterations in the range [fromIteration, keepFrom) where
|
|
2289
|
+
* keepFrom = currentIteration - historySize. This avoids re-scanning
|
|
2290
|
+
* already-deleted iterations.
|
|
2096
2291
|
*/
|
|
2097
|
-
|
|
2098
|
-
if (
|
|
2099
|
-
return;
|
|
2292
|
+
collectLoopPruning(loopLocation, currentIteration, historySize, fromIteration) {
|
|
2293
|
+
if (currentIteration <= historySize) {
|
|
2294
|
+
return void 0;
|
|
2100
2295
|
}
|
|
2101
|
-
|
|
2102
|
-
|
|
2296
|
+
const keepFrom = Math.max(0, currentIteration - historySize);
|
|
2297
|
+
if (fromIteration >= keepFrom) {
|
|
2298
|
+
return void 0;
|
|
2103
2299
|
}
|
|
2104
|
-
const keepFrom = Math.max(0, currentIteration - historyKeep);
|
|
2105
2300
|
const loopSegment = loopLocation[loopLocation.length - 1];
|
|
2106
2301
|
if (typeof loopSegment !== "number") {
|
|
2107
2302
|
throw new Error("Expected loop location to end with a name index");
|
|
2108
2303
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2304
|
+
const range = buildLoopIterationRange(
|
|
2305
|
+
loopLocation,
|
|
2306
|
+
loopSegment,
|
|
2307
|
+
fromIteration,
|
|
2308
|
+
keepFrom
|
|
2309
|
+
);
|
|
2310
|
+
const metadataKeys = [];
|
|
2311
|
+
for (const [key, entry] of this.storage.history.entries) {
|
|
2312
|
+
if (!isLocationPrefix(loopLocation, entry.location)) {
|
|
2313
|
+
continue;
|
|
2314
|
+
}
|
|
2315
|
+
const iterationSegment = entry.location[loopLocation.length];
|
|
2316
|
+
if (!iterationSegment || typeof iterationSegment === "number" || iterationSegment.loop !== loopSegment || iterationSegment.iteration < fromIteration || iterationSegment.iteration >= keepFrom) {
|
|
2317
|
+
continue;
|
|
2318
|
+
}
|
|
2319
|
+
metadataKeys.push(buildEntryMetadataKey(entry.id));
|
|
2320
|
+
this.storage.entryMetadata.delete(entry.id);
|
|
2321
|
+
this.storage.history.entries.delete(key);
|
|
2120
2322
|
}
|
|
2323
|
+
return {
|
|
2324
|
+
prefixes: [],
|
|
2325
|
+
keys: metadataKeys,
|
|
2326
|
+
ranges: [range]
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
/**
|
|
2330
|
+
* Flush storage with optional pending deletions so pruning
|
|
2331
|
+
* happens alongside the state write.
|
|
2332
|
+
*/
|
|
2333
|
+
async flushStorageWithDeletions(deletions) {
|
|
2334
|
+
await flush(this.storage, this.driver, this.historyNotifier, deletions);
|
|
2121
2335
|
}
|
|
2122
2336
|
// === Sleep ===
|
|
2123
2337
|
async sleep(name, durationMs) {
|
|
@@ -2945,7 +3159,7 @@ async function awaitWithEviction(promise, abortSignal) {
|
|
|
2945
3159
|
cleanup();
|
|
2946
3160
|
}
|
|
2947
3161
|
}
|
|
2948
|
-
async function executeRollback(workflowId, workflowFn, input, driver, messageDriver, abortController, storage, historyNotifier, logger) {
|
|
3162
|
+
async function executeRollback(workflowId, workflowFn, input, driver, messageDriver, abortController, storage, historyNotifier, onError, logger) {
|
|
2949
3163
|
const rollbackActions = [];
|
|
2950
3164
|
const ctx = new WorkflowContextImpl(
|
|
2951
3165
|
workflowId,
|
|
@@ -2958,6 +3172,7 @@ async function executeRollback(workflowId, workflowFn, input, driver, messageDri
|
|
|
2958
3172
|
rollbackActions,
|
|
2959
3173
|
false,
|
|
2960
3174
|
historyNotifier,
|
|
3175
|
+
onError,
|
|
2961
3176
|
logger
|
|
2962
3177
|
);
|
|
2963
3178
|
try {
|
|
@@ -2983,6 +3198,7 @@ async function executeRollback(workflowId, workflowFn, input, driver, messageDri
|
|
|
2983
3198
|
if (metadata.rollbackCompletedAt !== void 0) {
|
|
2984
3199
|
continue;
|
|
2985
3200
|
}
|
|
3201
|
+
let rollbackEvent;
|
|
2986
3202
|
try {
|
|
2987
3203
|
await awaitWithEviction(
|
|
2988
3204
|
action.rollback(rollbackContext, action.output),
|
|
@@ -2995,13 +3211,39 @@ async function executeRollback(workflowId, workflowFn, input, driver, messageDri
|
|
|
2995
3211
|
throw error;
|
|
2996
3212
|
}
|
|
2997
3213
|
metadata.rollbackError = error instanceof Error ? error.message : String(error);
|
|
3214
|
+
if (onError) {
|
|
3215
|
+
rollbackEvent = {
|
|
3216
|
+
rollback: {
|
|
3217
|
+
workflowId,
|
|
3218
|
+
stepName: action.name,
|
|
3219
|
+
error: extractErrorInfo(error)
|
|
3220
|
+
}
|
|
3221
|
+
};
|
|
3222
|
+
}
|
|
3223
|
+
if (error instanceof Error) {
|
|
3224
|
+
markErrorReported(error);
|
|
3225
|
+
}
|
|
2998
3226
|
throw error;
|
|
2999
3227
|
} finally {
|
|
3000
3228
|
metadata.dirty = true;
|
|
3001
3229
|
await flush(storage, driver, historyNotifier);
|
|
3230
|
+
if (rollbackEvent && onError) {
|
|
3231
|
+
await notifyError(onError, logger, rollbackEvent);
|
|
3232
|
+
}
|
|
3002
3233
|
}
|
|
3003
3234
|
}
|
|
3004
3235
|
}
|
|
3236
|
+
async function notifyError(onError, logger, event) {
|
|
3237
|
+
try {
|
|
3238
|
+
await onError(event);
|
|
3239
|
+
} catch (error) {
|
|
3240
|
+
logger == null ? void 0 : logger.warn({
|
|
3241
|
+
msg: "workflow error hook failed",
|
|
3242
|
+
hookEventType: getErrorEventTag(event),
|
|
3243
|
+
error: extractErrorInfo(error)
|
|
3244
|
+
});
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3005
3247
|
async function setSleepState(storage, driver, workflowId, deadline, messageNames, historyNotifier) {
|
|
3006
3248
|
storage.state = "sleeping";
|
|
3007
3249
|
await flush(storage, driver, historyNotifier);
|
|
@@ -3021,10 +3263,9 @@ async function setEvictedState(storage, driver, historyNotifier) {
|
|
|
3021
3263
|
await flush(storage, driver, historyNotifier);
|
|
3022
3264
|
return { state: storage.state };
|
|
3023
3265
|
}
|
|
3024
|
-
async function setRetryState(storage, driver, workflowId, historyNotifier) {
|
|
3266
|
+
async function setRetryState(storage, driver, workflowId, retryAt, historyNotifier) {
|
|
3025
3267
|
storage.state = "sleeping";
|
|
3026
3268
|
await flush(storage, driver, historyNotifier);
|
|
3027
|
-
const retryAt = Date.now() + 100;
|
|
3028
3269
|
await driver.setAlarm(workflowId, retryAt);
|
|
3029
3270
|
return { state: "sleeping", sleepUntil: retryAt };
|
|
3030
3271
|
}
|
|
@@ -3065,7 +3306,7 @@ async function waitForSleep(runtime, deadline, abortSignal) {
|
|
|
3065
3306
|
}
|
|
3066
3307
|
}
|
|
3067
3308
|
}
|
|
3068
|
-
async function executeLiveWorkflow(workflowId, workflowFn, input, driver, messageDriver, abortController, runtime, onHistoryUpdated, logger) {
|
|
3309
|
+
async function executeLiveWorkflow(workflowId, workflowFn, input, driver, messageDriver, abortController, runtime, onHistoryUpdated, onError, logger) {
|
|
3069
3310
|
let lastResult;
|
|
3070
3311
|
while (true) {
|
|
3071
3312
|
const result = await executeWorkflow(
|
|
@@ -3076,6 +3317,7 @@ async function executeLiveWorkflow(workflowId, workflowFn, input, driver, messag
|
|
|
3076
3317
|
messageDriver,
|
|
3077
3318
|
abortController,
|
|
3078
3319
|
onHistoryUpdated,
|
|
3320
|
+
onError,
|
|
3079
3321
|
logger
|
|
3080
3322
|
);
|
|
3081
3323
|
lastResult = result;
|
|
@@ -3157,6 +3399,7 @@ function runWorkflow(workflowId, workflowFn, input, driver, options = {}) {
|
|
|
3157
3399
|
abortController,
|
|
3158
3400
|
liveRuntime,
|
|
3159
3401
|
options.onHistoryUpdated,
|
|
3402
|
+
options.onError,
|
|
3160
3403
|
logger
|
|
3161
3404
|
) : executeWorkflow(
|
|
3162
3405
|
workflowId,
|
|
@@ -3166,6 +3409,7 @@ function runWorkflow(workflowId, workflowFn, input, driver, options = {}) {
|
|
|
3166
3409
|
messageDriver,
|
|
3167
3410
|
abortController,
|
|
3168
3411
|
options.onHistoryUpdated,
|
|
3412
|
+
options.onError,
|
|
3169
3413
|
logger
|
|
3170
3414
|
);
|
|
3171
3415
|
return {
|
|
@@ -3257,7 +3501,7 @@ function runWorkflow(workflowId, workflowFn, input, driver, options = {}) {
|
|
|
3257
3501
|
}
|
|
3258
3502
|
};
|
|
3259
3503
|
}
|
|
3260
|
-
async function executeWorkflow(workflowId, workflowFn, input, driver, messageDriver, abortController, onHistoryUpdated, logger) {
|
|
3504
|
+
async function executeWorkflow(workflowId, workflowFn, input, driver, messageDriver, abortController, onHistoryUpdated, onError, logger) {
|
|
3261
3505
|
var _a;
|
|
3262
3506
|
const storage = await loadStorage(driver);
|
|
3263
3507
|
const historyNotifier = onHistoryUpdated ? () => onHistoryUpdated(createHistorySnapshot(storage)) : void 0;
|
|
@@ -3299,6 +3543,7 @@ async function executeWorkflow(workflowId, workflowFn, input, driver, messageDri
|
|
|
3299
3543
|
abortController,
|
|
3300
3544
|
storage,
|
|
3301
3545
|
historyNotifier,
|
|
3546
|
+
onError,
|
|
3302
3547
|
logger
|
|
3303
3548
|
);
|
|
3304
3549
|
} catch (error) {
|
|
@@ -3326,6 +3571,7 @@ async function executeWorkflow(workflowId, workflowFn, input, driver, messageDri
|
|
|
3326
3571
|
void 0,
|
|
3327
3572
|
false,
|
|
3328
3573
|
historyNotifier,
|
|
3574
|
+
onError,
|
|
3329
3575
|
logger
|
|
3330
3576
|
);
|
|
3331
3577
|
storage.state = "running";
|
|
@@ -3363,11 +3609,20 @@ async function executeWorkflow(workflowId, workflowFn, input, driver, messageDri
|
|
|
3363
3609
|
storage,
|
|
3364
3610
|
driver,
|
|
3365
3611
|
workflowId,
|
|
3612
|
+
error.retryAt,
|
|
3366
3613
|
historyNotifier
|
|
3367
3614
|
);
|
|
3368
3615
|
}
|
|
3369
3616
|
if (error instanceof RollbackCheckpointError) {
|
|
3370
3617
|
await setFailedState(storage, driver, error, historyNotifier);
|
|
3618
|
+
if (onError && !isErrorReported(error)) {
|
|
3619
|
+
await notifyError(onError, logger, {
|
|
3620
|
+
workflow: {
|
|
3621
|
+
workflowId,
|
|
3622
|
+
error: extractErrorInfo(error)
|
|
3623
|
+
}
|
|
3624
|
+
});
|
|
3625
|
+
}
|
|
3371
3626
|
throw error;
|
|
3372
3627
|
}
|
|
3373
3628
|
storage.error = extractErrorInfo(error);
|
|
@@ -3383,6 +3638,7 @@ async function executeWorkflow(workflowId, workflowFn, input, driver, messageDri
|
|
|
3383
3638
|
abortController,
|
|
3384
3639
|
storage,
|
|
3385
3640
|
historyNotifier,
|
|
3641
|
+
onError,
|
|
3386
3642
|
logger
|
|
3387
3643
|
);
|
|
3388
3644
|
} catch (rollbackError) {
|
|
@@ -3393,37 +3649,23 @@ async function executeWorkflow(workflowId, workflowFn, input, driver, messageDri
|
|
|
3393
3649
|
}
|
|
3394
3650
|
storage.state = "failed";
|
|
3395
3651
|
await flush(storage, driver, historyNotifier);
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
const result = {
|
|
3402
|
-
name: error.name,
|
|
3403
|
-
message: error.message,
|
|
3404
|
-
stack: error.stack
|
|
3405
|
-
};
|
|
3406
|
-
const metadata = {};
|
|
3407
|
-
for (const key of Object.keys(error)) {
|
|
3408
|
-
if (key !== "name" && key !== "message" && key !== "stack") {
|
|
3409
|
-
const value = error[key];
|
|
3410
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
3411
|
-
metadata[key] = value;
|
|
3652
|
+
if (onError && !isErrorReported(error)) {
|
|
3653
|
+
await notifyError(onError, logger, {
|
|
3654
|
+
workflow: {
|
|
3655
|
+
workflowId,
|
|
3656
|
+
error: extractErrorInfo(error)
|
|
3412
3657
|
}
|
|
3658
|
+
});
|
|
3659
|
+
if (error instanceof CriticalError || error instanceof RollbackError || error instanceof StepExhaustedError) {
|
|
3660
|
+
markErrorReported(error);
|
|
3413
3661
|
}
|
|
3414
3662
|
}
|
|
3415
|
-
|
|
3416
|
-
result.metadata = metadata;
|
|
3417
|
-
}
|
|
3418
|
-
return result;
|
|
3663
|
+
throw error;
|
|
3419
3664
|
}
|
|
3420
|
-
return {
|
|
3421
|
-
name: "Error",
|
|
3422
|
-
message: String(error)
|
|
3423
|
-
};
|
|
3424
3665
|
}
|
|
3425
3666
|
|
|
3426
3667
|
export {
|
|
3668
|
+
extractErrorInfo,
|
|
3427
3669
|
CriticalError,
|
|
3428
3670
|
RollbackError,
|
|
3429
3671
|
RollbackCheckpointError,
|
|
@@ -3437,6 +3679,9 @@ export {
|
|
|
3437
3679
|
RaceError,
|
|
3438
3680
|
CancelledError,
|
|
3439
3681
|
EntryInProgressError,
|
|
3682
|
+
keyStartsWith,
|
|
3683
|
+
compareKeys,
|
|
3684
|
+
keyToHex,
|
|
3440
3685
|
isLoopIterationMarker,
|
|
3441
3686
|
registerName,
|
|
3442
3687
|
resolveName,
|
|
@@ -3447,9 +3692,6 @@ export {
|
|
|
3447
3692
|
parentLocation,
|
|
3448
3693
|
isLocationPrefix,
|
|
3449
3694
|
locationsEqual,
|
|
3450
|
-
keyStartsWith,
|
|
3451
|
-
compareKeys,
|
|
3452
|
-
keyToHex,
|
|
3453
3695
|
createStorage,
|
|
3454
3696
|
createHistorySnapshot,
|
|
3455
3697
|
generateId,
|
|
@@ -3465,12 +3707,10 @@ export {
|
|
|
3465
3707
|
DEFAULT_MAX_RETRIES,
|
|
3466
3708
|
DEFAULT_RETRY_BACKOFF_BASE,
|
|
3467
3709
|
DEFAULT_RETRY_BACKOFF_MAX,
|
|
3468
|
-
|
|
3469
|
-
DEFAULT_LOOP_HISTORY_EVERY,
|
|
3470
|
-
DEFAULT_LOOP_HISTORY_KEEP,
|
|
3710
|
+
DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL,
|
|
3471
3711
|
DEFAULT_STEP_TIMEOUT,
|
|
3472
3712
|
WorkflowContextImpl,
|
|
3473
3713
|
Loop,
|
|
3474
3714
|
runWorkflow
|
|
3475
3715
|
};
|
|
3476
|
-
//# sourceMappingURL=chunk-
|
|
3716
|
+
//# sourceMappingURL=chunk-MMWB37UG.js.map
|