@rivetkit/workflow-engine 2.1.0-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/LICENSE +203 -0
- package/dist/schemas/v1.ts +781 -0
- package/dist/tsup/chunk-GJ66YE5W.cjs +3441 -0
- package/dist/tsup/chunk-GJ66YE5W.cjs.map +1 -0
- package/dist/tsup/chunk-JWHWQBZP.js +3441 -0
- package/dist/tsup/chunk-JWHWQBZP.js.map +1 -0
- package/dist/tsup/index.cjs +93 -0
- package/dist/tsup/index.cjs.map +1 -0
- package/dist/tsup/index.d.cts +884 -0
- package/dist/tsup/index.d.ts +884 -0
- package/dist/tsup/index.js +93 -0
- package/dist/tsup/index.js.map +1 -0
- package/dist/tsup/testing.cjs +316 -0
- package/dist/tsup/testing.cjs.map +1 -0
- package/dist/tsup/testing.d.cts +52 -0
- package/dist/tsup/testing.d.ts +52 -0
- package/dist/tsup/testing.js +316 -0
- package/dist/tsup/testing.js.map +1 -0
- package/package.json +70 -0
- package/schemas/serde.ts +609 -0
- package/schemas/v1.bare +203 -0
- package/schemas/versioned.ts +107 -0
- package/src/context.ts +1845 -0
- package/src/driver.ts +103 -0
- package/src/errors.ts +170 -0
- package/src/index.ts +907 -0
- package/src/keys.ts +277 -0
- package/src/location.ts +168 -0
- package/src/storage.ts +364 -0
- package/src/testing.ts +292 -0
- package/src/types.ts +508 -0
- package/src/utils.ts +48 -0
|
@@ -0,0 +1,3441 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } var _class;// src/errors.ts
|
|
2
|
+
var CriticalError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "CriticalError";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
var RollbackError = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "RollbackError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var RollbackCheckpointError = class extends Error {
|
|
15
|
+
constructor() {
|
|
16
|
+
super("Rollback requires a checkpoint before any rollback step");
|
|
17
|
+
this.name = "RollbackCheckpointError";
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var SleepError = class extends Error {
|
|
21
|
+
constructor(deadline, messageNames) {
|
|
22
|
+
super(
|
|
23
|
+
messageNames && messageNames.length > 0 ? `Sleeping until ${deadline} or messages: ${messageNames.join(", ")}` : `Sleeping until ${deadline}`
|
|
24
|
+
);
|
|
25
|
+
this.deadline = deadline;
|
|
26
|
+
this.messageNames = messageNames;
|
|
27
|
+
this.name = "SleepError";
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var MessageWaitError = class extends Error {
|
|
31
|
+
constructor(messageNames) {
|
|
32
|
+
super(`Waiting for messages: ${messageNames.join(", ")}`);
|
|
33
|
+
this.messageNames = messageNames;
|
|
34
|
+
this.name = "MessageWaitError";
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var EvictedError = class extends Error {
|
|
38
|
+
constructor() {
|
|
39
|
+
super("Workflow evicted");
|
|
40
|
+
this.name = "EvictedError";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var RollbackStopError = class extends Error {
|
|
44
|
+
constructor() {
|
|
45
|
+
super("Rollback traversal halted");
|
|
46
|
+
this.name = "RollbackStopError";
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var HistoryDivergedError = class extends Error {
|
|
50
|
+
constructor(message) {
|
|
51
|
+
super(message);
|
|
52
|
+
this.name = "HistoryDivergedError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var StepExhaustedError = class extends Error {
|
|
56
|
+
constructor(stepName, lastError) {
|
|
57
|
+
super(
|
|
58
|
+
`Step "${stepName}" exhausted retries: ${_nullishCoalesce(lastError, () => ( "unknown error"))}`
|
|
59
|
+
);
|
|
60
|
+
this.stepName = stepName;
|
|
61
|
+
this.lastError = lastError;
|
|
62
|
+
this.name = "StepExhaustedError";
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var StepFailedError = class extends Error {
|
|
66
|
+
constructor(stepName, originalError, attempts) {
|
|
67
|
+
super(`Step "${stepName}" failed (attempt ${attempts})`);
|
|
68
|
+
this.stepName = stepName;
|
|
69
|
+
this.originalError = originalError;
|
|
70
|
+
this.attempts = attempts;
|
|
71
|
+
this.name = "StepFailedError";
|
|
72
|
+
this.cause = originalError;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var JoinError = class extends Error {
|
|
76
|
+
constructor(errors) {
|
|
77
|
+
super(`Join failed: ${Object.keys(errors).join(", ")}`);
|
|
78
|
+
this.errors = errors;
|
|
79
|
+
this.name = "JoinError";
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var RaceError = class extends Error {
|
|
83
|
+
constructor(message, errors) {
|
|
84
|
+
super(message);
|
|
85
|
+
this.errors = errors;
|
|
86
|
+
this.name = "RaceError";
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
var CancelledError = class extends Error {
|
|
90
|
+
constructor() {
|
|
91
|
+
super("Branch cancelled");
|
|
92
|
+
this.name = "CancelledError";
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var EntryInProgressError = class extends Error {
|
|
96
|
+
constructor() {
|
|
97
|
+
super(
|
|
98
|
+
"Cannot start a new workflow entry while another is in progress. Did you forget to await the previous step/loop/sleep?"
|
|
99
|
+
);
|
|
100
|
+
this.name = "EntryInProgressError";
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/location.ts
|
|
105
|
+
function isLoopIterationMarker(segment) {
|
|
106
|
+
return typeof segment === "object" && "loop" in segment;
|
|
107
|
+
}
|
|
108
|
+
function registerName(storage, name) {
|
|
109
|
+
const existing = storage.nameRegistry.indexOf(name);
|
|
110
|
+
if (existing !== -1) {
|
|
111
|
+
return existing;
|
|
112
|
+
}
|
|
113
|
+
storage.nameRegistry.push(name);
|
|
114
|
+
return storage.nameRegistry.length - 1;
|
|
115
|
+
}
|
|
116
|
+
function resolveName(storage, index) {
|
|
117
|
+
const name = storage.nameRegistry[index];
|
|
118
|
+
if (name === void 0) {
|
|
119
|
+
throw new Error(`Name index ${index} not found in registry`);
|
|
120
|
+
}
|
|
121
|
+
return name;
|
|
122
|
+
}
|
|
123
|
+
function locationToKey(storage, location) {
|
|
124
|
+
return location.map((segment) => {
|
|
125
|
+
if (typeof segment === "number") {
|
|
126
|
+
return resolveName(storage, segment);
|
|
127
|
+
}
|
|
128
|
+
return `~${segment.iteration}`;
|
|
129
|
+
}).join("/");
|
|
130
|
+
}
|
|
131
|
+
function appendName(storage, location, name) {
|
|
132
|
+
const nameIndex = registerName(storage, name);
|
|
133
|
+
return [...location, nameIndex];
|
|
134
|
+
}
|
|
135
|
+
function appendLoopIteration(storage, location, loopName, iteration) {
|
|
136
|
+
const loopIndex = registerName(storage, loopName);
|
|
137
|
+
return [...location, { loop: loopIndex, iteration }];
|
|
138
|
+
}
|
|
139
|
+
function emptyLocation() {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
function parentLocation(location) {
|
|
143
|
+
return location.slice(0, -1);
|
|
144
|
+
}
|
|
145
|
+
function isLocationPrefix(prefix, location) {
|
|
146
|
+
if (prefix.length > location.length) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
150
|
+
const prefixSegment = prefix[i];
|
|
151
|
+
const locationSegment = location[i];
|
|
152
|
+
if (typeof prefixSegment === "number" && typeof locationSegment === "number") {
|
|
153
|
+
if (prefixSegment !== locationSegment) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
} else if (isLoopIterationMarker(prefixSegment) && isLoopIterationMarker(locationSegment)) {
|
|
157
|
+
if (prefixSegment.loop !== locationSegment.loop || prefixSegment.iteration !== locationSegment.iteration) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
function locationsEqual(a, b) {
|
|
167
|
+
if (a.length !== b.length) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
return isLocationPrefix(a, b);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// schemas/serde.ts
|
|
174
|
+
var _cborx = require('cbor-x'); var cbor = _interopRequireWildcard(_cborx);
|
|
175
|
+
|
|
176
|
+
// dist/schemas/v1.ts
|
|
177
|
+
var _barets = require('@rivetkit/bare-ts'); var bare = _interopRequireWildcard(_barets);
|
|
178
|
+
var config = /* @__PURE__ */ bare.Config({});
|
|
179
|
+
function readCbor(bc) {
|
|
180
|
+
return bare.readData(bc);
|
|
181
|
+
}
|
|
182
|
+
function writeCbor(bc, x) {
|
|
183
|
+
bare.writeData(bc, x);
|
|
184
|
+
}
|
|
185
|
+
function readNameIndex(bc) {
|
|
186
|
+
return bare.readU32(bc);
|
|
187
|
+
}
|
|
188
|
+
function writeNameIndex(bc, x) {
|
|
189
|
+
bare.writeU32(bc, x);
|
|
190
|
+
}
|
|
191
|
+
function readLoopIterationMarker(bc) {
|
|
192
|
+
return {
|
|
193
|
+
loop: readNameIndex(bc),
|
|
194
|
+
iteration: bare.readU32(bc)
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function writeLoopIterationMarker(bc, x) {
|
|
198
|
+
writeNameIndex(bc, x.loop);
|
|
199
|
+
bare.writeU32(bc, x.iteration);
|
|
200
|
+
}
|
|
201
|
+
function readPathSegment(bc) {
|
|
202
|
+
const offset = bc.offset;
|
|
203
|
+
const tag = bare.readU8(bc);
|
|
204
|
+
switch (tag) {
|
|
205
|
+
case 0:
|
|
206
|
+
return { tag: "NameIndex", val: readNameIndex(bc) };
|
|
207
|
+
case 1:
|
|
208
|
+
return { tag: "LoopIterationMarker", val: readLoopIterationMarker(bc) };
|
|
209
|
+
default: {
|
|
210
|
+
bc.offset = offset;
|
|
211
|
+
throw new bare.BareError(offset, "invalid tag");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function writePathSegment(bc, x) {
|
|
216
|
+
switch (x.tag) {
|
|
217
|
+
case "NameIndex": {
|
|
218
|
+
bare.writeU8(bc, 0);
|
|
219
|
+
writeNameIndex(bc, x.val);
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
case "LoopIterationMarker": {
|
|
223
|
+
bare.writeU8(bc, 1);
|
|
224
|
+
writeLoopIterationMarker(bc, x.val);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function readLocation(bc) {
|
|
230
|
+
const len = bare.readUintSafe(bc);
|
|
231
|
+
if (len === 0) {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
const result = [readPathSegment(bc)];
|
|
235
|
+
for (let i = 1; i < len; i++) {
|
|
236
|
+
result[i] = readPathSegment(bc);
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
function writeLocation(bc, x) {
|
|
241
|
+
bare.writeUintSafe(bc, x.length);
|
|
242
|
+
for (let i = 0; i < x.length; i++) {
|
|
243
|
+
writePathSegment(bc, x[i]);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function readEntryStatus(bc) {
|
|
247
|
+
const offset = bc.offset;
|
|
248
|
+
const tag = bare.readU8(bc);
|
|
249
|
+
switch (tag) {
|
|
250
|
+
case 0:
|
|
251
|
+
return "PENDING" /* PENDING */;
|
|
252
|
+
case 1:
|
|
253
|
+
return "RUNNING" /* RUNNING */;
|
|
254
|
+
case 2:
|
|
255
|
+
return "COMPLETED" /* COMPLETED */;
|
|
256
|
+
case 3:
|
|
257
|
+
return "FAILED" /* FAILED */;
|
|
258
|
+
case 4:
|
|
259
|
+
return "EXHAUSTED" /* EXHAUSTED */;
|
|
260
|
+
default: {
|
|
261
|
+
bc.offset = offset;
|
|
262
|
+
throw new bare.BareError(offset, "invalid tag");
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function writeEntryStatus(bc, x) {
|
|
267
|
+
switch (x) {
|
|
268
|
+
case "PENDING" /* PENDING */: {
|
|
269
|
+
bare.writeU8(bc, 0);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
case "RUNNING" /* RUNNING */: {
|
|
273
|
+
bare.writeU8(bc, 1);
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
case "COMPLETED" /* COMPLETED */: {
|
|
277
|
+
bare.writeU8(bc, 2);
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case "FAILED" /* FAILED */: {
|
|
281
|
+
bare.writeU8(bc, 3);
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
case "EXHAUSTED" /* EXHAUSTED */: {
|
|
285
|
+
bare.writeU8(bc, 4);
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function readSleepState(bc) {
|
|
291
|
+
const offset = bc.offset;
|
|
292
|
+
const tag = bare.readU8(bc);
|
|
293
|
+
switch (tag) {
|
|
294
|
+
case 0:
|
|
295
|
+
return "PENDING" /* PENDING */;
|
|
296
|
+
case 1:
|
|
297
|
+
return "COMPLETED" /* COMPLETED */;
|
|
298
|
+
case 2:
|
|
299
|
+
return "INTERRUPTED" /* INTERRUPTED */;
|
|
300
|
+
default: {
|
|
301
|
+
bc.offset = offset;
|
|
302
|
+
throw new bare.BareError(offset, "invalid tag");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function writeSleepState(bc, x) {
|
|
307
|
+
switch (x) {
|
|
308
|
+
case "PENDING" /* PENDING */: {
|
|
309
|
+
bare.writeU8(bc, 0);
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case "COMPLETED" /* COMPLETED */: {
|
|
313
|
+
bare.writeU8(bc, 1);
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
case "INTERRUPTED" /* INTERRUPTED */: {
|
|
317
|
+
bare.writeU8(bc, 2);
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function readBranchStatusType(bc) {
|
|
323
|
+
const offset = bc.offset;
|
|
324
|
+
const tag = bare.readU8(bc);
|
|
325
|
+
switch (tag) {
|
|
326
|
+
case 0:
|
|
327
|
+
return "PENDING" /* PENDING */;
|
|
328
|
+
case 1:
|
|
329
|
+
return "RUNNING" /* RUNNING */;
|
|
330
|
+
case 2:
|
|
331
|
+
return "COMPLETED" /* COMPLETED */;
|
|
332
|
+
case 3:
|
|
333
|
+
return "FAILED" /* FAILED */;
|
|
334
|
+
case 4:
|
|
335
|
+
return "CANCELLED" /* CANCELLED */;
|
|
336
|
+
default: {
|
|
337
|
+
bc.offset = offset;
|
|
338
|
+
throw new bare.BareError(offset, "invalid tag");
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function writeBranchStatusType(bc, x) {
|
|
343
|
+
switch (x) {
|
|
344
|
+
case "PENDING" /* PENDING */: {
|
|
345
|
+
bare.writeU8(bc, 0);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
case "RUNNING" /* RUNNING */: {
|
|
349
|
+
bare.writeU8(bc, 1);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
case "COMPLETED" /* COMPLETED */: {
|
|
353
|
+
bare.writeU8(bc, 2);
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
case "FAILED" /* FAILED */: {
|
|
357
|
+
bare.writeU8(bc, 3);
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
case "CANCELLED" /* CANCELLED */: {
|
|
361
|
+
bare.writeU8(bc, 4);
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function read0(bc) {
|
|
367
|
+
return bare.readBool(bc) ? readCbor(bc) : null;
|
|
368
|
+
}
|
|
369
|
+
function write0(bc, x) {
|
|
370
|
+
bare.writeBool(bc, x !== null);
|
|
371
|
+
if (x !== null) {
|
|
372
|
+
writeCbor(bc, x);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function read1(bc) {
|
|
376
|
+
return bare.readBool(bc) ? bare.readString(bc) : null;
|
|
377
|
+
}
|
|
378
|
+
function write1(bc, x) {
|
|
379
|
+
bare.writeBool(bc, x !== null);
|
|
380
|
+
if (x !== null) {
|
|
381
|
+
bare.writeString(bc, x);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function readStepEntry(bc) {
|
|
385
|
+
return {
|
|
386
|
+
output: read0(bc),
|
|
387
|
+
error: read1(bc)
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function writeStepEntry(bc, x) {
|
|
391
|
+
write0(bc, x.output);
|
|
392
|
+
write1(bc, x.error);
|
|
393
|
+
}
|
|
394
|
+
function readLoopEntry(bc) {
|
|
395
|
+
return {
|
|
396
|
+
state: readCbor(bc),
|
|
397
|
+
iteration: bare.readU32(bc),
|
|
398
|
+
output: read0(bc)
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function writeLoopEntry(bc, x) {
|
|
402
|
+
writeCbor(bc, x.state);
|
|
403
|
+
bare.writeU32(bc, x.iteration);
|
|
404
|
+
write0(bc, x.output);
|
|
405
|
+
}
|
|
406
|
+
function readSleepEntry(bc) {
|
|
407
|
+
return {
|
|
408
|
+
deadline: bare.readU64(bc),
|
|
409
|
+
state: readSleepState(bc)
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function writeSleepEntry(bc, x) {
|
|
413
|
+
bare.writeU64(bc, x.deadline);
|
|
414
|
+
writeSleepState(bc, x.state);
|
|
415
|
+
}
|
|
416
|
+
function readMessageEntry(bc) {
|
|
417
|
+
return {
|
|
418
|
+
name: bare.readString(bc),
|
|
419
|
+
messageData: readCbor(bc)
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function writeMessageEntry(bc, x) {
|
|
423
|
+
bare.writeString(bc, x.name);
|
|
424
|
+
writeCbor(bc, x.messageData);
|
|
425
|
+
}
|
|
426
|
+
function readRollbackCheckpointEntry(bc) {
|
|
427
|
+
return {
|
|
428
|
+
name: bare.readString(bc)
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function writeRollbackCheckpointEntry(bc, x) {
|
|
432
|
+
bare.writeString(bc, x.name);
|
|
433
|
+
}
|
|
434
|
+
function readBranchStatus(bc) {
|
|
435
|
+
return {
|
|
436
|
+
status: readBranchStatusType(bc),
|
|
437
|
+
output: read0(bc),
|
|
438
|
+
error: read1(bc)
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function writeBranchStatus(bc, x) {
|
|
442
|
+
writeBranchStatusType(bc, x.status);
|
|
443
|
+
write0(bc, x.output);
|
|
444
|
+
write1(bc, x.error);
|
|
445
|
+
}
|
|
446
|
+
function read2(bc) {
|
|
447
|
+
const len = bare.readUintSafe(bc);
|
|
448
|
+
const result = /* @__PURE__ */ new Map();
|
|
449
|
+
for (let i = 0; i < len; i++) {
|
|
450
|
+
const offset = bc.offset;
|
|
451
|
+
const key = bare.readString(bc);
|
|
452
|
+
if (result.has(key)) {
|
|
453
|
+
bc.offset = offset;
|
|
454
|
+
throw new bare.BareError(offset, "duplicated key");
|
|
455
|
+
}
|
|
456
|
+
result.set(key, readBranchStatus(bc));
|
|
457
|
+
}
|
|
458
|
+
return result;
|
|
459
|
+
}
|
|
460
|
+
function write2(bc, x) {
|
|
461
|
+
bare.writeUintSafe(bc, x.size);
|
|
462
|
+
for (const kv of x) {
|
|
463
|
+
bare.writeString(bc, kv[0]);
|
|
464
|
+
writeBranchStatus(bc, kv[1]);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function readJoinEntry(bc) {
|
|
468
|
+
return {
|
|
469
|
+
branches: read2(bc)
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function writeJoinEntry(bc, x) {
|
|
473
|
+
write2(bc, x.branches);
|
|
474
|
+
}
|
|
475
|
+
function readRaceEntry(bc) {
|
|
476
|
+
return {
|
|
477
|
+
winner: read1(bc),
|
|
478
|
+
branches: read2(bc)
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function writeRaceEntry(bc, x) {
|
|
482
|
+
write1(bc, x.winner);
|
|
483
|
+
write2(bc, x.branches);
|
|
484
|
+
}
|
|
485
|
+
function readRemovedEntry(bc) {
|
|
486
|
+
return {
|
|
487
|
+
originalType: bare.readString(bc),
|
|
488
|
+
originalName: read1(bc)
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function writeRemovedEntry(bc, x) {
|
|
492
|
+
bare.writeString(bc, x.originalType);
|
|
493
|
+
write1(bc, x.originalName);
|
|
494
|
+
}
|
|
495
|
+
function readEntryKind(bc) {
|
|
496
|
+
const offset = bc.offset;
|
|
497
|
+
const tag = bare.readU8(bc);
|
|
498
|
+
switch (tag) {
|
|
499
|
+
case 0:
|
|
500
|
+
return { tag: "StepEntry", val: readStepEntry(bc) };
|
|
501
|
+
case 1:
|
|
502
|
+
return { tag: "LoopEntry", val: readLoopEntry(bc) };
|
|
503
|
+
case 2:
|
|
504
|
+
return { tag: "SleepEntry", val: readSleepEntry(bc) };
|
|
505
|
+
case 3:
|
|
506
|
+
return { tag: "MessageEntry", val: readMessageEntry(bc) };
|
|
507
|
+
case 4:
|
|
508
|
+
return { tag: "RollbackCheckpointEntry", val: readRollbackCheckpointEntry(bc) };
|
|
509
|
+
case 5:
|
|
510
|
+
return { tag: "JoinEntry", val: readJoinEntry(bc) };
|
|
511
|
+
case 6:
|
|
512
|
+
return { tag: "RaceEntry", val: readRaceEntry(bc) };
|
|
513
|
+
case 7:
|
|
514
|
+
return { tag: "RemovedEntry", val: readRemovedEntry(bc) };
|
|
515
|
+
default: {
|
|
516
|
+
bc.offset = offset;
|
|
517
|
+
throw new bare.BareError(offset, "invalid tag");
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
function writeEntryKind(bc, x) {
|
|
522
|
+
switch (x.tag) {
|
|
523
|
+
case "StepEntry": {
|
|
524
|
+
bare.writeU8(bc, 0);
|
|
525
|
+
writeStepEntry(bc, x.val);
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
case "LoopEntry": {
|
|
529
|
+
bare.writeU8(bc, 1);
|
|
530
|
+
writeLoopEntry(bc, x.val);
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
case "SleepEntry": {
|
|
534
|
+
bare.writeU8(bc, 2);
|
|
535
|
+
writeSleepEntry(bc, x.val);
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
case "MessageEntry": {
|
|
539
|
+
bare.writeU8(bc, 3);
|
|
540
|
+
writeMessageEntry(bc, x.val);
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
case "RollbackCheckpointEntry": {
|
|
544
|
+
bare.writeU8(bc, 4);
|
|
545
|
+
writeRollbackCheckpointEntry(bc, x.val);
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
case "JoinEntry": {
|
|
549
|
+
bare.writeU8(bc, 5);
|
|
550
|
+
writeJoinEntry(bc, x.val);
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
case "RaceEntry": {
|
|
554
|
+
bare.writeU8(bc, 6);
|
|
555
|
+
writeRaceEntry(bc, x.val);
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
case "RemovedEntry": {
|
|
559
|
+
bare.writeU8(bc, 7);
|
|
560
|
+
writeRemovedEntry(bc, x.val);
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function readEntry(bc) {
|
|
566
|
+
return {
|
|
567
|
+
id: bare.readString(bc),
|
|
568
|
+
location: readLocation(bc),
|
|
569
|
+
kind: readEntryKind(bc)
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function writeEntry(bc, x) {
|
|
573
|
+
bare.writeString(bc, x.id);
|
|
574
|
+
writeLocation(bc, x.location);
|
|
575
|
+
writeEntryKind(bc, x.kind);
|
|
576
|
+
}
|
|
577
|
+
function encodeEntry(x) {
|
|
578
|
+
const bc = new bare.ByteCursor(
|
|
579
|
+
new Uint8Array(config.initialBufferLength),
|
|
580
|
+
config
|
|
581
|
+
);
|
|
582
|
+
writeEntry(bc, x);
|
|
583
|
+
return new Uint8Array(bc.view.buffer, bc.view.byteOffset, bc.offset);
|
|
584
|
+
}
|
|
585
|
+
function decodeEntry(bytes) {
|
|
586
|
+
const bc = new bare.ByteCursor(bytes, config);
|
|
587
|
+
const result = readEntry(bc);
|
|
588
|
+
if (bc.offset < bc.view.byteLength) {
|
|
589
|
+
throw new bare.BareError(bc.offset, "remaining bytes");
|
|
590
|
+
}
|
|
591
|
+
return result;
|
|
592
|
+
}
|
|
593
|
+
function read3(bc) {
|
|
594
|
+
return bare.readBool(bc) ? bare.readU64(bc) : null;
|
|
595
|
+
}
|
|
596
|
+
function write3(bc, x) {
|
|
597
|
+
bare.writeBool(bc, x !== null);
|
|
598
|
+
if (x !== null) {
|
|
599
|
+
bare.writeU64(bc, x);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
function readEntryMetadata(bc) {
|
|
603
|
+
return {
|
|
604
|
+
status: readEntryStatus(bc),
|
|
605
|
+
error: read1(bc),
|
|
606
|
+
attempts: bare.readU32(bc),
|
|
607
|
+
lastAttemptAt: bare.readU64(bc),
|
|
608
|
+
createdAt: bare.readU64(bc),
|
|
609
|
+
completedAt: read3(bc),
|
|
610
|
+
rollbackCompletedAt: read3(bc),
|
|
611
|
+
rollbackError: read1(bc)
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
function writeEntryMetadata(bc, x) {
|
|
615
|
+
writeEntryStatus(bc, x.status);
|
|
616
|
+
write1(bc, x.error);
|
|
617
|
+
bare.writeU32(bc, x.attempts);
|
|
618
|
+
bare.writeU64(bc, x.lastAttemptAt);
|
|
619
|
+
bare.writeU64(bc, x.createdAt);
|
|
620
|
+
write3(bc, x.completedAt);
|
|
621
|
+
write3(bc, x.rollbackCompletedAt);
|
|
622
|
+
write1(bc, x.rollbackError);
|
|
623
|
+
}
|
|
624
|
+
function encodeEntryMetadata(x) {
|
|
625
|
+
const bc = new bare.ByteCursor(
|
|
626
|
+
new Uint8Array(config.initialBufferLength),
|
|
627
|
+
config
|
|
628
|
+
);
|
|
629
|
+
writeEntryMetadata(bc, x);
|
|
630
|
+
return new Uint8Array(bc.view.buffer, bc.view.byteOffset, bc.offset);
|
|
631
|
+
}
|
|
632
|
+
function decodeEntryMetadata(bytes) {
|
|
633
|
+
const bc = new bare.ByteCursor(bytes, config);
|
|
634
|
+
const result = readEntryMetadata(bc);
|
|
635
|
+
if (bc.offset < bc.view.byteLength) {
|
|
636
|
+
throw new bare.BareError(bc.offset, "remaining bytes");
|
|
637
|
+
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
function readWorkflowState(bc) {
|
|
641
|
+
const offset = bc.offset;
|
|
642
|
+
const tag = bare.readU8(bc);
|
|
643
|
+
switch (tag) {
|
|
644
|
+
case 0:
|
|
645
|
+
return "PENDING" /* PENDING */;
|
|
646
|
+
case 1:
|
|
647
|
+
return "RUNNING" /* RUNNING */;
|
|
648
|
+
case 2:
|
|
649
|
+
return "SLEEPING" /* SLEEPING */;
|
|
650
|
+
case 3:
|
|
651
|
+
return "FAILED" /* FAILED */;
|
|
652
|
+
case 4:
|
|
653
|
+
return "COMPLETED" /* COMPLETED */;
|
|
654
|
+
case 5:
|
|
655
|
+
return "ROLLING_BACK" /* ROLLING_BACK */;
|
|
656
|
+
default: {
|
|
657
|
+
bc.offset = offset;
|
|
658
|
+
throw new bare.BareError(offset, "invalid tag");
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
function writeWorkflowState(bc, x) {
|
|
663
|
+
switch (x) {
|
|
664
|
+
case "PENDING" /* PENDING */: {
|
|
665
|
+
bare.writeU8(bc, 0);
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
case "RUNNING" /* RUNNING */: {
|
|
669
|
+
bare.writeU8(bc, 1);
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
case "SLEEPING" /* SLEEPING */: {
|
|
673
|
+
bare.writeU8(bc, 2);
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
case "FAILED" /* FAILED */: {
|
|
677
|
+
bare.writeU8(bc, 3);
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
case "COMPLETED" /* COMPLETED */: {
|
|
681
|
+
bare.writeU8(bc, 4);
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
case "ROLLING_BACK" /* ROLLING_BACK */: {
|
|
685
|
+
bare.writeU8(bc, 5);
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
function readWorkflowMetadata(bc) {
|
|
691
|
+
return {
|
|
692
|
+
state: readWorkflowState(bc),
|
|
693
|
+
output: read0(bc),
|
|
694
|
+
error: read1(bc),
|
|
695
|
+
version: read1(bc)
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
function writeWorkflowMetadata(bc, x) {
|
|
699
|
+
writeWorkflowState(bc, x.state);
|
|
700
|
+
write0(bc, x.output);
|
|
701
|
+
write1(bc, x.error);
|
|
702
|
+
write1(bc, x.version);
|
|
703
|
+
}
|
|
704
|
+
function encodeWorkflowMetadata(x) {
|
|
705
|
+
const bc = new bare.ByteCursor(
|
|
706
|
+
new Uint8Array(config.initialBufferLength),
|
|
707
|
+
config
|
|
708
|
+
);
|
|
709
|
+
writeWorkflowMetadata(bc, x);
|
|
710
|
+
return new Uint8Array(bc.view.buffer, bc.view.byteOffset, bc.offset);
|
|
711
|
+
}
|
|
712
|
+
function decodeWorkflowMetadata(bytes) {
|
|
713
|
+
const bc = new bare.ByteCursor(bytes, config);
|
|
714
|
+
const result = readWorkflowMetadata(bc);
|
|
715
|
+
if (bc.offset < bc.view.byteLength) {
|
|
716
|
+
throw new bare.BareError(bc.offset, "remaining bytes");
|
|
717
|
+
}
|
|
718
|
+
return result;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// schemas/versioned.ts
|
|
722
|
+
var _vbare = require('vbare');
|
|
723
|
+
var CURRENT_VERSION = 1;
|
|
724
|
+
var ENTRY_VERSIONED = _vbare.createVersionedDataHandler.call(void 0, {
|
|
725
|
+
deserializeVersion: (bytes, version) => {
|
|
726
|
+
switch (version) {
|
|
727
|
+
case 1:
|
|
728
|
+
return decodeEntry(bytes);
|
|
729
|
+
default:
|
|
730
|
+
throw new Error(`Unknown Entry version ${version}`);
|
|
731
|
+
}
|
|
732
|
+
},
|
|
733
|
+
serializeVersion: (data, version) => {
|
|
734
|
+
switch (version) {
|
|
735
|
+
case 1:
|
|
736
|
+
return encodeEntry(data);
|
|
737
|
+
default:
|
|
738
|
+
throw new Error(`Unknown Entry version ${version}`);
|
|
739
|
+
}
|
|
740
|
+
},
|
|
741
|
+
deserializeConverters: () => [],
|
|
742
|
+
serializeConverters: () => []
|
|
743
|
+
});
|
|
744
|
+
var ENTRY_METADATA_VERSIONED = _vbare.createVersionedDataHandler.call(void 0, {
|
|
745
|
+
deserializeVersion: (bytes, version) => {
|
|
746
|
+
switch (version) {
|
|
747
|
+
case 1:
|
|
748
|
+
return decodeEntryMetadata(bytes);
|
|
749
|
+
default:
|
|
750
|
+
throw new Error(`Unknown EntryMetadata version ${version}`);
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
serializeVersion: (data, version) => {
|
|
754
|
+
switch (version) {
|
|
755
|
+
case 1:
|
|
756
|
+
return encodeEntryMetadata(data);
|
|
757
|
+
default:
|
|
758
|
+
throw new Error(`Unknown EntryMetadata version ${version}`);
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
deserializeConverters: () => [],
|
|
762
|
+
serializeConverters: () => []
|
|
763
|
+
});
|
|
764
|
+
var WORKFLOW_METADATA_VERSIONED = _vbare.createVersionedDataHandler.call(void 0, {
|
|
765
|
+
deserializeVersion: (bytes, version) => {
|
|
766
|
+
switch (version) {
|
|
767
|
+
case 1:
|
|
768
|
+
return decodeWorkflowMetadata(bytes);
|
|
769
|
+
default:
|
|
770
|
+
throw new Error(
|
|
771
|
+
`Unknown WorkflowMetadata version ${version}`
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
serializeVersion: (data, version) => {
|
|
776
|
+
switch (version) {
|
|
777
|
+
case 1:
|
|
778
|
+
return encodeWorkflowMetadata(
|
|
779
|
+
data
|
|
780
|
+
);
|
|
781
|
+
default:
|
|
782
|
+
throw new Error(
|
|
783
|
+
`Unknown WorkflowMetadata version ${version}`
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
},
|
|
787
|
+
deserializeConverters: () => [],
|
|
788
|
+
serializeConverters: () => []
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
// schemas/serde.ts
|
|
792
|
+
function bufferToArrayBuffer(buf) {
|
|
793
|
+
const arrayBuffer = new ArrayBuffer(buf.byteLength);
|
|
794
|
+
new Uint8Array(arrayBuffer).set(buf);
|
|
795
|
+
return arrayBuffer;
|
|
796
|
+
}
|
|
797
|
+
function encodeCbor(value) {
|
|
798
|
+
return bufferToArrayBuffer(cbor.encode(value));
|
|
799
|
+
}
|
|
800
|
+
function decodeCbor(data) {
|
|
801
|
+
return cbor.decode(new Uint8Array(data));
|
|
802
|
+
}
|
|
803
|
+
function assertObject(value, context) {
|
|
804
|
+
if (typeof value !== "object" || value === null) {
|
|
805
|
+
throw new Error(`${context}: expected object, got ${typeof value}`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function assertString(value, context) {
|
|
809
|
+
if (typeof value !== "string") {
|
|
810
|
+
throw new Error(`${context}: expected string, got ${typeof value}`);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
function entryStatusToBare(status) {
|
|
814
|
+
switch (status) {
|
|
815
|
+
case "pending":
|
|
816
|
+
return "PENDING" /* PENDING */;
|
|
817
|
+
case "running":
|
|
818
|
+
return "RUNNING" /* RUNNING */;
|
|
819
|
+
case "completed":
|
|
820
|
+
return "COMPLETED" /* COMPLETED */;
|
|
821
|
+
case "failed":
|
|
822
|
+
return "FAILED" /* FAILED */;
|
|
823
|
+
case "exhausted":
|
|
824
|
+
return "EXHAUSTED" /* EXHAUSTED */;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
function entryStatusFromBare(status) {
|
|
828
|
+
switch (status) {
|
|
829
|
+
case "PENDING" /* PENDING */:
|
|
830
|
+
return "pending";
|
|
831
|
+
case "RUNNING" /* RUNNING */:
|
|
832
|
+
return "running";
|
|
833
|
+
case "COMPLETED" /* COMPLETED */:
|
|
834
|
+
return "completed";
|
|
835
|
+
case "FAILED" /* FAILED */:
|
|
836
|
+
return "failed";
|
|
837
|
+
case "EXHAUSTED" /* EXHAUSTED */:
|
|
838
|
+
return "exhausted";
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
function sleepStateToBare(state) {
|
|
842
|
+
switch (state) {
|
|
843
|
+
case "pending":
|
|
844
|
+
return "PENDING" /* PENDING */;
|
|
845
|
+
case "completed":
|
|
846
|
+
return "COMPLETED" /* COMPLETED */;
|
|
847
|
+
case "interrupted":
|
|
848
|
+
return "INTERRUPTED" /* INTERRUPTED */;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
function sleepStateFromBare(state) {
|
|
852
|
+
switch (state) {
|
|
853
|
+
case "PENDING" /* PENDING */:
|
|
854
|
+
return "pending";
|
|
855
|
+
case "COMPLETED" /* COMPLETED */:
|
|
856
|
+
return "completed";
|
|
857
|
+
case "INTERRUPTED" /* INTERRUPTED */:
|
|
858
|
+
return "interrupted";
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
function branchStatusTypeToBare(status) {
|
|
862
|
+
switch (status) {
|
|
863
|
+
case "pending":
|
|
864
|
+
return "PENDING" /* PENDING */;
|
|
865
|
+
case "running":
|
|
866
|
+
return "RUNNING" /* RUNNING */;
|
|
867
|
+
case "completed":
|
|
868
|
+
return "COMPLETED" /* COMPLETED */;
|
|
869
|
+
case "failed":
|
|
870
|
+
return "FAILED" /* FAILED */;
|
|
871
|
+
case "cancelled":
|
|
872
|
+
return "CANCELLED" /* CANCELLED */;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function branchStatusTypeFromBare(status) {
|
|
876
|
+
switch (status) {
|
|
877
|
+
case "PENDING" /* PENDING */:
|
|
878
|
+
return "pending";
|
|
879
|
+
case "RUNNING" /* RUNNING */:
|
|
880
|
+
return "running";
|
|
881
|
+
case "COMPLETED" /* COMPLETED */:
|
|
882
|
+
return "completed";
|
|
883
|
+
case "FAILED" /* FAILED */:
|
|
884
|
+
return "failed";
|
|
885
|
+
case "CANCELLED" /* CANCELLED */:
|
|
886
|
+
return "cancelled";
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
function locationToBare(location) {
|
|
890
|
+
return location.map((segment) => {
|
|
891
|
+
if (typeof segment === "number") {
|
|
892
|
+
return { tag: "NameIndex", val: segment };
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
tag: "LoopIterationMarker",
|
|
896
|
+
val: {
|
|
897
|
+
loop: segment.loop,
|
|
898
|
+
iteration: segment.iteration
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
function locationFromBare(location) {
|
|
904
|
+
return location.map((segment) => {
|
|
905
|
+
if (segment.tag === "NameIndex") {
|
|
906
|
+
return segment.val;
|
|
907
|
+
}
|
|
908
|
+
return {
|
|
909
|
+
loop: segment.val.loop,
|
|
910
|
+
iteration: segment.val.iteration
|
|
911
|
+
};
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
function branchStatusToBare(status) {
|
|
915
|
+
return {
|
|
916
|
+
status: branchStatusTypeToBare(status.status),
|
|
917
|
+
output: status.output !== void 0 ? encodeCbor(status.output) : null,
|
|
918
|
+
error: _nullishCoalesce(status.error, () => ( null))
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
function branchStatusFromBare(status) {
|
|
922
|
+
return {
|
|
923
|
+
status: branchStatusTypeFromBare(status.status),
|
|
924
|
+
output: status.output !== null ? decodeCbor(status.output) : void 0,
|
|
925
|
+
error: _nullishCoalesce(status.error, () => ( void 0))
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
function entryKindToBare(kind) {
|
|
929
|
+
switch (kind.type) {
|
|
930
|
+
case "step":
|
|
931
|
+
return {
|
|
932
|
+
tag: "StepEntry",
|
|
933
|
+
val: {
|
|
934
|
+
output: kind.data.output !== void 0 ? encodeCbor(kind.data.output) : null,
|
|
935
|
+
error: _nullishCoalesce(kind.data.error, () => ( null))
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
case "loop":
|
|
939
|
+
return {
|
|
940
|
+
tag: "LoopEntry",
|
|
941
|
+
val: {
|
|
942
|
+
state: encodeCbor(kind.data.state),
|
|
943
|
+
iteration: kind.data.iteration,
|
|
944
|
+
output: kind.data.output !== void 0 ? encodeCbor(kind.data.output) : null
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
case "sleep":
|
|
948
|
+
return {
|
|
949
|
+
tag: "SleepEntry",
|
|
950
|
+
val: {
|
|
951
|
+
deadline: BigInt(kind.data.deadline),
|
|
952
|
+
state: sleepStateToBare(kind.data.state)
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
case "message":
|
|
956
|
+
return {
|
|
957
|
+
tag: "MessageEntry",
|
|
958
|
+
val: {
|
|
959
|
+
name: kind.data.name,
|
|
960
|
+
messageData: encodeCbor(kind.data.data)
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
case "rollback_checkpoint":
|
|
964
|
+
return {
|
|
965
|
+
tag: "RollbackCheckpointEntry",
|
|
966
|
+
val: {
|
|
967
|
+
name: kind.data.name
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
case "join":
|
|
971
|
+
return {
|
|
972
|
+
tag: "JoinEntry",
|
|
973
|
+
val: {
|
|
974
|
+
branches: new Map(
|
|
975
|
+
Object.entries(kind.data.branches).map(
|
|
976
|
+
([name, status]) => [
|
|
977
|
+
name,
|
|
978
|
+
branchStatusToBare(status)
|
|
979
|
+
]
|
|
980
|
+
)
|
|
981
|
+
)
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
case "race":
|
|
985
|
+
return {
|
|
986
|
+
tag: "RaceEntry",
|
|
987
|
+
val: {
|
|
988
|
+
winner: kind.data.winner,
|
|
989
|
+
branches: new Map(
|
|
990
|
+
Object.entries(kind.data.branches).map(
|
|
991
|
+
([name, status]) => [
|
|
992
|
+
name,
|
|
993
|
+
branchStatusToBare(status)
|
|
994
|
+
]
|
|
995
|
+
)
|
|
996
|
+
)
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
case "removed":
|
|
1000
|
+
return {
|
|
1001
|
+
tag: "RemovedEntry",
|
|
1002
|
+
val: {
|
|
1003
|
+
originalType: kind.data.originalType,
|
|
1004
|
+
originalName: _nullishCoalesce(kind.data.originalName, () => ( null))
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
function entryKindFromBare(kind) {
|
|
1010
|
+
switch (kind.tag) {
|
|
1011
|
+
case "StepEntry":
|
|
1012
|
+
return {
|
|
1013
|
+
type: "step",
|
|
1014
|
+
data: {
|
|
1015
|
+
output: kind.val.output !== null ? decodeCbor(kind.val.output) : void 0,
|
|
1016
|
+
error: _nullishCoalesce(kind.val.error, () => ( void 0))
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
case "LoopEntry":
|
|
1020
|
+
return {
|
|
1021
|
+
type: "loop",
|
|
1022
|
+
data: {
|
|
1023
|
+
state: decodeCbor(kind.val.state),
|
|
1024
|
+
iteration: kind.val.iteration,
|
|
1025
|
+
output: kind.val.output !== null ? decodeCbor(kind.val.output) : void 0
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
case "SleepEntry":
|
|
1029
|
+
return {
|
|
1030
|
+
type: "sleep",
|
|
1031
|
+
data: {
|
|
1032
|
+
deadline: Number(kind.val.deadline),
|
|
1033
|
+
state: sleepStateFromBare(kind.val.state)
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
case "MessageEntry":
|
|
1037
|
+
return {
|
|
1038
|
+
type: "message",
|
|
1039
|
+
data: {
|
|
1040
|
+
name: kind.val.name,
|
|
1041
|
+
data: decodeCbor(kind.val.messageData)
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
case "RollbackCheckpointEntry":
|
|
1045
|
+
return {
|
|
1046
|
+
type: "rollback_checkpoint",
|
|
1047
|
+
data: {
|
|
1048
|
+
name: kind.val.name
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
case "JoinEntry":
|
|
1052
|
+
return {
|
|
1053
|
+
type: "join",
|
|
1054
|
+
data: {
|
|
1055
|
+
branches: Object.fromEntries(
|
|
1056
|
+
Array.from(kind.val.branches.entries()).map(
|
|
1057
|
+
([name, status]) => [
|
|
1058
|
+
name,
|
|
1059
|
+
branchStatusFromBare(status)
|
|
1060
|
+
]
|
|
1061
|
+
)
|
|
1062
|
+
)
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
case "RaceEntry":
|
|
1066
|
+
return {
|
|
1067
|
+
type: "race",
|
|
1068
|
+
data: {
|
|
1069
|
+
winner: kind.val.winner,
|
|
1070
|
+
branches: Object.fromEntries(
|
|
1071
|
+
Array.from(kind.val.branches.entries()).map(
|
|
1072
|
+
([name, status]) => [
|
|
1073
|
+
name,
|
|
1074
|
+
branchStatusFromBare(status)
|
|
1075
|
+
]
|
|
1076
|
+
)
|
|
1077
|
+
)
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
case "RemovedEntry":
|
|
1081
|
+
return {
|
|
1082
|
+
type: "removed",
|
|
1083
|
+
data: {
|
|
1084
|
+
originalType: kind.val.originalType,
|
|
1085
|
+
originalName: _nullishCoalesce(kind.val.originalName, () => ( void 0))
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
default:
|
|
1089
|
+
throw new Error(
|
|
1090
|
+
`Unknown entry kind: ${kind.tag}`
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
function entryToBare(entry) {
|
|
1095
|
+
return {
|
|
1096
|
+
id: entry.id,
|
|
1097
|
+
location: locationToBare(entry.location),
|
|
1098
|
+
kind: entryKindToBare(entry.kind)
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
function entryFromBare(bareEntry) {
|
|
1102
|
+
return {
|
|
1103
|
+
id: bareEntry.id,
|
|
1104
|
+
location: locationFromBare(bareEntry.location),
|
|
1105
|
+
kind: entryKindFromBare(bareEntry.kind),
|
|
1106
|
+
dirty: false
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
function serializeEntry(entry) {
|
|
1110
|
+
const bareEntry = entryToBare(entry);
|
|
1111
|
+
return ENTRY_VERSIONED.serializeWithEmbeddedVersion(
|
|
1112
|
+
bareEntry,
|
|
1113
|
+
CURRENT_VERSION
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
function deserializeEntry(bytes) {
|
|
1117
|
+
const bareEntry = ENTRY_VERSIONED.deserializeWithEmbeddedVersion(bytes);
|
|
1118
|
+
return entryFromBare(bareEntry);
|
|
1119
|
+
}
|
|
1120
|
+
function entryMetadataToBare(metadata) {
|
|
1121
|
+
return {
|
|
1122
|
+
status: entryStatusToBare(metadata.status),
|
|
1123
|
+
error: _nullishCoalesce(metadata.error, () => ( null)),
|
|
1124
|
+
attempts: metadata.attempts,
|
|
1125
|
+
lastAttemptAt: BigInt(metadata.lastAttemptAt),
|
|
1126
|
+
createdAt: BigInt(metadata.createdAt),
|
|
1127
|
+
completedAt: metadata.completedAt !== void 0 ? BigInt(metadata.completedAt) : null,
|
|
1128
|
+
rollbackCompletedAt: metadata.rollbackCompletedAt !== void 0 ? BigInt(metadata.rollbackCompletedAt) : null,
|
|
1129
|
+
rollbackError: _nullishCoalesce(metadata.rollbackError, () => ( null))
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
function entryMetadataFromBare(bareMetadata) {
|
|
1133
|
+
return {
|
|
1134
|
+
status: entryStatusFromBare(bareMetadata.status),
|
|
1135
|
+
error: _nullishCoalesce(bareMetadata.error, () => ( void 0)),
|
|
1136
|
+
attempts: bareMetadata.attempts,
|
|
1137
|
+
lastAttemptAt: Number(bareMetadata.lastAttemptAt),
|
|
1138
|
+
createdAt: Number(bareMetadata.createdAt),
|
|
1139
|
+
completedAt: bareMetadata.completedAt !== null ? Number(bareMetadata.completedAt) : void 0,
|
|
1140
|
+
rollbackCompletedAt: bareMetadata.rollbackCompletedAt !== null ? Number(bareMetadata.rollbackCompletedAt) : void 0,
|
|
1141
|
+
rollbackError: _nullishCoalesce(bareMetadata.rollbackError, () => ( void 0)),
|
|
1142
|
+
dirty: false
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
function serializeEntryMetadata(metadata) {
|
|
1146
|
+
const bareMetadata = entryMetadataToBare(metadata);
|
|
1147
|
+
return ENTRY_METADATA_VERSIONED.serializeWithEmbeddedVersion(
|
|
1148
|
+
bareMetadata,
|
|
1149
|
+
CURRENT_VERSION
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
function deserializeEntryMetadata(bytes) {
|
|
1153
|
+
const bareMetadata = ENTRY_METADATA_VERSIONED.deserializeWithEmbeddedVersion(bytes);
|
|
1154
|
+
return entryMetadataFromBare(bareMetadata);
|
|
1155
|
+
}
|
|
1156
|
+
function serializeWorkflowState(state) {
|
|
1157
|
+
const encoder = new TextEncoder();
|
|
1158
|
+
return encoder.encode(state);
|
|
1159
|
+
}
|
|
1160
|
+
function deserializeWorkflowState(bytes) {
|
|
1161
|
+
const decoder = new TextDecoder();
|
|
1162
|
+
const state = decoder.decode(bytes);
|
|
1163
|
+
const validStates = [
|
|
1164
|
+
"pending",
|
|
1165
|
+
"running",
|
|
1166
|
+
"sleeping",
|
|
1167
|
+
"failed",
|
|
1168
|
+
"completed",
|
|
1169
|
+
"cancelled",
|
|
1170
|
+
"rolling_back"
|
|
1171
|
+
];
|
|
1172
|
+
if (!validStates.includes(state)) {
|
|
1173
|
+
throw new Error(`Invalid workflow state: ${state}`);
|
|
1174
|
+
}
|
|
1175
|
+
return state;
|
|
1176
|
+
}
|
|
1177
|
+
function serializeWorkflowOutput(output) {
|
|
1178
|
+
return cbor.encode(output);
|
|
1179
|
+
}
|
|
1180
|
+
function deserializeWorkflowOutput(bytes) {
|
|
1181
|
+
try {
|
|
1182
|
+
return cbor.decode(bytes);
|
|
1183
|
+
} catch (error) {
|
|
1184
|
+
throw new Error(
|
|
1185
|
+
`Failed to deserialize workflow output: ${error instanceof Error ? error.message : String(error)}`
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
function serializeWorkflowError(error) {
|
|
1190
|
+
return cbor.encode(error);
|
|
1191
|
+
}
|
|
1192
|
+
function deserializeWorkflowError(bytes) {
|
|
1193
|
+
const decoded = cbor.decode(bytes);
|
|
1194
|
+
assertObject(decoded, "WorkflowError");
|
|
1195
|
+
const obj = decoded;
|
|
1196
|
+
assertString(obj.name, "WorkflowError.name");
|
|
1197
|
+
assertString(obj.message, "WorkflowError.message");
|
|
1198
|
+
return {
|
|
1199
|
+
name: obj.name,
|
|
1200
|
+
message: obj.message,
|
|
1201
|
+
stack: typeof obj.stack === "string" ? obj.stack : void 0,
|
|
1202
|
+
metadata: typeof obj.metadata === "object" && obj.metadata !== null ? obj.metadata : void 0
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
function serializeWorkflowInput(input) {
|
|
1206
|
+
return cbor.encode(input);
|
|
1207
|
+
}
|
|
1208
|
+
function deserializeWorkflowInput(bytes) {
|
|
1209
|
+
try {
|
|
1210
|
+
return cbor.decode(bytes);
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
throw new Error(
|
|
1213
|
+
`Failed to deserialize workflow input: ${error instanceof Error ? error.message : String(error)}`
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
function serializeName(name) {
|
|
1218
|
+
const encoder = new TextEncoder();
|
|
1219
|
+
return encoder.encode(name);
|
|
1220
|
+
}
|
|
1221
|
+
function deserializeName(bytes) {
|
|
1222
|
+
const decoder = new TextDecoder();
|
|
1223
|
+
return decoder.decode(bytes);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// src/keys.ts
|
|
1227
|
+
var _fdbtuple = require('fdb-tuple'); var tuple = _interopRequireWildcard(_fdbtuple);
|
|
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
|
+
// src/storage.ts
|
|
1332
|
+
function createStorage() {
|
|
1333
|
+
return {
|
|
1334
|
+
nameRegistry: [],
|
|
1335
|
+
flushedNameCount: 0,
|
|
1336
|
+
history: { entries: /* @__PURE__ */ new Map() },
|
|
1337
|
+
entryMetadata: /* @__PURE__ */ new Map(),
|
|
1338
|
+
output: void 0,
|
|
1339
|
+
state: "pending",
|
|
1340
|
+
flushedState: void 0,
|
|
1341
|
+
error: void 0,
|
|
1342
|
+
flushedError: void 0,
|
|
1343
|
+
flushedOutput: void 0
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
function createHistorySnapshot(storage) {
|
|
1347
|
+
const entryMetadata = /* @__PURE__ */ new Map();
|
|
1348
|
+
for (const [id, metadata] of storage.entryMetadata) {
|
|
1349
|
+
const { dirty, ...rest } = metadata;
|
|
1350
|
+
entryMetadata.set(id, rest);
|
|
1351
|
+
}
|
|
1352
|
+
const entries = [];
|
|
1353
|
+
const entryKeys = Array.from(storage.history.entries.keys()).sort();
|
|
1354
|
+
for (const key of entryKeys) {
|
|
1355
|
+
const entry = storage.history.entries.get(key);
|
|
1356
|
+
if (!entry) continue;
|
|
1357
|
+
const { dirty, ...rest } = entry;
|
|
1358
|
+
entries.push(rest);
|
|
1359
|
+
}
|
|
1360
|
+
return {
|
|
1361
|
+
nameRegistry: [...storage.nameRegistry],
|
|
1362
|
+
entries,
|
|
1363
|
+
entryMetadata
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
function generateId() {
|
|
1367
|
+
return crypto.randomUUID();
|
|
1368
|
+
}
|
|
1369
|
+
function createEntry(location, kind) {
|
|
1370
|
+
return {
|
|
1371
|
+
id: generateId(),
|
|
1372
|
+
location,
|
|
1373
|
+
kind,
|
|
1374
|
+
dirty: true
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
function getOrCreateMetadata(storage, entryId) {
|
|
1378
|
+
let metadata = storage.entryMetadata.get(entryId);
|
|
1379
|
+
if (!metadata) {
|
|
1380
|
+
metadata = {
|
|
1381
|
+
status: "pending",
|
|
1382
|
+
attempts: 0,
|
|
1383
|
+
lastAttemptAt: 0,
|
|
1384
|
+
createdAt: Date.now(),
|
|
1385
|
+
rollbackCompletedAt: void 0,
|
|
1386
|
+
rollbackError: void 0,
|
|
1387
|
+
dirty: true
|
|
1388
|
+
};
|
|
1389
|
+
storage.entryMetadata.set(entryId, metadata);
|
|
1390
|
+
}
|
|
1391
|
+
return metadata;
|
|
1392
|
+
}
|
|
1393
|
+
async function loadStorage(driver) {
|
|
1394
|
+
const storage = createStorage();
|
|
1395
|
+
const nameEntries = await driver.list(buildNamePrefix());
|
|
1396
|
+
nameEntries.sort((a, b) => compareKeys(a.key, b.key));
|
|
1397
|
+
for (const entry of nameEntries) {
|
|
1398
|
+
const index = parseNameKey(entry.key);
|
|
1399
|
+
storage.nameRegistry[index] = deserializeName(entry.value);
|
|
1400
|
+
}
|
|
1401
|
+
storage.flushedNameCount = storage.nameRegistry.length;
|
|
1402
|
+
const historyEntries = await driver.list(buildHistoryPrefixAll());
|
|
1403
|
+
for (const entry of historyEntries) {
|
|
1404
|
+
const parsed = deserializeEntry(entry.value);
|
|
1405
|
+
parsed.dirty = false;
|
|
1406
|
+
const key = locationToKey(storage, parsed.location);
|
|
1407
|
+
storage.history.entries.set(key, parsed);
|
|
1408
|
+
}
|
|
1409
|
+
const stateValue = await driver.get(buildWorkflowStateKey());
|
|
1410
|
+
if (stateValue) {
|
|
1411
|
+
storage.state = deserializeWorkflowState(stateValue);
|
|
1412
|
+
storage.flushedState = storage.state;
|
|
1413
|
+
}
|
|
1414
|
+
const outputValue = await driver.get(buildWorkflowOutputKey());
|
|
1415
|
+
if (outputValue) {
|
|
1416
|
+
storage.output = deserializeWorkflowOutput(outputValue);
|
|
1417
|
+
storage.flushedOutput = storage.output;
|
|
1418
|
+
}
|
|
1419
|
+
const errorValue = await driver.get(buildWorkflowErrorKey());
|
|
1420
|
+
if (errorValue) {
|
|
1421
|
+
storage.error = deserializeWorkflowError(errorValue);
|
|
1422
|
+
storage.flushedError = storage.error;
|
|
1423
|
+
}
|
|
1424
|
+
return storage;
|
|
1425
|
+
}
|
|
1426
|
+
async function loadMetadata(storage, driver, entryId) {
|
|
1427
|
+
const existing = storage.entryMetadata.get(entryId);
|
|
1428
|
+
if (existing) {
|
|
1429
|
+
return existing;
|
|
1430
|
+
}
|
|
1431
|
+
const value = await driver.get(buildEntryMetadataKey(entryId));
|
|
1432
|
+
if (value) {
|
|
1433
|
+
const metadata = deserializeEntryMetadata(value);
|
|
1434
|
+
metadata.dirty = false;
|
|
1435
|
+
storage.entryMetadata.set(entryId, metadata);
|
|
1436
|
+
return metadata;
|
|
1437
|
+
}
|
|
1438
|
+
return getOrCreateMetadata(storage, entryId);
|
|
1439
|
+
}
|
|
1440
|
+
async function flush(storage, driver, onHistoryUpdated) {
|
|
1441
|
+
const writes = [];
|
|
1442
|
+
let historyUpdated = false;
|
|
1443
|
+
for (let i = storage.flushedNameCount; i < storage.nameRegistry.length; i++) {
|
|
1444
|
+
const name = storage.nameRegistry[i];
|
|
1445
|
+
if (name !== void 0) {
|
|
1446
|
+
writes.push({
|
|
1447
|
+
key: buildNameKey(i),
|
|
1448
|
+
value: serializeName(name)
|
|
1449
|
+
});
|
|
1450
|
+
historyUpdated = true;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
for (const [, entry] of storage.history.entries) {
|
|
1454
|
+
if (entry.dirty) {
|
|
1455
|
+
writes.push({
|
|
1456
|
+
key: buildHistoryKey(entry.location),
|
|
1457
|
+
value: serializeEntry(entry)
|
|
1458
|
+
});
|
|
1459
|
+
entry.dirty = false;
|
|
1460
|
+
historyUpdated = true;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
for (const [id, metadata] of storage.entryMetadata) {
|
|
1464
|
+
if (metadata.dirty) {
|
|
1465
|
+
writes.push({
|
|
1466
|
+
key: buildEntryMetadataKey(id),
|
|
1467
|
+
value: serializeEntryMetadata(metadata)
|
|
1468
|
+
});
|
|
1469
|
+
metadata.dirty = false;
|
|
1470
|
+
historyUpdated = true;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
if (storage.state !== storage.flushedState) {
|
|
1474
|
+
writes.push({
|
|
1475
|
+
key: buildWorkflowStateKey(),
|
|
1476
|
+
value: serializeWorkflowState(storage.state)
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
if (storage.output !== void 0 && storage.output !== storage.flushedOutput) {
|
|
1480
|
+
writes.push({
|
|
1481
|
+
key: buildWorkflowOutputKey(),
|
|
1482
|
+
value: serializeWorkflowOutput(storage.output)
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
const errorChanged = storage.error !== void 0 && (storage.flushedError === void 0 || storage.error.name !== storage.flushedError.name || storage.error.message !== storage.flushedError.message);
|
|
1486
|
+
if (errorChanged) {
|
|
1487
|
+
writes.push({
|
|
1488
|
+
key: buildWorkflowErrorKey(),
|
|
1489
|
+
value: serializeWorkflowError(storage.error)
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
if (writes.length > 0) {
|
|
1493
|
+
await driver.batch(writes);
|
|
1494
|
+
}
|
|
1495
|
+
storage.flushedNameCount = storage.nameRegistry.length;
|
|
1496
|
+
storage.flushedState = storage.state;
|
|
1497
|
+
storage.flushedOutput = storage.output;
|
|
1498
|
+
storage.flushedError = storage.error;
|
|
1499
|
+
if (historyUpdated && onHistoryUpdated) {
|
|
1500
|
+
onHistoryUpdated();
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
async function deleteEntriesWithPrefix(storage, driver, prefixLocation, onHistoryUpdated) {
|
|
1504
|
+
const entryIds = [];
|
|
1505
|
+
for (const [key, entry] of storage.history.entries) {
|
|
1506
|
+
if (isLocationPrefix(prefixLocation, entry.location)) {
|
|
1507
|
+
entryIds.push(entry.id);
|
|
1508
|
+
storage.entryMetadata.delete(entry.id);
|
|
1509
|
+
storage.history.entries.delete(key);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
await driver.deletePrefix(buildHistoryPrefix(prefixLocation));
|
|
1513
|
+
await Promise.all(
|
|
1514
|
+
entryIds.map((id) => driver.delete(buildEntryMetadataKey(id)))
|
|
1515
|
+
);
|
|
1516
|
+
if (entryIds.length > 0 && onHistoryUpdated) {
|
|
1517
|
+
onHistoryUpdated();
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
function getEntry(storage, location) {
|
|
1521
|
+
const key = locationToKey(storage, location);
|
|
1522
|
+
return storage.history.entries.get(key);
|
|
1523
|
+
}
|
|
1524
|
+
function setEntry(storage, location, entry) {
|
|
1525
|
+
const key = locationToKey(storage, location);
|
|
1526
|
+
storage.history.entries.set(key, entry);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
// src/utils.ts
|
|
1530
|
+
function sleep(ms) {
|
|
1531
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1532
|
+
}
|
|
1533
|
+
var TIMEOUT_MAX = 2147483647;
|
|
1534
|
+
function setLongTimeout(listener, after) {
|
|
1535
|
+
let timeout;
|
|
1536
|
+
function start(remaining) {
|
|
1537
|
+
if (remaining <= TIMEOUT_MAX) {
|
|
1538
|
+
timeout = setTimeout(listener, remaining);
|
|
1539
|
+
} else {
|
|
1540
|
+
timeout = setTimeout(() => {
|
|
1541
|
+
start(remaining - TIMEOUT_MAX);
|
|
1542
|
+
}, TIMEOUT_MAX);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
start(after);
|
|
1546
|
+
return {
|
|
1547
|
+
abort: () => {
|
|
1548
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
// src/context.ts
|
|
1554
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
1555
|
+
var DEFAULT_RETRY_BACKOFF_BASE = 100;
|
|
1556
|
+
var DEFAULT_RETRY_BACKOFF_MAX = 3e4;
|
|
1557
|
+
var DEFAULT_LOOP_COMMIT_INTERVAL = 20;
|
|
1558
|
+
var DEFAULT_LOOP_HISTORY_EVERY = 20;
|
|
1559
|
+
var DEFAULT_LOOP_HISTORY_KEEP = 20;
|
|
1560
|
+
var DEFAULT_STEP_TIMEOUT = 3e4;
|
|
1561
|
+
var QUEUE_HISTORY_MESSAGE_MARKER = "__rivetWorkflowQueueMessage";
|
|
1562
|
+
function calculateBackoff(attempts, base, max) {
|
|
1563
|
+
return Math.min(max, base * 2 ** attempts);
|
|
1564
|
+
}
|
|
1565
|
+
var StepTimeoutError = class extends Error {
|
|
1566
|
+
constructor(stepName, timeoutMs) {
|
|
1567
|
+
super(`Step "${stepName}" timed out after ${timeoutMs}ms`);
|
|
1568
|
+
this.stepName = stepName;
|
|
1569
|
+
this.timeoutMs = timeoutMs;
|
|
1570
|
+
this.name = "StepTimeoutError";
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
var WorkflowContextImpl = (_class = class _WorkflowContextImpl {
|
|
1574
|
+
constructor(workflowId, storage, driver, messageDriver, location = emptyLocation(), abortController, mode = "forward", rollbackActions, rollbackCheckpointSet = false, historyNotifier, logger) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
|
|
1575
|
+
this.workflowId = workflowId;
|
|
1576
|
+
this.storage = storage;
|
|
1577
|
+
this.driver = driver;
|
|
1578
|
+
this.messageDriver = messageDriver;
|
|
1579
|
+
this.currentLocation = location;
|
|
1580
|
+
this.abortController = _nullishCoalesce(abortController, () => ( new AbortController()));
|
|
1581
|
+
this.mode = mode;
|
|
1582
|
+
this.rollbackActions = rollbackActions;
|
|
1583
|
+
this.rollbackCheckpointSet = rollbackCheckpointSet;
|
|
1584
|
+
this.historyNotifier = historyNotifier;
|
|
1585
|
+
this.logger = logger;
|
|
1586
|
+
}
|
|
1587
|
+
__init() {this.entryInProgress = false}
|
|
1588
|
+
|
|
1589
|
+
|
|
1590
|
+
__init2() {this.visitedKeys = /* @__PURE__ */ new Set()}
|
|
1591
|
+
|
|
1592
|
+
|
|
1593
|
+
|
|
1594
|
+
/** Track names used in current execution to detect duplicates */
|
|
1595
|
+
__init3() {this.usedNamesInExecution = /* @__PURE__ */ new Set()}
|
|
1596
|
+
__init4() {this.pendingCompletableMessageIds = /* @__PURE__ */ new Set()}
|
|
1597
|
+
|
|
1598
|
+
|
|
1599
|
+
get abortSignal() {
|
|
1600
|
+
return this.abortController.signal;
|
|
1601
|
+
}
|
|
1602
|
+
get queue() {
|
|
1603
|
+
return {
|
|
1604
|
+
next: async (name, opts) => await this.queueNext(name, opts),
|
|
1605
|
+
send: async (name, body) => await this.queueSend(name, body)
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
isEvicted() {
|
|
1609
|
+
return this.abortSignal.aborted;
|
|
1610
|
+
}
|
|
1611
|
+
assertNotInProgress() {
|
|
1612
|
+
if (this.entryInProgress) {
|
|
1613
|
+
throw new EntryInProgressError();
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
checkEvicted() {
|
|
1617
|
+
if (this.abortSignal.aborted) {
|
|
1618
|
+
throw new EvictedError();
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
async flushStorage() {
|
|
1622
|
+
await flush(this.storage, this.driver, this.historyNotifier);
|
|
1623
|
+
}
|
|
1624
|
+
/**
|
|
1625
|
+
* Create a new branch context for parallel/nested execution.
|
|
1626
|
+
*/
|
|
1627
|
+
createBranch(location, abortController) {
|
|
1628
|
+
return new _WorkflowContextImpl(
|
|
1629
|
+
this.workflowId,
|
|
1630
|
+
this.storage,
|
|
1631
|
+
this.driver,
|
|
1632
|
+
this.messageDriver,
|
|
1633
|
+
location,
|
|
1634
|
+
_nullishCoalesce(abortController, () => ( this.abortController)),
|
|
1635
|
+
this.mode,
|
|
1636
|
+
this.rollbackActions,
|
|
1637
|
+
this.rollbackCheckpointSet,
|
|
1638
|
+
this.historyNotifier,
|
|
1639
|
+
this.logger
|
|
1640
|
+
);
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Log a debug message using the configured logger.
|
|
1644
|
+
*/
|
|
1645
|
+
log(level, data) {
|
|
1646
|
+
if (!this.logger) return;
|
|
1647
|
+
this.logger[level](data);
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Mark a key as visited.
|
|
1651
|
+
*/
|
|
1652
|
+
markVisited(key) {
|
|
1653
|
+
this.visitedKeys.add(key);
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Check if a name has already been used at the current location in this execution.
|
|
1657
|
+
* Throws HistoryDivergedError if duplicate detected.
|
|
1658
|
+
*/
|
|
1659
|
+
checkDuplicateName(name) {
|
|
1660
|
+
const fullKey = locationToKey(this.storage, this.currentLocation) + "/" + name;
|
|
1661
|
+
if (this.usedNamesInExecution.has(fullKey)) {
|
|
1662
|
+
throw new HistoryDivergedError(
|
|
1663
|
+
`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.`
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
this.usedNamesInExecution.add(fullKey);
|
|
1667
|
+
}
|
|
1668
|
+
stopRollback() {
|
|
1669
|
+
throw new RollbackStopError();
|
|
1670
|
+
}
|
|
1671
|
+
stopRollbackIfMissing(entry) {
|
|
1672
|
+
if (this.mode === "rollback" && !entry) {
|
|
1673
|
+
this.stopRollback();
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
stopRollbackIfIncomplete(condition) {
|
|
1677
|
+
if (this.mode === "rollback" && condition) {
|
|
1678
|
+
this.stopRollback();
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
registerRollbackAction(config2, entryId, output, metadata) {
|
|
1682
|
+
var _a;
|
|
1683
|
+
if (!config2.rollback) {
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
if (metadata.rollbackCompletedAt !== void 0) {
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
(_a = this.rollbackActions) == null ? void 0 : _a.push({
|
|
1690
|
+
entryId,
|
|
1691
|
+
name: config2.name,
|
|
1692
|
+
output,
|
|
1693
|
+
rollback: config2.rollback
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Ensure a rollback checkpoint exists before registering rollback handlers.
|
|
1698
|
+
*/
|
|
1699
|
+
ensureRollbackCheckpoint(config2) {
|
|
1700
|
+
if (!config2.rollback) {
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
if (!this.rollbackCheckpointSet) {
|
|
1704
|
+
throw new RollbackCheckpointError();
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
/**
|
|
1708
|
+
* Validate that all expected entries in the branch were visited.
|
|
1709
|
+
* Throws HistoryDivergedError if there are unvisited entries.
|
|
1710
|
+
*/
|
|
1711
|
+
validateComplete() {
|
|
1712
|
+
const prefix = locationToKey(this.storage, this.currentLocation);
|
|
1713
|
+
for (const key of this.storage.history.entries.keys()) {
|
|
1714
|
+
const isUnderPrefix = prefix === "" ? true : key.startsWith(prefix + "/") || key === prefix;
|
|
1715
|
+
if (isUnderPrefix) {
|
|
1716
|
+
if (!this.visitedKeys.has(key)) {
|
|
1717
|
+
throw new HistoryDivergedError(
|
|
1718
|
+
`Entry "${key}" exists in history but was not visited. Workflow code may have changed. Use ctx.removed() to handle migrations.`
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Evict the workflow.
|
|
1726
|
+
*/
|
|
1727
|
+
evict() {
|
|
1728
|
+
this.abortController.abort(new EvictedError());
|
|
1729
|
+
}
|
|
1730
|
+
/**
|
|
1731
|
+
* Wait for eviction message.
|
|
1732
|
+
*
|
|
1733
|
+
* The event listener uses { once: true } to auto-remove after firing,
|
|
1734
|
+
* preventing memory leaks if this method is called multiple times.
|
|
1735
|
+
*/
|
|
1736
|
+
waitForEviction() {
|
|
1737
|
+
return new Promise((_, reject) => {
|
|
1738
|
+
if (this.abortSignal.aborted) {
|
|
1739
|
+
reject(new EvictedError());
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
this.abortSignal.addEventListener(
|
|
1743
|
+
"abort",
|
|
1744
|
+
() => {
|
|
1745
|
+
reject(new EvictedError());
|
|
1746
|
+
},
|
|
1747
|
+
{ once: true }
|
|
1748
|
+
);
|
|
1749
|
+
});
|
|
1750
|
+
}
|
|
1751
|
+
// === Step ===
|
|
1752
|
+
async step(nameOrConfig, run) {
|
|
1753
|
+
this.assertNotInProgress();
|
|
1754
|
+
this.checkEvicted();
|
|
1755
|
+
const config2 = typeof nameOrConfig === "string" ? { name: nameOrConfig, run } : nameOrConfig;
|
|
1756
|
+
this.entryInProgress = true;
|
|
1757
|
+
try {
|
|
1758
|
+
return await this.executeStep(config2);
|
|
1759
|
+
} finally {
|
|
1760
|
+
this.entryInProgress = false;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
async executeStep(config2) {
|
|
1764
|
+
this.ensureRollbackCheckpoint(config2);
|
|
1765
|
+
if (this.mode === "rollback") {
|
|
1766
|
+
return await this.executeStepRollback(config2);
|
|
1767
|
+
}
|
|
1768
|
+
this.checkDuplicateName(config2.name);
|
|
1769
|
+
const location = appendName(
|
|
1770
|
+
this.storage,
|
|
1771
|
+
this.currentLocation,
|
|
1772
|
+
config2.name
|
|
1773
|
+
);
|
|
1774
|
+
const key = locationToKey(this.storage, location);
|
|
1775
|
+
const existing = this.storage.history.entries.get(key);
|
|
1776
|
+
this.markVisited(key);
|
|
1777
|
+
if (existing) {
|
|
1778
|
+
if (existing.kind.type !== "step") {
|
|
1779
|
+
throw new HistoryDivergedError(
|
|
1780
|
+
`Expected step "${config2.name}" at ${key}, found ${existing.kind.type}`
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
const stepData = existing.kind.data;
|
|
1784
|
+
if (stepData.output !== void 0) {
|
|
1785
|
+
this.log("debug", { msg: "replaying step from history", step: config2.name, key });
|
|
1786
|
+
return stepData.output;
|
|
1787
|
+
}
|
|
1788
|
+
const metadata2 = await loadMetadata(
|
|
1789
|
+
this.storage,
|
|
1790
|
+
this.driver,
|
|
1791
|
+
existing.id
|
|
1792
|
+
);
|
|
1793
|
+
const maxRetries = _nullishCoalesce(config2.maxRetries, () => ( DEFAULT_MAX_RETRIES));
|
|
1794
|
+
if (metadata2.attempts >= maxRetries) {
|
|
1795
|
+
const lastError = _nullishCoalesce(stepData.error, () => ( metadata2.error));
|
|
1796
|
+
throw new StepExhaustedError(config2.name, lastError);
|
|
1797
|
+
}
|
|
1798
|
+
const backoffDelay = calculateBackoff(
|
|
1799
|
+
metadata2.attempts,
|
|
1800
|
+
_nullishCoalesce(config2.retryBackoffBase, () => ( DEFAULT_RETRY_BACKOFF_BASE)),
|
|
1801
|
+
_nullishCoalesce(config2.retryBackoffMax, () => ( DEFAULT_RETRY_BACKOFF_MAX))
|
|
1802
|
+
);
|
|
1803
|
+
const retryAt = metadata2.lastAttemptAt + backoffDelay;
|
|
1804
|
+
const now = Date.now();
|
|
1805
|
+
if (now < retryAt) {
|
|
1806
|
+
throw new SleepError(retryAt);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
const entry = _nullishCoalesce(existing, () => ( createEntry(location, { type: "step", data: {} })));
|
|
1810
|
+
if (!existing) {
|
|
1811
|
+
this.log("debug", { msg: "executing new step", step: config2.name, key });
|
|
1812
|
+
const nameIndex = registerName(this.storage, config2.name);
|
|
1813
|
+
entry.location = [...location];
|
|
1814
|
+
entry.location[entry.location.length - 1] = nameIndex;
|
|
1815
|
+
setEntry(this.storage, location, entry);
|
|
1816
|
+
} else {
|
|
1817
|
+
this.log("debug", { msg: "retrying step", step: config2.name, key });
|
|
1818
|
+
}
|
|
1819
|
+
const metadata = getOrCreateMetadata(this.storage, entry.id);
|
|
1820
|
+
metadata.status = "running";
|
|
1821
|
+
metadata.attempts++;
|
|
1822
|
+
metadata.lastAttemptAt = Date.now();
|
|
1823
|
+
metadata.dirty = true;
|
|
1824
|
+
const timeout = _nullishCoalesce(config2.timeout, () => ( DEFAULT_STEP_TIMEOUT));
|
|
1825
|
+
try {
|
|
1826
|
+
const output = await this.executeWithTimeout(
|
|
1827
|
+
config2.run(),
|
|
1828
|
+
timeout,
|
|
1829
|
+
config2.name
|
|
1830
|
+
);
|
|
1831
|
+
if (entry.kind.type === "step") {
|
|
1832
|
+
entry.kind.data.output = output;
|
|
1833
|
+
}
|
|
1834
|
+
entry.dirty = true;
|
|
1835
|
+
metadata.status = "completed";
|
|
1836
|
+
metadata.error = void 0;
|
|
1837
|
+
metadata.completedAt = Date.now();
|
|
1838
|
+
if (!config2.ephemeral) {
|
|
1839
|
+
this.log("debug", { msg: "flushing step", step: config2.name, key });
|
|
1840
|
+
await this.flushStorage();
|
|
1841
|
+
}
|
|
1842
|
+
this.log("debug", { msg: "step completed", step: config2.name, key });
|
|
1843
|
+
return output;
|
|
1844
|
+
} catch (error) {
|
|
1845
|
+
if (error instanceof StepTimeoutError) {
|
|
1846
|
+
if (entry.kind.type === "step") {
|
|
1847
|
+
entry.kind.data.error = String(error);
|
|
1848
|
+
}
|
|
1849
|
+
entry.dirty = true;
|
|
1850
|
+
metadata.status = "exhausted";
|
|
1851
|
+
metadata.error = String(error);
|
|
1852
|
+
await this.flushStorage();
|
|
1853
|
+
throw new CriticalError(error.message);
|
|
1854
|
+
}
|
|
1855
|
+
if (error instanceof CriticalError || error instanceof RollbackError) {
|
|
1856
|
+
if (entry.kind.type === "step") {
|
|
1857
|
+
entry.kind.data.error = String(error);
|
|
1858
|
+
}
|
|
1859
|
+
entry.dirty = true;
|
|
1860
|
+
metadata.status = "exhausted";
|
|
1861
|
+
metadata.error = String(error);
|
|
1862
|
+
await this.flushStorage();
|
|
1863
|
+
throw error;
|
|
1864
|
+
}
|
|
1865
|
+
if (entry.kind.type === "step") {
|
|
1866
|
+
entry.kind.data.error = String(error);
|
|
1867
|
+
}
|
|
1868
|
+
entry.dirty = true;
|
|
1869
|
+
metadata.status = "failed";
|
|
1870
|
+
metadata.error = String(error);
|
|
1871
|
+
await this.flushStorage();
|
|
1872
|
+
throw new StepFailedError(config2.name, error, metadata.attempts);
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* Execute a promise with timeout.
|
|
1877
|
+
*
|
|
1878
|
+
* Note: This does NOT cancel the underlying operation. JavaScript Promises
|
|
1879
|
+
* cannot be cancelled once started. When a timeout occurs:
|
|
1880
|
+
* - The step is marked as failed with StepTimeoutError
|
|
1881
|
+
* - The underlying async operation continues running in the background
|
|
1882
|
+
* - Any side effects from the operation may still occur
|
|
1883
|
+
*
|
|
1884
|
+
* For cancellable operations, pass ctx.abortSignal to APIs that support AbortSignal:
|
|
1885
|
+
*
|
|
1886
|
+
* return fetch(url, { signal: ctx.abortSignal });
|
|
1887
|
+
|
|
1888
|
+
* });
|
|
1889
|
+
*
|
|
1890
|
+
* Or check ctx.isEvicted() periodically in long-running loops.
|
|
1891
|
+
*/
|
|
1892
|
+
async executeStepRollback(config2) {
|
|
1893
|
+
this.checkDuplicateName(config2.name);
|
|
1894
|
+
this.ensureRollbackCheckpoint(config2);
|
|
1895
|
+
const location = appendName(
|
|
1896
|
+
this.storage,
|
|
1897
|
+
this.currentLocation,
|
|
1898
|
+
config2.name
|
|
1899
|
+
);
|
|
1900
|
+
const key = locationToKey(this.storage, location);
|
|
1901
|
+
const existing = this.storage.history.entries.get(key);
|
|
1902
|
+
this.markVisited(key);
|
|
1903
|
+
if (!existing || existing.kind.type !== "step") {
|
|
1904
|
+
this.stopRollback();
|
|
1905
|
+
}
|
|
1906
|
+
const metadata = await loadMetadata(
|
|
1907
|
+
this.storage,
|
|
1908
|
+
this.driver,
|
|
1909
|
+
existing.id
|
|
1910
|
+
);
|
|
1911
|
+
if (metadata.status !== "completed") {
|
|
1912
|
+
this.stopRollback();
|
|
1913
|
+
}
|
|
1914
|
+
const output = existing.kind.data.output;
|
|
1915
|
+
this.registerRollbackAction(config2, existing.id, output, metadata);
|
|
1916
|
+
return output;
|
|
1917
|
+
}
|
|
1918
|
+
async executeWithTimeout(promise, timeoutMs, stepName) {
|
|
1919
|
+
if (timeoutMs <= 0) {
|
|
1920
|
+
return promise;
|
|
1921
|
+
}
|
|
1922
|
+
let timeoutId;
|
|
1923
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1924
|
+
timeoutId = setTimeout(() => {
|
|
1925
|
+
reject(new StepTimeoutError(stepName, timeoutMs));
|
|
1926
|
+
}, timeoutMs);
|
|
1927
|
+
});
|
|
1928
|
+
try {
|
|
1929
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
1930
|
+
} finally {
|
|
1931
|
+
if (timeoutId !== void 0) {
|
|
1932
|
+
clearTimeout(timeoutId);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
// === Loop ===
|
|
1937
|
+
async loop(nameOrConfig, run) {
|
|
1938
|
+
this.assertNotInProgress();
|
|
1939
|
+
this.checkEvicted();
|
|
1940
|
+
const config2 = typeof nameOrConfig === "string" ? { name: nameOrConfig, run } : nameOrConfig;
|
|
1941
|
+
this.entryInProgress = true;
|
|
1942
|
+
try {
|
|
1943
|
+
return await this.executeLoop(config2);
|
|
1944
|
+
} finally {
|
|
1945
|
+
this.entryInProgress = false;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
async executeLoop(config2) {
|
|
1949
|
+
this.checkDuplicateName(config2.name);
|
|
1950
|
+
const location = appendName(
|
|
1951
|
+
this.storage,
|
|
1952
|
+
this.currentLocation,
|
|
1953
|
+
config2.name
|
|
1954
|
+
);
|
|
1955
|
+
const key = locationToKey(this.storage, location);
|
|
1956
|
+
const existing = this.storage.history.entries.get(key);
|
|
1957
|
+
this.markVisited(key);
|
|
1958
|
+
let entry;
|
|
1959
|
+
let state;
|
|
1960
|
+
let iteration;
|
|
1961
|
+
let rollbackSingleIteration = false;
|
|
1962
|
+
let rollbackIterationRan = false;
|
|
1963
|
+
let rollbackOutput;
|
|
1964
|
+
const rollbackMode = this.mode === "rollback";
|
|
1965
|
+
if (existing) {
|
|
1966
|
+
if (existing.kind.type !== "loop") {
|
|
1967
|
+
throw new HistoryDivergedError(
|
|
1968
|
+
`Expected loop "${config2.name}" at ${key}, found ${existing.kind.type}`
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
const loopData = existing.kind.data;
|
|
1972
|
+
if (rollbackMode) {
|
|
1973
|
+
if (loopData.output !== void 0) {
|
|
1974
|
+
return loopData.output;
|
|
1975
|
+
}
|
|
1976
|
+
rollbackSingleIteration = true;
|
|
1977
|
+
rollbackIterationRan = false;
|
|
1978
|
+
rollbackOutput = void 0;
|
|
1979
|
+
}
|
|
1980
|
+
if (loopData.output !== void 0) {
|
|
1981
|
+
return loopData.output;
|
|
1982
|
+
}
|
|
1983
|
+
entry = existing;
|
|
1984
|
+
state = loopData.state;
|
|
1985
|
+
iteration = loopData.iteration;
|
|
1986
|
+
if (rollbackMode) {
|
|
1987
|
+
rollbackOutput = loopData.output;
|
|
1988
|
+
rollbackIterationRan = rollbackOutput !== void 0;
|
|
1989
|
+
}
|
|
1990
|
+
} else {
|
|
1991
|
+
this.stopRollbackIfIncomplete(true);
|
|
1992
|
+
state = config2.state;
|
|
1993
|
+
iteration = 0;
|
|
1994
|
+
entry = createEntry(location, {
|
|
1995
|
+
type: "loop",
|
|
1996
|
+
data: { state, iteration }
|
|
1997
|
+
});
|
|
1998
|
+
setEntry(this.storage, location, entry);
|
|
1999
|
+
}
|
|
2000
|
+
const commitInterval = _nullishCoalesce(config2.commitInterval, () => ( DEFAULT_LOOP_COMMIT_INTERVAL));
|
|
2001
|
+
const historyEvery = _nullishCoalesce(_nullishCoalesce(config2.historyEvery, () => ( config2.commitInterval)), () => ( DEFAULT_LOOP_HISTORY_EVERY));
|
|
2002
|
+
const historyKeep = _nullishCoalesce(_nullishCoalesce(config2.historyKeep, () => ( config2.commitInterval)), () => ( DEFAULT_LOOP_HISTORY_KEEP));
|
|
2003
|
+
while (true) {
|
|
2004
|
+
if (rollbackMode && rollbackSingleIteration) {
|
|
2005
|
+
if (rollbackIterationRan) {
|
|
2006
|
+
return rollbackOutput;
|
|
2007
|
+
}
|
|
2008
|
+
this.stopRollbackIfIncomplete(true);
|
|
2009
|
+
}
|
|
2010
|
+
this.checkEvicted();
|
|
2011
|
+
const iterationLocation = appendLoopIteration(
|
|
2012
|
+
this.storage,
|
|
2013
|
+
location,
|
|
2014
|
+
config2.name,
|
|
2015
|
+
iteration
|
|
2016
|
+
);
|
|
2017
|
+
const branchCtx = this.createBranch(iterationLocation);
|
|
2018
|
+
const result = await config2.run(branchCtx, state);
|
|
2019
|
+
branchCtx.validateComplete();
|
|
2020
|
+
if ("break" in result && result.break) {
|
|
2021
|
+
if (entry.kind.type === "loop") {
|
|
2022
|
+
entry.kind.data.output = result.value;
|
|
2023
|
+
entry.kind.data.state = state;
|
|
2024
|
+
entry.kind.data.iteration = iteration;
|
|
2025
|
+
}
|
|
2026
|
+
entry.dirty = true;
|
|
2027
|
+
await this.flushStorage();
|
|
2028
|
+
await this.forgetOldIterations(
|
|
2029
|
+
location,
|
|
2030
|
+
iteration + 1,
|
|
2031
|
+
historyEvery,
|
|
2032
|
+
historyKeep
|
|
2033
|
+
);
|
|
2034
|
+
if (rollbackMode && rollbackSingleIteration) {
|
|
2035
|
+
rollbackOutput = result.value;
|
|
2036
|
+
rollbackIterationRan = true;
|
|
2037
|
+
continue;
|
|
2038
|
+
}
|
|
2039
|
+
return result.value;
|
|
2040
|
+
}
|
|
2041
|
+
if ("continue" in result && result.continue) {
|
|
2042
|
+
state = result.state;
|
|
2043
|
+
}
|
|
2044
|
+
iteration++;
|
|
2045
|
+
if (iteration % commitInterval === 0) {
|
|
2046
|
+
if (entry.kind.type === "loop") {
|
|
2047
|
+
entry.kind.data.state = state;
|
|
2048
|
+
entry.kind.data.iteration = iteration;
|
|
2049
|
+
}
|
|
2050
|
+
entry.dirty = true;
|
|
2051
|
+
await this.flushStorage();
|
|
2052
|
+
await this.forgetOldIterations(
|
|
2053
|
+
location,
|
|
2054
|
+
iteration,
|
|
2055
|
+
historyEvery,
|
|
2056
|
+
historyKeep
|
|
2057
|
+
);
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
/**
|
|
2062
|
+
* Delete old loop iteration entries to save storage space.
|
|
2063
|
+
*
|
|
2064
|
+
* Loop locations always end with a NameIndex (number) because loops are
|
|
2065
|
+
* created via appendName(). Even for nested loops, the innermost loop's
|
|
2066
|
+
* location ends with its name index:
|
|
2067
|
+
*
|
|
2068
|
+
* ctx.loop("outer") → location: [outerIndex]
|
|
2069
|
+
* iteration 0 → location: [{ loop: outerIndex, iteration: 0 }]
|
|
2070
|
+
* ctx.loop("inner") → location: [{ loop: outerIndex, iteration: 0 }, innerIndex]
|
|
2071
|
+
*
|
|
2072
|
+
* This function removes iterations older than (currentIteration - historyKeep)
|
|
2073
|
+
* every historyEvery iterations.
|
|
2074
|
+
*/
|
|
2075
|
+
async forgetOldIterations(loopLocation, currentIteration, historyEvery, historyKeep) {
|
|
2076
|
+
if (historyEvery <= 0 || historyKeep <= 0) {
|
|
2077
|
+
return;
|
|
2078
|
+
}
|
|
2079
|
+
if (currentIteration === 0 || currentIteration % historyEvery !== 0) {
|
|
2080
|
+
return;
|
|
2081
|
+
}
|
|
2082
|
+
const keepFrom = Math.max(0, currentIteration - historyKeep);
|
|
2083
|
+
const loopSegment = loopLocation[loopLocation.length - 1];
|
|
2084
|
+
if (typeof loopSegment !== "number") {
|
|
2085
|
+
throw new Error("Expected loop location to end with a name index");
|
|
2086
|
+
}
|
|
2087
|
+
for (let i = 0; i < keepFrom; i++) {
|
|
2088
|
+
const iterationLocation = [
|
|
2089
|
+
...loopLocation,
|
|
2090
|
+
{ loop: loopSegment, iteration: i }
|
|
2091
|
+
];
|
|
2092
|
+
await deleteEntriesWithPrefix(
|
|
2093
|
+
this.storage,
|
|
2094
|
+
this.driver,
|
|
2095
|
+
iterationLocation,
|
|
2096
|
+
this.historyNotifier
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
// === Sleep ===
|
|
2101
|
+
async sleep(name, durationMs) {
|
|
2102
|
+
const deadline = Date.now() + durationMs;
|
|
2103
|
+
return this.sleepUntil(name, deadline);
|
|
2104
|
+
}
|
|
2105
|
+
async sleepUntil(name, timestampMs) {
|
|
2106
|
+
this.assertNotInProgress();
|
|
2107
|
+
this.checkEvicted();
|
|
2108
|
+
this.entryInProgress = true;
|
|
2109
|
+
try {
|
|
2110
|
+
await this.executeSleep(name, timestampMs);
|
|
2111
|
+
} finally {
|
|
2112
|
+
this.entryInProgress = false;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
async executeSleep(name, deadline) {
|
|
2116
|
+
this.checkDuplicateName(name);
|
|
2117
|
+
const location = appendName(this.storage, this.currentLocation, name);
|
|
2118
|
+
const key = locationToKey(this.storage, location);
|
|
2119
|
+
const existing = this.storage.history.entries.get(key);
|
|
2120
|
+
this.markVisited(key);
|
|
2121
|
+
let entry;
|
|
2122
|
+
if (existing) {
|
|
2123
|
+
if (existing.kind.type !== "sleep") {
|
|
2124
|
+
throw new HistoryDivergedError(
|
|
2125
|
+
`Expected sleep "${name}" at ${key}, found ${existing.kind.type}`
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
const sleepData = existing.kind.data;
|
|
2129
|
+
if (this.mode === "rollback") {
|
|
2130
|
+
this.stopRollbackIfIncomplete(sleepData.state === "pending");
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
if (sleepData.state !== "pending") {
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2136
|
+
deadline = sleepData.deadline;
|
|
2137
|
+
entry = existing;
|
|
2138
|
+
} else {
|
|
2139
|
+
this.stopRollbackIfIncomplete(true);
|
|
2140
|
+
entry = createEntry(location, {
|
|
2141
|
+
type: "sleep",
|
|
2142
|
+
data: { deadline, state: "pending" }
|
|
2143
|
+
});
|
|
2144
|
+
setEntry(this.storage, location, entry);
|
|
2145
|
+
entry.dirty = true;
|
|
2146
|
+
await this.flushStorage();
|
|
2147
|
+
}
|
|
2148
|
+
const now = Date.now();
|
|
2149
|
+
const remaining = deadline - now;
|
|
2150
|
+
if (remaining <= 0) {
|
|
2151
|
+
if (entry.kind.type === "sleep") {
|
|
2152
|
+
entry.kind.data.state = "completed";
|
|
2153
|
+
}
|
|
2154
|
+
entry.dirty = true;
|
|
2155
|
+
await this.flushStorage();
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
if (remaining < this.driver.workerPollInterval) {
|
|
2159
|
+
await Promise.race([sleep(remaining), this.waitForEviction()]);
|
|
2160
|
+
this.checkEvicted();
|
|
2161
|
+
if (entry.kind.type === "sleep") {
|
|
2162
|
+
entry.kind.data.state = "completed";
|
|
2163
|
+
}
|
|
2164
|
+
entry.dirty = true;
|
|
2165
|
+
await this.flushStorage();
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
throw new SleepError(deadline);
|
|
2169
|
+
}
|
|
2170
|
+
// === Rollback Checkpoint ===
|
|
2171
|
+
async rollbackCheckpoint(name) {
|
|
2172
|
+
this.assertNotInProgress();
|
|
2173
|
+
this.checkEvicted();
|
|
2174
|
+
this.entryInProgress = true;
|
|
2175
|
+
try {
|
|
2176
|
+
await this.executeRollbackCheckpoint(name);
|
|
2177
|
+
} finally {
|
|
2178
|
+
this.entryInProgress = false;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
async executeRollbackCheckpoint(name) {
|
|
2182
|
+
this.checkDuplicateName(name);
|
|
2183
|
+
const location = appendName(this.storage, this.currentLocation, name);
|
|
2184
|
+
const key = locationToKey(this.storage, location);
|
|
2185
|
+
const existing = this.storage.history.entries.get(key);
|
|
2186
|
+
this.markVisited(key);
|
|
2187
|
+
if (existing) {
|
|
2188
|
+
if (existing.kind.type !== "rollback_checkpoint") {
|
|
2189
|
+
throw new HistoryDivergedError(
|
|
2190
|
+
`Expected rollback checkpoint "${name}" at ${key}, found ${existing.kind.type}`
|
|
2191
|
+
);
|
|
2192
|
+
}
|
|
2193
|
+
this.rollbackCheckpointSet = true;
|
|
2194
|
+
return;
|
|
2195
|
+
}
|
|
2196
|
+
if (this.mode === "rollback") {
|
|
2197
|
+
throw new HistoryDivergedError(
|
|
2198
|
+
`Missing rollback checkpoint "${name}" at ${key}`
|
|
2199
|
+
);
|
|
2200
|
+
}
|
|
2201
|
+
const entry = createEntry(location, {
|
|
2202
|
+
type: "rollback_checkpoint",
|
|
2203
|
+
data: { name }
|
|
2204
|
+
});
|
|
2205
|
+
setEntry(this.storage, location, entry);
|
|
2206
|
+
entry.dirty = true;
|
|
2207
|
+
await this.flushStorage();
|
|
2208
|
+
this.rollbackCheckpointSet = true;
|
|
2209
|
+
}
|
|
2210
|
+
// === Queue ===
|
|
2211
|
+
async queueSend(name, body) {
|
|
2212
|
+
const message = {
|
|
2213
|
+
id: crypto.randomUUID(),
|
|
2214
|
+
name,
|
|
2215
|
+
data: body,
|
|
2216
|
+
sentAt: Date.now()
|
|
2217
|
+
};
|
|
2218
|
+
await this.messageDriver.addMessage(message);
|
|
2219
|
+
}
|
|
2220
|
+
async queueNext(name, opts) {
|
|
2221
|
+
this.assertNotInProgress();
|
|
2222
|
+
this.checkEvicted();
|
|
2223
|
+
this.entryInProgress = true;
|
|
2224
|
+
try {
|
|
2225
|
+
return await this.executeQueueNext(name, opts);
|
|
2226
|
+
} finally {
|
|
2227
|
+
this.entryInProgress = false;
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
async executeQueueNext(name, opts) {
|
|
2231
|
+
if (this.pendingCompletableMessageIds.size > 0) {
|
|
2232
|
+
throw new Error(
|
|
2233
|
+
"Previous completable queue message is not completed. Call `message.complete(...)` before receiving the next message."
|
|
2234
|
+
);
|
|
2235
|
+
}
|
|
2236
|
+
const resolvedOpts = _nullishCoalesce(opts, () => ( {}));
|
|
2237
|
+
const messageNames = this.normalizeQueueNames(resolvedOpts.names);
|
|
2238
|
+
const messageNameLabel = this.messageNamesLabel(messageNames);
|
|
2239
|
+
const count = Math.max(1, _nullishCoalesce(resolvedOpts.count, () => ( 1)));
|
|
2240
|
+
const completable = resolvedOpts.completable === true;
|
|
2241
|
+
this.checkDuplicateName(name);
|
|
2242
|
+
const countLocation = appendName(
|
|
2243
|
+
this.storage,
|
|
2244
|
+
this.currentLocation,
|
|
2245
|
+
`${name}:count`
|
|
2246
|
+
);
|
|
2247
|
+
const countKey = locationToKey(this.storage, countLocation);
|
|
2248
|
+
const existingCount = this.storage.history.entries.get(countKey);
|
|
2249
|
+
this.markVisited(countKey);
|
|
2250
|
+
this.stopRollbackIfMissing(existingCount);
|
|
2251
|
+
let deadline;
|
|
2252
|
+
let deadlineEntry;
|
|
2253
|
+
if (resolvedOpts.timeout !== void 0) {
|
|
2254
|
+
const deadlineLocation = appendName(
|
|
2255
|
+
this.storage,
|
|
2256
|
+
this.currentLocation,
|
|
2257
|
+
`${name}:deadline`
|
|
2258
|
+
);
|
|
2259
|
+
const deadlineKey = locationToKey(this.storage, deadlineLocation);
|
|
2260
|
+
deadlineEntry = this.storage.history.entries.get(deadlineKey);
|
|
2261
|
+
this.markVisited(deadlineKey);
|
|
2262
|
+
this.stopRollbackIfMissing(deadlineEntry);
|
|
2263
|
+
if (deadlineEntry && deadlineEntry.kind.type === "sleep") {
|
|
2264
|
+
deadline = deadlineEntry.kind.data.deadline;
|
|
2265
|
+
} else {
|
|
2266
|
+
deadline = Date.now() + resolvedOpts.timeout;
|
|
2267
|
+
const created = createEntry(deadlineLocation, {
|
|
2268
|
+
type: "sleep",
|
|
2269
|
+
data: { deadline, state: "pending" }
|
|
2270
|
+
});
|
|
2271
|
+
setEntry(this.storage, deadlineLocation, created);
|
|
2272
|
+
created.dirty = true;
|
|
2273
|
+
await this.flushStorage();
|
|
2274
|
+
deadlineEntry = created;
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
if (existingCount && existingCount.kind.type === "message") {
|
|
2278
|
+
const replayCount = existingCount.kind.data.data;
|
|
2279
|
+
return await this.readReplayQueueMessages(
|
|
2280
|
+
name,
|
|
2281
|
+
replayCount,
|
|
2282
|
+
completable
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
const now = Date.now();
|
|
2286
|
+
if (deadline !== void 0 && now >= deadline) {
|
|
2287
|
+
if (deadlineEntry && deadlineEntry.kind.type === "sleep") {
|
|
2288
|
+
deadlineEntry.kind.data.state = "completed";
|
|
2289
|
+
deadlineEntry.dirty = true;
|
|
2290
|
+
}
|
|
2291
|
+
await this.recordQueueCountEntry(
|
|
2292
|
+
countLocation,
|
|
2293
|
+
`${messageNameLabel}:count`,
|
|
2294
|
+
0
|
|
2295
|
+
);
|
|
2296
|
+
return [];
|
|
2297
|
+
}
|
|
2298
|
+
const received = await this.receiveMessagesNow(
|
|
2299
|
+
messageNames,
|
|
2300
|
+
count,
|
|
2301
|
+
completable
|
|
2302
|
+
);
|
|
2303
|
+
if (received.length > 0) {
|
|
2304
|
+
const historyMessages = received.map(
|
|
2305
|
+
(message) => this.toWorkflowQueueMessage(message)
|
|
2306
|
+
);
|
|
2307
|
+
if (deadlineEntry && deadlineEntry.kind.type === "sleep") {
|
|
2308
|
+
deadlineEntry.kind.data.state = "interrupted";
|
|
2309
|
+
deadlineEntry.dirty = true;
|
|
2310
|
+
}
|
|
2311
|
+
await this.recordQueueMessages(
|
|
2312
|
+
name,
|
|
2313
|
+
countLocation,
|
|
2314
|
+
messageNames,
|
|
2315
|
+
historyMessages
|
|
2316
|
+
);
|
|
2317
|
+
const queueMessages = received.map(
|
|
2318
|
+
(message, index) => this.createQueueMessage(message, completable, {
|
|
2319
|
+
historyLocation: appendName(
|
|
2320
|
+
this.storage,
|
|
2321
|
+
this.currentLocation,
|
|
2322
|
+
`${name}:${index}`
|
|
2323
|
+
)
|
|
2324
|
+
})
|
|
2325
|
+
);
|
|
2326
|
+
return queueMessages;
|
|
2327
|
+
}
|
|
2328
|
+
if (deadline === void 0) {
|
|
2329
|
+
throw new MessageWaitError(messageNames);
|
|
2330
|
+
}
|
|
2331
|
+
throw new SleepError(deadline, messageNames);
|
|
2332
|
+
}
|
|
2333
|
+
normalizeQueueNames(names) {
|
|
2334
|
+
if (!names || names.length === 0) {
|
|
2335
|
+
return [];
|
|
2336
|
+
}
|
|
2337
|
+
const deduped = [];
|
|
2338
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2339
|
+
for (const name of names) {
|
|
2340
|
+
if (seen.has(name)) {
|
|
2341
|
+
continue;
|
|
2342
|
+
}
|
|
2343
|
+
seen.add(name);
|
|
2344
|
+
deduped.push(name);
|
|
2345
|
+
}
|
|
2346
|
+
return deduped;
|
|
2347
|
+
}
|
|
2348
|
+
messageNamesLabel(messageNames) {
|
|
2349
|
+
if (messageNames.length === 0) {
|
|
2350
|
+
return "*";
|
|
2351
|
+
}
|
|
2352
|
+
return messageNames.length === 1 ? messageNames[0] : messageNames.join("|");
|
|
2353
|
+
}
|
|
2354
|
+
async receiveMessagesNow(messageNames, count, completable) {
|
|
2355
|
+
return await this.messageDriver.receiveMessages({
|
|
2356
|
+
names: messageNames.length > 0 ? messageNames : void 0,
|
|
2357
|
+
count,
|
|
2358
|
+
completable
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
2361
|
+
async recordQueueMessages(name, countLocation, messageNames, messages) {
|
|
2362
|
+
for (let i = 0; i < messages.length; i++) {
|
|
2363
|
+
const messageLocation = appendName(
|
|
2364
|
+
this.storage,
|
|
2365
|
+
this.currentLocation,
|
|
2366
|
+
`${name}:${i}`
|
|
2367
|
+
);
|
|
2368
|
+
const messageEntry = createEntry(messageLocation, {
|
|
2369
|
+
type: "message",
|
|
2370
|
+
data: {
|
|
2371
|
+
name: messages[i].name,
|
|
2372
|
+
data: this.toHistoryQueueMessage(messages[i])
|
|
2373
|
+
}
|
|
2374
|
+
});
|
|
2375
|
+
setEntry(this.storage, messageLocation, messageEntry);
|
|
2376
|
+
this.markVisited(locationToKey(this.storage, messageLocation));
|
|
2377
|
+
}
|
|
2378
|
+
await this.recordQueueCountEntry(
|
|
2379
|
+
countLocation,
|
|
2380
|
+
`${this.messageNamesLabel(messageNames)}:count`,
|
|
2381
|
+
messages.length
|
|
2382
|
+
);
|
|
2383
|
+
}
|
|
2384
|
+
async recordQueueCountEntry(countLocation, countLabel, count) {
|
|
2385
|
+
const countEntry = createEntry(countLocation, {
|
|
2386
|
+
type: "message",
|
|
2387
|
+
data: {
|
|
2388
|
+
name: countLabel,
|
|
2389
|
+
data: count
|
|
2390
|
+
}
|
|
2391
|
+
});
|
|
2392
|
+
setEntry(this.storage, countLocation, countEntry);
|
|
2393
|
+
await this.flushStorage();
|
|
2394
|
+
}
|
|
2395
|
+
async readReplayQueueMessages(name, count, completable) {
|
|
2396
|
+
const results = [];
|
|
2397
|
+
for (let i = 0; i < count; i++) {
|
|
2398
|
+
const messageLocation = appendName(
|
|
2399
|
+
this.storage,
|
|
2400
|
+
this.currentLocation,
|
|
2401
|
+
`${name}:${i}`
|
|
2402
|
+
);
|
|
2403
|
+
const messageKey = locationToKey(this.storage, messageLocation);
|
|
2404
|
+
this.markVisited(messageKey);
|
|
2405
|
+
const existingMessage = this.storage.history.entries.get(messageKey);
|
|
2406
|
+
if (!existingMessage || existingMessage.kind.type !== "message") {
|
|
2407
|
+
throw new HistoryDivergedError(
|
|
2408
|
+
`Expected queue message "${name}:${i}" in history`
|
|
2409
|
+
);
|
|
2410
|
+
}
|
|
2411
|
+
const parsed = this.fromHistoryQueueMessage(
|
|
2412
|
+
existingMessage.kind.data.name,
|
|
2413
|
+
existingMessage.kind.data.data
|
|
2414
|
+
);
|
|
2415
|
+
results.push(
|
|
2416
|
+
this.createQueueMessage(parsed.message, completable, {
|
|
2417
|
+
historyLocation: messageLocation,
|
|
2418
|
+
completed: parsed.completed,
|
|
2419
|
+
replay: true
|
|
2420
|
+
})
|
|
2421
|
+
);
|
|
2422
|
+
}
|
|
2423
|
+
return results;
|
|
2424
|
+
}
|
|
2425
|
+
toWorkflowQueueMessage(message) {
|
|
2426
|
+
return {
|
|
2427
|
+
id: message.id,
|
|
2428
|
+
name: message.name,
|
|
2429
|
+
body: message.data,
|
|
2430
|
+
createdAt: message.sentAt
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
createQueueMessage(message, completable, opts) {
|
|
2434
|
+
const queueMessage = this.toWorkflowQueueMessage(message);
|
|
2435
|
+
if (!completable) {
|
|
2436
|
+
return queueMessage;
|
|
2437
|
+
}
|
|
2438
|
+
if ((opts == null ? void 0 : opts.replay) && opts.completed) {
|
|
2439
|
+
return {
|
|
2440
|
+
...queueMessage,
|
|
2441
|
+
complete: async () => {
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
const messageId = message.id;
|
|
2446
|
+
this.pendingCompletableMessageIds.add(messageId);
|
|
2447
|
+
let completed = false;
|
|
2448
|
+
return {
|
|
2449
|
+
...queueMessage,
|
|
2450
|
+
complete: async (response) => {
|
|
2451
|
+
if (completed) {
|
|
2452
|
+
throw new Error("Queue message already completed");
|
|
2453
|
+
}
|
|
2454
|
+
completed = true;
|
|
2455
|
+
try {
|
|
2456
|
+
await this.completeMessage(message, response);
|
|
2457
|
+
await this.markQueueMessageCompleted(opts == null ? void 0 : opts.historyLocation);
|
|
2458
|
+
this.pendingCompletableMessageIds.delete(messageId);
|
|
2459
|
+
} catch (error) {
|
|
2460
|
+
completed = false;
|
|
2461
|
+
throw error;
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
async markQueueMessageCompleted(historyLocation) {
|
|
2467
|
+
if (!historyLocation) {
|
|
2468
|
+
return;
|
|
2469
|
+
}
|
|
2470
|
+
const key = locationToKey(this.storage, historyLocation);
|
|
2471
|
+
const entry = this.storage.history.entries.get(key);
|
|
2472
|
+
if (!entry || entry.kind.type !== "message") {
|
|
2473
|
+
return;
|
|
2474
|
+
}
|
|
2475
|
+
const parsed = this.fromHistoryQueueMessage(
|
|
2476
|
+
entry.kind.data.name,
|
|
2477
|
+
entry.kind.data.data
|
|
2478
|
+
);
|
|
2479
|
+
entry.kind.data.data = this.toHistoryQueueMessage(
|
|
2480
|
+
this.toWorkflowQueueMessage(parsed.message),
|
|
2481
|
+
true
|
|
2482
|
+
);
|
|
2483
|
+
entry.dirty = true;
|
|
2484
|
+
await this.flushStorage();
|
|
2485
|
+
}
|
|
2486
|
+
async completeMessage(message, response) {
|
|
2487
|
+
if (message.complete) {
|
|
2488
|
+
await message.complete(response);
|
|
2489
|
+
return;
|
|
2490
|
+
}
|
|
2491
|
+
await this.messageDriver.completeMessage(message.id, response);
|
|
2492
|
+
}
|
|
2493
|
+
toHistoryQueueMessage(message, completed = false) {
|
|
2494
|
+
return {
|
|
2495
|
+
[QUEUE_HISTORY_MESSAGE_MARKER]: 1,
|
|
2496
|
+
id: message.id,
|
|
2497
|
+
name: message.name,
|
|
2498
|
+
body: message.body,
|
|
2499
|
+
createdAt: message.createdAt,
|
|
2500
|
+
completed
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
fromHistoryQueueMessage(name, value) {
|
|
2504
|
+
if (typeof value === "object" && value !== null && value[QUEUE_HISTORY_MESSAGE_MARKER] === 1) {
|
|
2505
|
+
const serialized = value;
|
|
2506
|
+
const id = typeof serialized.id === "string" ? serialized.id : "";
|
|
2507
|
+
const serializedName = typeof serialized.name === "string" ? serialized.name : name;
|
|
2508
|
+
const createdAt = typeof serialized.createdAt === "number" ? serialized.createdAt : 0;
|
|
2509
|
+
const completed = typeof serialized.completed === "boolean" ? serialized.completed : false;
|
|
2510
|
+
return {
|
|
2511
|
+
message: {
|
|
2512
|
+
id,
|
|
2513
|
+
name: serializedName,
|
|
2514
|
+
data: serialized.body,
|
|
2515
|
+
sentAt: createdAt
|
|
2516
|
+
},
|
|
2517
|
+
completed
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
return {
|
|
2521
|
+
message: {
|
|
2522
|
+
id: "",
|
|
2523
|
+
name,
|
|
2524
|
+
data: value,
|
|
2525
|
+
sentAt: 0
|
|
2526
|
+
},
|
|
2527
|
+
completed: false
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
// === Join ===
|
|
2531
|
+
async join(name, branches) {
|
|
2532
|
+
this.assertNotInProgress();
|
|
2533
|
+
this.checkEvicted();
|
|
2534
|
+
this.entryInProgress = true;
|
|
2535
|
+
try {
|
|
2536
|
+
return await this.executeJoin(name, branches);
|
|
2537
|
+
} finally {
|
|
2538
|
+
this.entryInProgress = false;
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
async executeJoin(name, branches) {
|
|
2542
|
+
this.checkDuplicateName(name);
|
|
2543
|
+
const location = appendName(this.storage, this.currentLocation, name);
|
|
2544
|
+
const key = locationToKey(this.storage, location);
|
|
2545
|
+
const existing = this.storage.history.entries.get(key);
|
|
2546
|
+
this.markVisited(key);
|
|
2547
|
+
this.stopRollbackIfMissing(existing);
|
|
2548
|
+
let entry;
|
|
2549
|
+
if (existing) {
|
|
2550
|
+
if (existing.kind.type !== "join") {
|
|
2551
|
+
throw new HistoryDivergedError(
|
|
2552
|
+
`Expected join "${name}" at ${key}, found ${existing.kind.type}`
|
|
2553
|
+
);
|
|
2554
|
+
}
|
|
2555
|
+
entry = existing;
|
|
2556
|
+
} else {
|
|
2557
|
+
entry = createEntry(location, {
|
|
2558
|
+
type: "join",
|
|
2559
|
+
data: {
|
|
2560
|
+
branches: Object.fromEntries(
|
|
2561
|
+
Object.keys(branches).map((k) => [
|
|
2562
|
+
k,
|
|
2563
|
+
{ status: "pending" }
|
|
2564
|
+
])
|
|
2565
|
+
)
|
|
2566
|
+
}
|
|
2567
|
+
});
|
|
2568
|
+
setEntry(this.storage, location, entry);
|
|
2569
|
+
entry.dirty = true;
|
|
2570
|
+
await this.flushStorage();
|
|
2571
|
+
}
|
|
2572
|
+
if (entry.kind.type !== "join") {
|
|
2573
|
+
throw new HistoryDivergedError("Entry type mismatch");
|
|
2574
|
+
}
|
|
2575
|
+
this.stopRollbackIfIncomplete(
|
|
2576
|
+
Object.values(entry.kind.data.branches).some(
|
|
2577
|
+
(branch) => branch.status !== "completed"
|
|
2578
|
+
)
|
|
2579
|
+
);
|
|
2580
|
+
const joinData = entry.kind.data;
|
|
2581
|
+
const results = {};
|
|
2582
|
+
const errors = {};
|
|
2583
|
+
const branchPromises = Object.entries(branches).map(
|
|
2584
|
+
async ([branchName, config2]) => {
|
|
2585
|
+
const branchStatus = joinData.branches[branchName];
|
|
2586
|
+
if (branchStatus.status === "completed") {
|
|
2587
|
+
results[branchName] = branchStatus.output;
|
|
2588
|
+
return;
|
|
2589
|
+
}
|
|
2590
|
+
if (branchStatus.status === "failed") {
|
|
2591
|
+
errors[branchName] = new Error(branchStatus.error);
|
|
2592
|
+
return;
|
|
2593
|
+
}
|
|
2594
|
+
const branchLocation = appendName(
|
|
2595
|
+
this.storage,
|
|
2596
|
+
location,
|
|
2597
|
+
branchName
|
|
2598
|
+
);
|
|
2599
|
+
const branchCtx = this.createBranch(branchLocation);
|
|
2600
|
+
branchStatus.status = "running";
|
|
2601
|
+
entry.dirty = true;
|
|
2602
|
+
try {
|
|
2603
|
+
const output = await config2.run(branchCtx);
|
|
2604
|
+
branchCtx.validateComplete();
|
|
2605
|
+
branchStatus.status = "completed";
|
|
2606
|
+
branchStatus.output = output;
|
|
2607
|
+
results[branchName] = output;
|
|
2608
|
+
} catch (error) {
|
|
2609
|
+
branchStatus.status = "failed";
|
|
2610
|
+
branchStatus.error = String(error);
|
|
2611
|
+
errors[branchName] = error;
|
|
2612
|
+
}
|
|
2613
|
+
entry.dirty = true;
|
|
2614
|
+
}
|
|
2615
|
+
);
|
|
2616
|
+
await Promise.allSettled(branchPromises);
|
|
2617
|
+
await this.flushStorage();
|
|
2618
|
+
if (Object.keys(errors).length > 0) {
|
|
2619
|
+
throw new JoinError(errors);
|
|
2620
|
+
}
|
|
2621
|
+
return results;
|
|
2622
|
+
}
|
|
2623
|
+
// === Race ===
|
|
2624
|
+
async race(name, branches) {
|
|
2625
|
+
this.assertNotInProgress();
|
|
2626
|
+
this.checkEvicted();
|
|
2627
|
+
this.entryInProgress = true;
|
|
2628
|
+
try {
|
|
2629
|
+
return await this.executeRace(name, branches);
|
|
2630
|
+
} finally {
|
|
2631
|
+
this.entryInProgress = false;
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
async executeRace(name, branches) {
|
|
2635
|
+
this.checkDuplicateName(name);
|
|
2636
|
+
const location = appendName(this.storage, this.currentLocation, name);
|
|
2637
|
+
const key = locationToKey(this.storage, location);
|
|
2638
|
+
const existing = this.storage.history.entries.get(key);
|
|
2639
|
+
this.markVisited(key);
|
|
2640
|
+
this.stopRollbackIfMissing(existing);
|
|
2641
|
+
let entry;
|
|
2642
|
+
if (existing) {
|
|
2643
|
+
if (existing.kind.type !== "race") {
|
|
2644
|
+
throw new HistoryDivergedError(
|
|
2645
|
+
`Expected race "${name}" at ${key}, found ${existing.kind.type}`
|
|
2646
|
+
);
|
|
2647
|
+
}
|
|
2648
|
+
entry = existing;
|
|
2649
|
+
const raceKind = existing.kind;
|
|
2650
|
+
if (raceKind.data.winner !== null) {
|
|
2651
|
+
const winnerStatus = raceKind.data.branches[raceKind.data.winner];
|
|
2652
|
+
return {
|
|
2653
|
+
winner: raceKind.data.winner,
|
|
2654
|
+
value: winnerStatus.output
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
this.stopRollbackIfIncomplete(true);
|
|
2658
|
+
} else {
|
|
2659
|
+
entry = createEntry(location, {
|
|
2660
|
+
type: "race",
|
|
2661
|
+
data: {
|
|
2662
|
+
winner: null,
|
|
2663
|
+
branches: Object.fromEntries(
|
|
2664
|
+
branches.map((b) => [
|
|
2665
|
+
b.name,
|
|
2666
|
+
{ status: "pending" }
|
|
2667
|
+
])
|
|
2668
|
+
)
|
|
2669
|
+
}
|
|
2670
|
+
});
|
|
2671
|
+
setEntry(this.storage, location, entry);
|
|
2672
|
+
entry.dirty = true;
|
|
2673
|
+
await this.flushStorage();
|
|
2674
|
+
}
|
|
2675
|
+
if (entry.kind.type !== "race") {
|
|
2676
|
+
throw new HistoryDivergedError("Entry type mismatch");
|
|
2677
|
+
}
|
|
2678
|
+
const raceData = entry.kind.data;
|
|
2679
|
+
const raceAbortController = new AbortController();
|
|
2680
|
+
const branchPromises = [];
|
|
2681
|
+
let winnerName = null;
|
|
2682
|
+
let winnerValue = null;
|
|
2683
|
+
let settled = false;
|
|
2684
|
+
let pendingCount = branches.length;
|
|
2685
|
+
const errors = {};
|
|
2686
|
+
const lateErrors = [];
|
|
2687
|
+
let yieldError = null;
|
|
2688
|
+
for (const branch of branches) {
|
|
2689
|
+
const branchStatus = raceData.branches[branch.name];
|
|
2690
|
+
if (branchStatus.status !== "pending" && branchStatus.status !== "running") {
|
|
2691
|
+
pendingCount--;
|
|
2692
|
+
if (branchStatus.status === "completed" && !settled) {
|
|
2693
|
+
settled = true;
|
|
2694
|
+
winnerName = branch.name;
|
|
2695
|
+
winnerValue = branchStatus.output;
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
if (settled && winnerName !== null && winnerValue !== null) {
|
|
2700
|
+
return { winner: winnerName, value: winnerValue };
|
|
2701
|
+
}
|
|
2702
|
+
for (const branch of branches) {
|
|
2703
|
+
const branchStatus = raceData.branches[branch.name];
|
|
2704
|
+
if (branchStatus.status !== "pending" && branchStatus.status !== "running") {
|
|
2705
|
+
continue;
|
|
2706
|
+
}
|
|
2707
|
+
const branchLocation = appendName(
|
|
2708
|
+
this.storage,
|
|
2709
|
+
location,
|
|
2710
|
+
branch.name
|
|
2711
|
+
);
|
|
2712
|
+
const branchCtx = this.createBranch(
|
|
2713
|
+
branchLocation,
|
|
2714
|
+
raceAbortController
|
|
2715
|
+
);
|
|
2716
|
+
branchStatus.status = "running";
|
|
2717
|
+
entry.dirty = true;
|
|
2718
|
+
const branchPromise = branch.run(branchCtx).then(
|
|
2719
|
+
async (output) => {
|
|
2720
|
+
if (settled) {
|
|
2721
|
+
branchStatus.status = "completed";
|
|
2722
|
+
branchStatus.output = output;
|
|
2723
|
+
entry.dirty = true;
|
|
2724
|
+
return;
|
|
2725
|
+
}
|
|
2726
|
+
settled = true;
|
|
2727
|
+
winnerName = branch.name;
|
|
2728
|
+
winnerValue = output;
|
|
2729
|
+
branchCtx.validateComplete();
|
|
2730
|
+
branchStatus.status = "completed";
|
|
2731
|
+
branchStatus.output = output;
|
|
2732
|
+
raceData.winner = branch.name;
|
|
2733
|
+
entry.dirty = true;
|
|
2734
|
+
raceAbortController.abort();
|
|
2735
|
+
},
|
|
2736
|
+
(error) => {
|
|
2737
|
+
pendingCount--;
|
|
2738
|
+
if (error instanceof SleepError) {
|
|
2739
|
+
if (!yieldError || !(yieldError instanceof SleepError) || error.deadline < yieldError.deadline) {
|
|
2740
|
+
yieldError = error;
|
|
2741
|
+
}
|
|
2742
|
+
branchStatus.status = "running";
|
|
2743
|
+
entry.dirty = true;
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
if (error instanceof MessageWaitError) {
|
|
2747
|
+
if (!yieldError || !(yieldError instanceof SleepError)) {
|
|
2748
|
+
if (!yieldError) {
|
|
2749
|
+
yieldError = error;
|
|
2750
|
+
} else if (yieldError instanceof MessageWaitError) {
|
|
2751
|
+
yieldError = new MessageWaitError([
|
|
2752
|
+
...yieldError.messageNames,
|
|
2753
|
+
...error.messageNames
|
|
2754
|
+
]);
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
branchStatus.status = "running";
|
|
2758
|
+
entry.dirty = true;
|
|
2759
|
+
return;
|
|
2760
|
+
}
|
|
2761
|
+
if (error instanceof CancelledError || error instanceof EvictedError) {
|
|
2762
|
+
branchStatus.status = "cancelled";
|
|
2763
|
+
} else {
|
|
2764
|
+
branchStatus.status = "failed";
|
|
2765
|
+
branchStatus.error = String(error);
|
|
2766
|
+
if (settled) {
|
|
2767
|
+
lateErrors.push({
|
|
2768
|
+
name: branch.name,
|
|
2769
|
+
error: String(error)
|
|
2770
|
+
});
|
|
2771
|
+
} else {
|
|
2772
|
+
errors[branch.name] = error;
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
entry.dirty = true;
|
|
2776
|
+
if (pendingCount === 0 && !settled) {
|
|
2777
|
+
settled = true;
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
);
|
|
2781
|
+
branchPromises.push(branchPromise);
|
|
2782
|
+
}
|
|
2783
|
+
await Promise.allSettled(branchPromises);
|
|
2784
|
+
if (yieldError && !settled) {
|
|
2785
|
+
await this.flushStorage();
|
|
2786
|
+
throw yieldError;
|
|
2787
|
+
}
|
|
2788
|
+
if (winnerName !== null) {
|
|
2789
|
+
for (const branch of branches) {
|
|
2790
|
+
if (branch.name !== winnerName) {
|
|
2791
|
+
const branchLocation = appendName(
|
|
2792
|
+
this.storage,
|
|
2793
|
+
location,
|
|
2794
|
+
branch.name
|
|
2795
|
+
);
|
|
2796
|
+
await deleteEntriesWithPrefix(
|
|
2797
|
+
this.storage,
|
|
2798
|
+
this.driver,
|
|
2799
|
+
branchLocation,
|
|
2800
|
+
this.historyNotifier
|
|
2801
|
+
);
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
await this.flushStorage();
|
|
2806
|
+
if (lateErrors.length > 0) {
|
|
2807
|
+
console.warn(
|
|
2808
|
+
`Race "${name}" had ${lateErrors.length} branch(es) fail after winner was determined:`,
|
|
2809
|
+
lateErrors
|
|
2810
|
+
);
|
|
2811
|
+
}
|
|
2812
|
+
if (winnerName !== null && winnerValue !== null) {
|
|
2813
|
+
return { winner: winnerName, value: winnerValue };
|
|
2814
|
+
}
|
|
2815
|
+
throw new RaceError(
|
|
2816
|
+
"All branches failed",
|
|
2817
|
+
Object.entries(errors).map(([name2, error]) => ({
|
|
2818
|
+
name: name2,
|
|
2819
|
+
error: String(error)
|
|
2820
|
+
}))
|
|
2821
|
+
);
|
|
2822
|
+
}
|
|
2823
|
+
// === Removed ===
|
|
2824
|
+
async removed(name, originalType) {
|
|
2825
|
+
this.assertNotInProgress();
|
|
2826
|
+
this.checkEvicted();
|
|
2827
|
+
this.entryInProgress = true;
|
|
2828
|
+
try {
|
|
2829
|
+
await this.executeRemoved(name, originalType);
|
|
2830
|
+
} finally {
|
|
2831
|
+
this.entryInProgress = false;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
async executeRemoved(name, originalType) {
|
|
2835
|
+
this.checkDuplicateName(name);
|
|
2836
|
+
const location = appendName(this.storage, this.currentLocation, name);
|
|
2837
|
+
const key = locationToKey(this.storage, location);
|
|
2838
|
+
const existing = this.storage.history.entries.get(key);
|
|
2839
|
+
this.markVisited(key);
|
|
2840
|
+
this.stopRollbackIfMissing(existing);
|
|
2841
|
+
if (existing) {
|
|
2842
|
+
if (existing.kind.type !== "removed" && existing.kind.type !== originalType) {
|
|
2843
|
+
throw new HistoryDivergedError(
|
|
2844
|
+
`Expected ${originalType} or removed at ${key}, found ${existing.kind.type}`
|
|
2845
|
+
);
|
|
2846
|
+
}
|
|
2847
|
+
return;
|
|
2848
|
+
}
|
|
2849
|
+
const entry = createEntry(location, {
|
|
2850
|
+
type: "removed",
|
|
2851
|
+
data: { originalType, originalName: name }
|
|
2852
|
+
});
|
|
2853
|
+
setEntry(this.storage, location, entry);
|
|
2854
|
+
await this.flushStorage();
|
|
2855
|
+
}
|
|
2856
|
+
}, _class);
|
|
2857
|
+
|
|
2858
|
+
// src/index.ts
|
|
2859
|
+
var Loop = {
|
|
2860
|
+
continue: (state) => ({
|
|
2861
|
+
continue: true,
|
|
2862
|
+
state
|
|
2863
|
+
}),
|
|
2864
|
+
break: (value) => ({
|
|
2865
|
+
break: true,
|
|
2866
|
+
value
|
|
2867
|
+
})
|
|
2868
|
+
};
|
|
2869
|
+
function createLiveRuntime() {
|
|
2870
|
+
return {
|
|
2871
|
+
isSleeping: false
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
function createEvictionWait(signal) {
|
|
2875
|
+
if (signal.aborted) {
|
|
2876
|
+
return {
|
|
2877
|
+
promise: Promise.reject(new EvictedError()),
|
|
2878
|
+
cleanup: () => {
|
|
2879
|
+
}
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
let onAbort;
|
|
2883
|
+
const promise = new Promise((_, reject) => {
|
|
2884
|
+
onAbort = () => {
|
|
2885
|
+
reject(new EvictedError());
|
|
2886
|
+
};
|
|
2887
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2888
|
+
});
|
|
2889
|
+
return {
|
|
2890
|
+
promise,
|
|
2891
|
+
cleanup: () => {
|
|
2892
|
+
if (onAbort) {
|
|
2893
|
+
signal.removeEventListener("abort", onAbort);
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
};
|
|
2897
|
+
}
|
|
2898
|
+
function createRollbackContext(workflowId, abortController) {
|
|
2899
|
+
return {
|
|
2900
|
+
workflowId,
|
|
2901
|
+
abortSignal: abortController.signal,
|
|
2902
|
+
isEvicted: () => abortController.signal.aborted
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
async function awaitWithEviction(promise, abortSignal) {
|
|
2906
|
+
const { promise: evictionPromise, cleanup } = createEvictionWait(abortSignal);
|
|
2907
|
+
try {
|
|
2908
|
+
return await Promise.race([promise, evictionPromise]);
|
|
2909
|
+
} finally {
|
|
2910
|
+
cleanup();
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
async function executeRollback(workflowId, workflowFn, input, driver, messageDriver, abortController, storage, historyNotifier, logger) {
|
|
2914
|
+
const rollbackActions = [];
|
|
2915
|
+
const ctx = new WorkflowContextImpl(
|
|
2916
|
+
workflowId,
|
|
2917
|
+
storage,
|
|
2918
|
+
driver,
|
|
2919
|
+
messageDriver,
|
|
2920
|
+
void 0,
|
|
2921
|
+
abortController,
|
|
2922
|
+
"rollback",
|
|
2923
|
+
rollbackActions,
|
|
2924
|
+
false,
|
|
2925
|
+
historyNotifier,
|
|
2926
|
+
logger
|
|
2927
|
+
);
|
|
2928
|
+
try {
|
|
2929
|
+
await workflowFn(ctx, input);
|
|
2930
|
+
} catch (error) {
|
|
2931
|
+
if (error instanceof EvictedError) {
|
|
2932
|
+
throw error;
|
|
2933
|
+
}
|
|
2934
|
+
if (error instanceof RollbackStopError) {
|
|
2935
|
+
} else {
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
if (rollbackActions.length === 0) {
|
|
2939
|
+
return;
|
|
2940
|
+
}
|
|
2941
|
+
const rollbackContext = createRollbackContext(workflowId, abortController);
|
|
2942
|
+
for (let i = rollbackActions.length - 1; i >= 0; i--) {
|
|
2943
|
+
if (abortController.signal.aborted) {
|
|
2944
|
+
throw new EvictedError();
|
|
2945
|
+
}
|
|
2946
|
+
const action = rollbackActions[i];
|
|
2947
|
+
const metadata = await loadMetadata(storage, driver, action.entryId);
|
|
2948
|
+
if (metadata.rollbackCompletedAt !== void 0) {
|
|
2949
|
+
continue;
|
|
2950
|
+
}
|
|
2951
|
+
try {
|
|
2952
|
+
await awaitWithEviction(
|
|
2953
|
+
action.rollback(rollbackContext, action.output),
|
|
2954
|
+
abortController.signal
|
|
2955
|
+
);
|
|
2956
|
+
metadata.rollbackCompletedAt = Date.now();
|
|
2957
|
+
metadata.rollbackError = void 0;
|
|
2958
|
+
} catch (error) {
|
|
2959
|
+
if (error instanceof EvictedError) {
|
|
2960
|
+
throw error;
|
|
2961
|
+
}
|
|
2962
|
+
metadata.rollbackError = error instanceof Error ? error.message : String(error);
|
|
2963
|
+
throw error;
|
|
2964
|
+
} finally {
|
|
2965
|
+
metadata.dirty = true;
|
|
2966
|
+
await flush(storage, driver, historyNotifier);
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
async function setSleepState(storage, driver, workflowId, deadline, messageNames, historyNotifier) {
|
|
2971
|
+
storage.state = "sleeping";
|
|
2972
|
+
await flush(storage, driver, historyNotifier);
|
|
2973
|
+
await driver.setAlarm(workflowId, deadline);
|
|
2974
|
+
return {
|
|
2975
|
+
state: "sleeping",
|
|
2976
|
+
sleepUntil: deadline,
|
|
2977
|
+
waitingForMessages: messageNames
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
async function setMessageWaitState(storage, driver, messageNames, historyNotifier) {
|
|
2981
|
+
storage.state = "sleeping";
|
|
2982
|
+
await flush(storage, driver, historyNotifier);
|
|
2983
|
+
return { state: "sleeping", waitingForMessages: messageNames };
|
|
2984
|
+
}
|
|
2985
|
+
async function setEvictedState(storage, driver, historyNotifier) {
|
|
2986
|
+
await flush(storage, driver, historyNotifier);
|
|
2987
|
+
return { state: storage.state };
|
|
2988
|
+
}
|
|
2989
|
+
async function setRetryState(storage, driver, workflowId, historyNotifier) {
|
|
2990
|
+
storage.state = "sleeping";
|
|
2991
|
+
await flush(storage, driver, historyNotifier);
|
|
2992
|
+
const retryAt = Date.now() + 100;
|
|
2993
|
+
await driver.setAlarm(workflowId, retryAt);
|
|
2994
|
+
return { state: "sleeping", sleepUntil: retryAt };
|
|
2995
|
+
}
|
|
2996
|
+
async function setFailedState(storage, driver, error, historyNotifier) {
|
|
2997
|
+
storage.state = "failed";
|
|
2998
|
+
storage.error = extractErrorInfo(error);
|
|
2999
|
+
await flush(storage, driver, historyNotifier);
|
|
3000
|
+
}
|
|
3001
|
+
async function waitForSleep(runtime, deadline, abortSignal) {
|
|
3002
|
+
while (true) {
|
|
3003
|
+
const remaining = deadline - Date.now();
|
|
3004
|
+
if (remaining <= 0) {
|
|
3005
|
+
return;
|
|
3006
|
+
}
|
|
3007
|
+
let timeoutHandle;
|
|
3008
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
3009
|
+
timeoutHandle = setLongTimeout(resolve, remaining);
|
|
3010
|
+
});
|
|
3011
|
+
const wakePromise = new Promise((resolve) => {
|
|
3012
|
+
runtime.sleepWaiter = resolve;
|
|
3013
|
+
});
|
|
3014
|
+
runtime.isSleeping = true;
|
|
3015
|
+
try {
|
|
3016
|
+
await awaitWithEviction(
|
|
3017
|
+
Promise.race([timeoutPromise, wakePromise]),
|
|
3018
|
+
abortSignal
|
|
3019
|
+
);
|
|
3020
|
+
} finally {
|
|
3021
|
+
runtime.isSleeping = false;
|
|
3022
|
+
runtime.sleepWaiter = void 0;
|
|
3023
|
+
timeoutHandle == null ? void 0 : timeoutHandle.abort();
|
|
3024
|
+
}
|
|
3025
|
+
if (abortSignal.aborted) {
|
|
3026
|
+
throw new EvictedError();
|
|
3027
|
+
}
|
|
3028
|
+
if (Date.now() >= deadline) {
|
|
3029
|
+
return;
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
async function executeLiveWorkflow(workflowId, workflowFn, input, driver, messageDriver, abortController, runtime, onHistoryUpdated, logger) {
|
|
3034
|
+
let lastResult;
|
|
3035
|
+
while (true) {
|
|
3036
|
+
const result = await executeWorkflow(
|
|
3037
|
+
workflowId,
|
|
3038
|
+
workflowFn,
|
|
3039
|
+
input,
|
|
3040
|
+
driver,
|
|
3041
|
+
messageDriver,
|
|
3042
|
+
abortController,
|
|
3043
|
+
onHistoryUpdated,
|
|
3044
|
+
logger
|
|
3045
|
+
);
|
|
3046
|
+
lastResult = result;
|
|
3047
|
+
if (result.state !== "sleeping") {
|
|
3048
|
+
return result;
|
|
3049
|
+
}
|
|
3050
|
+
const hasMessages = result.waitingForMessages !== void 0;
|
|
3051
|
+
const hasDeadline = result.sleepUntil !== void 0;
|
|
3052
|
+
if (hasMessages && hasDeadline) {
|
|
3053
|
+
try {
|
|
3054
|
+
const messagePromise = awaitWithEviction(
|
|
3055
|
+
driver.waitForMessages(
|
|
3056
|
+
result.waitingForMessages,
|
|
3057
|
+
abortController.signal
|
|
3058
|
+
),
|
|
3059
|
+
abortController.signal
|
|
3060
|
+
);
|
|
3061
|
+
const sleepPromise = waitForSleep(
|
|
3062
|
+
runtime,
|
|
3063
|
+
result.sleepUntil,
|
|
3064
|
+
abortController.signal
|
|
3065
|
+
);
|
|
3066
|
+
await Promise.race([messagePromise, sleepPromise]);
|
|
3067
|
+
} catch (error) {
|
|
3068
|
+
if (error instanceof EvictedError) {
|
|
3069
|
+
return lastResult;
|
|
3070
|
+
}
|
|
3071
|
+
throw error;
|
|
3072
|
+
}
|
|
3073
|
+
continue;
|
|
3074
|
+
}
|
|
3075
|
+
if (hasMessages) {
|
|
3076
|
+
try {
|
|
3077
|
+
await awaitWithEviction(
|
|
3078
|
+
driver.waitForMessages(
|
|
3079
|
+
result.waitingForMessages,
|
|
3080
|
+
abortController.signal
|
|
3081
|
+
),
|
|
3082
|
+
abortController.signal
|
|
3083
|
+
);
|
|
3084
|
+
} catch (error) {
|
|
3085
|
+
if (error instanceof EvictedError) {
|
|
3086
|
+
return lastResult;
|
|
3087
|
+
}
|
|
3088
|
+
throw error;
|
|
3089
|
+
}
|
|
3090
|
+
continue;
|
|
3091
|
+
}
|
|
3092
|
+
if (hasDeadline) {
|
|
3093
|
+
try {
|
|
3094
|
+
await waitForSleep(
|
|
3095
|
+
runtime,
|
|
3096
|
+
result.sleepUntil,
|
|
3097
|
+
abortController.signal
|
|
3098
|
+
);
|
|
3099
|
+
} catch (error) {
|
|
3100
|
+
if (error instanceof EvictedError) {
|
|
3101
|
+
return lastResult;
|
|
3102
|
+
}
|
|
3103
|
+
throw error;
|
|
3104
|
+
}
|
|
3105
|
+
continue;
|
|
3106
|
+
}
|
|
3107
|
+
return result;
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
function runWorkflow(workflowId, workflowFn, input, driver, options = {}) {
|
|
3111
|
+
const messageDriver = driver.messageDriver;
|
|
3112
|
+
const abortController = new AbortController();
|
|
3113
|
+
const mode = _nullishCoalesce(options.mode, () => ( "yield"));
|
|
3114
|
+
const liveRuntime = mode === "live" ? createLiveRuntime() : void 0;
|
|
3115
|
+
const logger = options.logger;
|
|
3116
|
+
const resultPromise = mode === "live" && liveRuntime ? executeLiveWorkflow(
|
|
3117
|
+
workflowId,
|
|
3118
|
+
workflowFn,
|
|
3119
|
+
input,
|
|
3120
|
+
driver,
|
|
3121
|
+
messageDriver,
|
|
3122
|
+
abortController,
|
|
3123
|
+
liveRuntime,
|
|
3124
|
+
options.onHistoryUpdated,
|
|
3125
|
+
logger
|
|
3126
|
+
) : executeWorkflow(
|
|
3127
|
+
workflowId,
|
|
3128
|
+
workflowFn,
|
|
3129
|
+
input,
|
|
3130
|
+
driver,
|
|
3131
|
+
messageDriver,
|
|
3132
|
+
abortController,
|
|
3133
|
+
options.onHistoryUpdated,
|
|
3134
|
+
logger
|
|
3135
|
+
);
|
|
3136
|
+
return {
|
|
3137
|
+
workflowId,
|
|
3138
|
+
result: resultPromise,
|
|
3139
|
+
async message(name, data) {
|
|
3140
|
+
const messageId = generateId();
|
|
3141
|
+
await messageDriver.addMessage({
|
|
3142
|
+
id: messageId,
|
|
3143
|
+
name,
|
|
3144
|
+
data,
|
|
3145
|
+
sentAt: Date.now()
|
|
3146
|
+
});
|
|
3147
|
+
},
|
|
3148
|
+
async wake() {
|
|
3149
|
+
if (liveRuntime) {
|
|
3150
|
+
if (liveRuntime.isSleeping && liveRuntime.sleepWaiter) {
|
|
3151
|
+
liveRuntime.sleepWaiter();
|
|
3152
|
+
}
|
|
3153
|
+
return;
|
|
3154
|
+
}
|
|
3155
|
+
await driver.setAlarm(workflowId, Date.now());
|
|
3156
|
+
},
|
|
3157
|
+
async recover() {
|
|
3158
|
+
const stateValue = await driver.get(buildWorkflowStateKey());
|
|
3159
|
+
const state = stateValue ? deserializeWorkflowState(stateValue) : "pending";
|
|
3160
|
+
if (state !== "failed") {
|
|
3161
|
+
return;
|
|
3162
|
+
}
|
|
3163
|
+
const metadataEntries = await driver.list(
|
|
3164
|
+
buildEntryMetadataPrefix()
|
|
3165
|
+
);
|
|
3166
|
+
const writes = [];
|
|
3167
|
+
for (const entry of metadataEntries) {
|
|
3168
|
+
const metadata = deserializeEntryMetadata(entry.value);
|
|
3169
|
+
if (metadata.status !== "failed" && metadata.status !== "exhausted") {
|
|
3170
|
+
continue;
|
|
3171
|
+
}
|
|
3172
|
+
metadata.status = "pending";
|
|
3173
|
+
metadata.attempts = 0;
|
|
3174
|
+
metadata.lastAttemptAt = 0;
|
|
3175
|
+
metadata.error = void 0;
|
|
3176
|
+
metadata.dirty = false;
|
|
3177
|
+
writes.push({
|
|
3178
|
+
key: entry.key,
|
|
3179
|
+
value: serializeEntryMetadata(metadata)
|
|
3180
|
+
});
|
|
3181
|
+
}
|
|
3182
|
+
if (writes.length > 0) {
|
|
3183
|
+
await driver.batch(writes);
|
|
3184
|
+
}
|
|
3185
|
+
await driver.delete(buildWorkflowErrorKey());
|
|
3186
|
+
await driver.set(
|
|
3187
|
+
buildWorkflowStateKey(),
|
|
3188
|
+
serializeWorkflowState("sleeping")
|
|
3189
|
+
);
|
|
3190
|
+
if (liveRuntime) {
|
|
3191
|
+
if (liveRuntime.isSleeping && liveRuntime.sleepWaiter) {
|
|
3192
|
+
liveRuntime.sleepWaiter();
|
|
3193
|
+
}
|
|
3194
|
+
return;
|
|
3195
|
+
}
|
|
3196
|
+
await driver.setAlarm(workflowId, Date.now());
|
|
3197
|
+
},
|
|
3198
|
+
evict() {
|
|
3199
|
+
abortController.abort(new EvictedError());
|
|
3200
|
+
},
|
|
3201
|
+
async cancel() {
|
|
3202
|
+
abortController.abort(new EvictedError());
|
|
3203
|
+
await driver.set(
|
|
3204
|
+
buildWorkflowStateKey(),
|
|
3205
|
+
serializeWorkflowState("cancelled")
|
|
3206
|
+
);
|
|
3207
|
+
await driver.clearAlarm(workflowId);
|
|
3208
|
+
},
|
|
3209
|
+
async getOutput() {
|
|
3210
|
+
const value = await driver.get(buildWorkflowOutputKey());
|
|
3211
|
+
if (!value) {
|
|
3212
|
+
return void 0;
|
|
3213
|
+
}
|
|
3214
|
+
return deserializeWorkflowOutput(value);
|
|
3215
|
+
},
|
|
3216
|
+
async getState() {
|
|
3217
|
+
const value = await driver.get(buildWorkflowStateKey());
|
|
3218
|
+
if (!value) {
|
|
3219
|
+
return "pending";
|
|
3220
|
+
}
|
|
3221
|
+
return deserializeWorkflowState(value);
|
|
3222
|
+
}
|
|
3223
|
+
};
|
|
3224
|
+
}
|
|
3225
|
+
async function executeWorkflow(workflowId, workflowFn, input, driver, messageDriver, abortController, onHistoryUpdated, logger) {
|
|
3226
|
+
var _a;
|
|
3227
|
+
const storage = await loadStorage(driver);
|
|
3228
|
+
const historyNotifier = onHistoryUpdated ? () => onHistoryUpdated(createHistorySnapshot(storage)) : void 0;
|
|
3229
|
+
if (historyNotifier) {
|
|
3230
|
+
historyNotifier();
|
|
3231
|
+
}
|
|
3232
|
+
if (logger) {
|
|
3233
|
+
const entryKeys = Array.from(storage.history.entries.keys());
|
|
3234
|
+
logger.debug({
|
|
3235
|
+
msg: "loaded workflow storage",
|
|
3236
|
+
state: storage.state,
|
|
3237
|
+
entryCount: entryKeys.length,
|
|
3238
|
+
entries: entryKeys.slice(0, 10),
|
|
3239
|
+
nameRegistry: storage.nameRegistry
|
|
3240
|
+
});
|
|
3241
|
+
}
|
|
3242
|
+
if (storage.state === "cancelled") {
|
|
3243
|
+
throw new EvictedError();
|
|
3244
|
+
}
|
|
3245
|
+
const storedInputBytes = await driver.get(buildWorkflowInputKey());
|
|
3246
|
+
let effectiveInput;
|
|
3247
|
+
if (storedInputBytes) {
|
|
3248
|
+
effectiveInput = deserializeWorkflowInput(storedInputBytes);
|
|
3249
|
+
} else {
|
|
3250
|
+
effectiveInput = input;
|
|
3251
|
+
await driver.set(
|
|
3252
|
+
buildWorkflowInputKey(),
|
|
3253
|
+
serializeWorkflowInput(input)
|
|
3254
|
+
);
|
|
3255
|
+
}
|
|
3256
|
+
if (storage.state === "rolling_back") {
|
|
3257
|
+
try {
|
|
3258
|
+
await executeRollback(
|
|
3259
|
+
workflowId,
|
|
3260
|
+
workflowFn,
|
|
3261
|
+
effectiveInput,
|
|
3262
|
+
driver,
|
|
3263
|
+
messageDriver,
|
|
3264
|
+
abortController,
|
|
3265
|
+
storage,
|
|
3266
|
+
historyNotifier,
|
|
3267
|
+
logger
|
|
3268
|
+
);
|
|
3269
|
+
} catch (error) {
|
|
3270
|
+
if (error instanceof EvictedError) {
|
|
3271
|
+
return { state: storage.state };
|
|
3272
|
+
}
|
|
3273
|
+
throw error;
|
|
3274
|
+
}
|
|
3275
|
+
storage.state = "failed";
|
|
3276
|
+
await flush(storage, driver, historyNotifier);
|
|
3277
|
+
const storedError = storage.error ? new Error(storage.error.message) : new Error("Workflow failed");
|
|
3278
|
+
if ((_a = storage.error) == null ? void 0 : _a.name) {
|
|
3279
|
+
storedError.name = storage.error.name;
|
|
3280
|
+
}
|
|
3281
|
+
throw storedError;
|
|
3282
|
+
}
|
|
3283
|
+
const ctx = new WorkflowContextImpl(
|
|
3284
|
+
workflowId,
|
|
3285
|
+
storage,
|
|
3286
|
+
driver,
|
|
3287
|
+
messageDriver,
|
|
3288
|
+
void 0,
|
|
3289
|
+
abortController,
|
|
3290
|
+
"forward",
|
|
3291
|
+
void 0,
|
|
3292
|
+
false,
|
|
3293
|
+
historyNotifier,
|
|
3294
|
+
logger
|
|
3295
|
+
);
|
|
3296
|
+
storage.state = "running";
|
|
3297
|
+
try {
|
|
3298
|
+
const output = await workflowFn(ctx, effectiveInput);
|
|
3299
|
+
storage.state = "completed";
|
|
3300
|
+
storage.output = output;
|
|
3301
|
+
await flush(storage, driver, historyNotifier);
|
|
3302
|
+
await driver.clearAlarm(workflowId);
|
|
3303
|
+
return { state: "completed", output };
|
|
3304
|
+
} catch (error) {
|
|
3305
|
+
if (error instanceof SleepError) {
|
|
3306
|
+
return await setSleepState(
|
|
3307
|
+
storage,
|
|
3308
|
+
driver,
|
|
3309
|
+
workflowId,
|
|
3310
|
+
error.deadline,
|
|
3311
|
+
error.messageNames,
|
|
3312
|
+
historyNotifier
|
|
3313
|
+
);
|
|
3314
|
+
}
|
|
3315
|
+
if (error instanceof MessageWaitError) {
|
|
3316
|
+
return await setMessageWaitState(
|
|
3317
|
+
storage,
|
|
3318
|
+
driver,
|
|
3319
|
+
error.messageNames,
|
|
3320
|
+
historyNotifier
|
|
3321
|
+
);
|
|
3322
|
+
}
|
|
3323
|
+
if (error instanceof EvictedError) {
|
|
3324
|
+
return await setEvictedState(storage, driver, historyNotifier);
|
|
3325
|
+
}
|
|
3326
|
+
if (error instanceof StepFailedError) {
|
|
3327
|
+
return await setRetryState(
|
|
3328
|
+
storage,
|
|
3329
|
+
driver,
|
|
3330
|
+
workflowId,
|
|
3331
|
+
historyNotifier
|
|
3332
|
+
);
|
|
3333
|
+
}
|
|
3334
|
+
if (error instanceof RollbackCheckpointError) {
|
|
3335
|
+
await setFailedState(storage, driver, error, historyNotifier);
|
|
3336
|
+
throw error;
|
|
3337
|
+
}
|
|
3338
|
+
storage.error = extractErrorInfo(error);
|
|
3339
|
+
storage.state = "rolling_back";
|
|
3340
|
+
await flush(storage, driver, historyNotifier);
|
|
3341
|
+
try {
|
|
3342
|
+
await executeRollback(
|
|
3343
|
+
workflowId,
|
|
3344
|
+
workflowFn,
|
|
3345
|
+
effectiveInput,
|
|
3346
|
+
driver,
|
|
3347
|
+
messageDriver,
|
|
3348
|
+
abortController,
|
|
3349
|
+
storage,
|
|
3350
|
+
historyNotifier,
|
|
3351
|
+
logger
|
|
3352
|
+
);
|
|
3353
|
+
} catch (rollbackError) {
|
|
3354
|
+
if (rollbackError instanceof EvictedError) {
|
|
3355
|
+
return { state: storage.state };
|
|
3356
|
+
}
|
|
3357
|
+
throw rollbackError;
|
|
3358
|
+
}
|
|
3359
|
+
storage.state = "failed";
|
|
3360
|
+
await flush(storage, driver, historyNotifier);
|
|
3361
|
+
throw error;
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
function extractErrorInfo(error) {
|
|
3365
|
+
if (error instanceof Error) {
|
|
3366
|
+
const result = {
|
|
3367
|
+
name: error.name,
|
|
3368
|
+
message: error.message,
|
|
3369
|
+
stack: error.stack
|
|
3370
|
+
};
|
|
3371
|
+
const metadata = {};
|
|
3372
|
+
for (const key of Object.keys(error)) {
|
|
3373
|
+
if (key !== "name" && key !== "message" && key !== "stack") {
|
|
3374
|
+
const value = error[key];
|
|
3375
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
3376
|
+
metadata[key] = value;
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
if (Object.keys(metadata).length > 0) {
|
|
3381
|
+
result.metadata = metadata;
|
|
3382
|
+
}
|
|
3383
|
+
return result;
|
|
3384
|
+
}
|
|
3385
|
+
return {
|
|
3386
|
+
name: "Error",
|
|
3387
|
+
message: String(error)
|
|
3388
|
+
};
|
|
3389
|
+
}
|
|
3390
|
+
|
|
3391
|
+
|
|
3392
|
+
|
|
3393
|
+
|
|
3394
|
+
|
|
3395
|
+
|
|
3396
|
+
|
|
3397
|
+
|
|
3398
|
+
|
|
3399
|
+
|
|
3400
|
+
|
|
3401
|
+
|
|
3402
|
+
|
|
3403
|
+
|
|
3404
|
+
|
|
3405
|
+
|
|
3406
|
+
|
|
3407
|
+
|
|
3408
|
+
|
|
3409
|
+
|
|
3410
|
+
|
|
3411
|
+
|
|
3412
|
+
|
|
3413
|
+
|
|
3414
|
+
|
|
3415
|
+
|
|
3416
|
+
|
|
3417
|
+
|
|
3418
|
+
|
|
3419
|
+
|
|
3420
|
+
|
|
3421
|
+
|
|
3422
|
+
|
|
3423
|
+
|
|
3424
|
+
|
|
3425
|
+
|
|
3426
|
+
|
|
3427
|
+
|
|
3428
|
+
|
|
3429
|
+
|
|
3430
|
+
|
|
3431
|
+
|
|
3432
|
+
|
|
3433
|
+
|
|
3434
|
+
|
|
3435
|
+
|
|
3436
|
+
|
|
3437
|
+
|
|
3438
|
+
|
|
3439
|
+
|
|
3440
|
+
exports.CriticalError = CriticalError; exports.RollbackError = RollbackError; exports.RollbackCheckpointError = RollbackCheckpointError; exports.SleepError = SleepError; exports.MessageWaitError = MessageWaitError; exports.EvictedError = EvictedError; exports.HistoryDivergedError = HistoryDivergedError; exports.StepExhaustedError = StepExhaustedError; exports.StepFailedError = StepFailedError; exports.JoinError = JoinError; exports.RaceError = RaceError; exports.CancelledError = CancelledError; exports.EntryInProgressError = EntryInProgressError; exports.isLoopIterationMarker = isLoopIterationMarker; exports.registerName = registerName; exports.resolveName = resolveName; exports.locationToKey = locationToKey; exports.appendName = appendName; exports.appendLoopIteration = appendLoopIteration; exports.emptyLocation = emptyLocation; exports.parentLocation = parentLocation; exports.isLocationPrefix = isLocationPrefix; exports.locationsEqual = locationsEqual; exports.keyStartsWith = keyStartsWith; exports.compareKeys = compareKeys; exports.keyToHex = keyToHex; exports.createStorage = createStorage; exports.createHistorySnapshot = createHistorySnapshot; exports.generateId = generateId; exports.createEntry = createEntry; exports.getOrCreateMetadata = getOrCreateMetadata; exports.loadStorage = loadStorage; exports.loadMetadata = loadMetadata; exports.flush = flush; exports.deleteEntriesWithPrefix = deleteEntriesWithPrefix; exports.getEntry = getEntry; exports.setEntry = setEntry; exports.sleep = sleep; exports.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES; exports.DEFAULT_RETRY_BACKOFF_BASE = DEFAULT_RETRY_BACKOFF_BASE; exports.DEFAULT_RETRY_BACKOFF_MAX = DEFAULT_RETRY_BACKOFF_MAX; exports.DEFAULT_LOOP_COMMIT_INTERVAL = DEFAULT_LOOP_COMMIT_INTERVAL; exports.DEFAULT_LOOP_HISTORY_EVERY = DEFAULT_LOOP_HISTORY_EVERY; exports.DEFAULT_LOOP_HISTORY_KEEP = DEFAULT_LOOP_HISTORY_KEEP; exports.DEFAULT_STEP_TIMEOUT = DEFAULT_STEP_TIMEOUT; exports.WorkflowContextImpl = WorkflowContextImpl; exports.Loop = Loop; exports.runWorkflow = runWorkflow;
|
|
3441
|
+
//# sourceMappingURL=chunk-GJ66YE5W.cjs.map
|