@rivetkit/workflow-engine 0.0.0-main.14140ce → 0.0.0-main.17f6330
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tsup/{chunk-4SWXLWKL.cjs → chunk-GQWOLYBA.cjs} +139 -91
- package/dist/tsup/chunk-GQWOLYBA.cjs.map +1 -0
- package/dist/tsup/{chunk-UMFB2AR3.js → chunk-KA2T56AJ.js} +139 -91
- package/dist/tsup/chunk-KA2T56AJ.js.map +1 -0
- package/dist/tsup/index.cjs +2 -2
- package/dist/tsup/index.d.cts +22 -12
- package/dist/tsup/index.d.ts +22 -12
- package/dist/tsup/index.js +1 -1
- package/dist/tsup/testing.cjs +23 -23
- package/dist/tsup/testing.js +1 -1
- package/package.json +1 -1
- package/schemas/serde.ts +1 -3
- package/src/context.ts +59 -60
- package/src/index.ts +41 -21
- package/src/location.ts +1 -1
- package/src/storage.ts +50 -3
- package/src/types.ts +10 -6
- package/dist/tsup/chunk-4SWXLWKL.cjs.map +0 -1
- package/dist/tsup/chunk-UMFB2AR3.js.map +0 -1
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export {
|
|
|
13
13
|
} from "./context.js";
|
|
14
14
|
// Driver
|
|
15
15
|
export type { EngineDriver, KVEntry, KVWrite } from "./driver.js";
|
|
16
|
+
export { extractErrorInfo } from "./error-utils.js";
|
|
16
17
|
// Errors
|
|
17
18
|
export {
|
|
18
19
|
CancelledError,
|
|
@@ -29,7 +30,6 @@ export {
|
|
|
29
30
|
StepExhaustedError,
|
|
30
31
|
StepFailedError,
|
|
31
32
|
} from "./errors.js";
|
|
32
|
-
export { extractErrorInfo } from "./error-utils.js";
|
|
33
33
|
|
|
34
34
|
// Location utilities
|
|
35
35
|
export {
|
|
@@ -82,9 +82,6 @@ export type {
|
|
|
82
82
|
PathSegment,
|
|
83
83
|
RaceEntry,
|
|
84
84
|
RemovedEntry,
|
|
85
|
-
WorkflowEntryMetadataSnapshot,
|
|
86
|
-
WorkflowHistoryEntry,
|
|
87
|
-
WorkflowHistorySnapshot,
|
|
88
85
|
RollbackCheckpointEntry,
|
|
89
86
|
RollbackContextInterface,
|
|
90
87
|
RunWorkflowOptions,
|
|
@@ -102,20 +99,23 @@ export type {
|
|
|
102
99
|
TryStepFailure,
|
|
103
100
|
TryStepResult,
|
|
104
101
|
WorkflowContextInterface,
|
|
102
|
+
WorkflowEntryMetadataSnapshot,
|
|
105
103
|
WorkflowError,
|
|
106
104
|
WorkflowErrorEvent,
|
|
107
105
|
WorkflowErrorHandler,
|
|
108
106
|
WorkflowFunction,
|
|
109
107
|
WorkflowHandle,
|
|
110
|
-
|
|
108
|
+
WorkflowHistoryEntry,
|
|
109
|
+
WorkflowHistorySnapshot,
|
|
110
|
+
WorkflowMessageDriver,
|
|
111
111
|
WorkflowQueue,
|
|
112
112
|
WorkflowQueueMessage,
|
|
113
113
|
WorkflowQueueNextBatchOptions,
|
|
114
114
|
WorkflowQueueNextOptions,
|
|
115
|
-
WorkflowMessageDriver,
|
|
116
115
|
WorkflowResult,
|
|
117
|
-
|
|
116
|
+
WorkflowRollbackErrorEvent,
|
|
118
117
|
WorkflowRunErrorEvent,
|
|
118
|
+
WorkflowRunMode,
|
|
119
119
|
WorkflowState,
|
|
120
120
|
WorkflowStepErrorEvent,
|
|
121
121
|
} from "./types.js";
|
|
@@ -185,9 +185,9 @@ import type {
|
|
|
185
185
|
RunWorkflowOptions,
|
|
186
186
|
Storage,
|
|
187
187
|
WorkflowErrorEvent,
|
|
188
|
-
WorkflowHistorySnapshot,
|
|
189
188
|
WorkflowFunction,
|
|
190
189
|
WorkflowHandle,
|
|
190
|
+
WorkflowHistorySnapshot,
|
|
191
191
|
WorkflowMessageDriver,
|
|
192
192
|
WorkflowResult,
|
|
193
193
|
WorkflowRunMode,
|
|
@@ -532,26 +532,46 @@ async function executeLiveWorkflow<TInput, TOutput>(
|
|
|
532
532
|
const hasDeadline = result.sleepUntil !== undefined;
|
|
533
533
|
|
|
534
534
|
if (hasMessages && hasDeadline) {
|
|
535
|
-
// Wait for EITHER a message OR the deadline (for queue.next timeout)
|
|
535
|
+
// Wait for EITHER a message OR the deadline (for queue.next timeout).
|
|
536
|
+
// Scope both waiters to a per-iteration controller chained to the run
|
|
537
|
+
// signal so that whichever loses the race is cancelled immediately.
|
|
538
|
+
// Otherwise the loser (a message waiter or an armed sleep timer) stays
|
|
539
|
+
// registered on the long-lived run signal and leaks once per cycle.
|
|
540
|
+
const iterationAbort = new AbortController();
|
|
541
|
+
const onRunAbort = () => iterationAbort.abort();
|
|
542
|
+
if (abortController.signal.aborted) {
|
|
543
|
+
iterationAbort.abort();
|
|
544
|
+
} else {
|
|
545
|
+
abortController.signal.addEventListener("abort", onRunAbort, {
|
|
546
|
+
once: true,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
const messagePromise = awaitWithEviction(
|
|
550
|
+
driver.waitForMessages(
|
|
551
|
+
result.waitingForMessages!,
|
|
552
|
+
iterationAbort.signal,
|
|
553
|
+
),
|
|
554
|
+
iterationAbort.signal,
|
|
555
|
+
);
|
|
556
|
+
const sleepPromise = waitForSleep(
|
|
557
|
+
runtime,
|
|
558
|
+
result.sleepUntil!,
|
|
559
|
+
iterationAbort.signal,
|
|
560
|
+
);
|
|
561
|
+
// Swallow the loser's rejection once it is aborted below so it does
|
|
562
|
+
// not surface as an unhandled rejection.
|
|
563
|
+
messagePromise.catch(() => {});
|
|
564
|
+
sleepPromise.catch(() => {});
|
|
536
565
|
try {
|
|
537
|
-
const messagePromise = awaitWithEviction(
|
|
538
|
-
driver.waitForMessages(
|
|
539
|
-
result.waitingForMessages!,
|
|
540
|
-
abortController.signal,
|
|
541
|
-
),
|
|
542
|
-
abortController.signal,
|
|
543
|
-
);
|
|
544
|
-
const sleepPromise = waitForSleep(
|
|
545
|
-
runtime,
|
|
546
|
-
result.sleepUntil!,
|
|
547
|
-
abortController.signal,
|
|
548
|
-
);
|
|
549
566
|
await Promise.race([messagePromise, sleepPromise]);
|
|
550
567
|
} catch (error) {
|
|
551
568
|
if (error instanceof EvictedError) {
|
|
552
569
|
return lastResult;
|
|
553
570
|
}
|
|
554
571
|
throw error;
|
|
572
|
+
} finally {
|
|
573
|
+
iterationAbort.abort();
|
|
574
|
+
abortController.signal.removeEventListener("abort", onRunAbort);
|
|
555
575
|
}
|
|
556
576
|
continue;
|
|
557
577
|
}
|
package/src/location.ts
CHANGED
|
@@ -159,7 +159,7 @@ export function getChildEntries(
|
|
|
159
159
|
const isChild =
|
|
160
160
|
parentKey === ""
|
|
161
161
|
? true
|
|
162
|
-
: key.startsWith(parentKey
|
|
162
|
+
: key.startsWith(`${parentKey}/`) || key === parentKey;
|
|
163
163
|
|
|
164
164
|
if (isChild) {
|
|
165
165
|
// Return the actual entry's location, not the parent location
|
package/src/storage.ts
CHANGED
|
@@ -40,6 +40,9 @@ import type {
|
|
|
40
40
|
WorkflowHistorySnapshot,
|
|
41
41
|
} from "./types.js";
|
|
42
42
|
|
|
43
|
+
export const MAX_KV_BATCH_ENTRIES = 128;
|
|
44
|
+
export const MAX_KV_BATCH_PAYLOAD_BYTES = 976 * 1024;
|
|
45
|
+
|
|
43
46
|
/**
|
|
44
47
|
* Create an empty storage instance.
|
|
45
48
|
*/
|
|
@@ -238,6 +241,8 @@ export async function flush(
|
|
|
238
241
|
pendingDeletions?: PendingDeletions,
|
|
239
242
|
): Promise<void> {
|
|
240
243
|
const writes: KVWrite[] = [];
|
|
244
|
+
const dirtyEntries: Entry[] = [];
|
|
245
|
+
const dirtyMetadata: EntryMetadata[] = [];
|
|
241
246
|
let historyUpdated = false;
|
|
242
247
|
|
|
243
248
|
// Flush only new names (those added since last flush)
|
|
@@ -263,7 +268,7 @@ export async function flush(
|
|
|
263
268
|
key: buildHistoryKey(entry.location),
|
|
264
269
|
value: serializeEntry(entry),
|
|
265
270
|
});
|
|
266
|
-
entry
|
|
271
|
+
dirtyEntries.push(entry);
|
|
267
272
|
historyUpdated = true;
|
|
268
273
|
}
|
|
269
274
|
}
|
|
@@ -275,7 +280,7 @@ export async function flush(
|
|
|
275
280
|
key: buildEntryMetadataKey(id),
|
|
276
281
|
value: serializeEntryMetadata(metadata),
|
|
277
282
|
});
|
|
278
|
-
metadata
|
|
283
|
+
dirtyMetadata.push(metadata);
|
|
279
284
|
historyUpdated = true;
|
|
280
285
|
}
|
|
281
286
|
}
|
|
@@ -313,7 +318,9 @@ export async function flush(
|
|
|
313
318
|
}
|
|
314
319
|
|
|
315
320
|
if (writes.length > 0) {
|
|
316
|
-
|
|
321
|
+
for (const chunk of splitBatchWrites(writes)) {
|
|
322
|
+
await driver.batch(chunk);
|
|
323
|
+
}
|
|
317
324
|
}
|
|
318
325
|
|
|
319
326
|
// Apply pending deletions after the batch write. These are collected
|
|
@@ -336,6 +343,12 @@ export async function flush(
|
|
|
336
343
|
}
|
|
337
344
|
|
|
338
345
|
// Update flushed tracking after successful write
|
|
346
|
+
for (const entry of dirtyEntries) {
|
|
347
|
+
entry.dirty = false;
|
|
348
|
+
}
|
|
349
|
+
for (const metadata of dirtyMetadata) {
|
|
350
|
+
metadata.dirty = false;
|
|
351
|
+
}
|
|
339
352
|
storage.flushedNameCount = storage.nameRegistry.length;
|
|
340
353
|
storage.flushedState = storage.state;
|
|
341
354
|
storage.flushedOutput = storage.output;
|
|
@@ -346,6 +359,40 @@ export async function flush(
|
|
|
346
359
|
}
|
|
347
360
|
}
|
|
348
361
|
|
|
362
|
+
function splitBatchWrites(writes: KVWrite[]): KVWrite[][] {
|
|
363
|
+
const chunks: KVWrite[][] = [];
|
|
364
|
+
let chunk: KVWrite[] = [];
|
|
365
|
+
let chunkBytes = 0;
|
|
366
|
+
|
|
367
|
+
for (const write of writes) {
|
|
368
|
+
const writeBytes = write.key.byteLength + write.value.byteLength;
|
|
369
|
+
if (writeBytes > MAX_KV_BATCH_PAYLOAD_BYTES) {
|
|
370
|
+
throw new Error(
|
|
371
|
+
`KV batch write is ${writeBytes} bytes, exceeding the ${MAX_KV_BATCH_PAYLOAD_BYTES} byte limit`,
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (
|
|
376
|
+
chunk.length >= MAX_KV_BATCH_ENTRIES ||
|
|
377
|
+
(chunk.length > 0 &&
|
|
378
|
+
chunkBytes + writeBytes > MAX_KV_BATCH_PAYLOAD_BYTES)
|
|
379
|
+
) {
|
|
380
|
+
chunks.push(chunk);
|
|
381
|
+
chunk = [];
|
|
382
|
+
chunkBytes = 0;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
chunk.push(write);
|
|
386
|
+
chunkBytes += writeBytes;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (chunk.length > 0) {
|
|
390
|
+
chunks.push(chunk);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return chunks;
|
|
394
|
+
}
|
|
395
|
+
|
|
349
396
|
/**
|
|
350
397
|
* Delete entries with a given location prefix (used for loop forgetting).
|
|
351
398
|
* Also cleans up associated metadata from both memory and driver.
|
package/src/types.ts
CHANGED
|
@@ -399,6 +399,8 @@ export interface StepConfig<T> {
|
|
|
399
399
|
retryBackoffMax?: number;
|
|
400
400
|
/** Timeout in ms for step execution (default: 30000). Set to 0 to disable. */
|
|
401
401
|
timeout?: number;
|
|
402
|
+
/** If true, step timeouts retry like any other error instead of failing immediately as critical. Default: false. */
|
|
403
|
+
retryOnTimeout?: boolean;
|
|
402
404
|
}
|
|
403
405
|
|
|
404
406
|
export type TryStepCatchKind =
|
|
@@ -422,11 +424,7 @@ export interface TryStepConfig<T> extends StepConfig<T> {
|
|
|
422
424
|
catch?: readonly TryStepCatchKind[];
|
|
423
425
|
}
|
|
424
426
|
|
|
425
|
-
export type TryBlockCatchKind =
|
|
426
|
-
| "step"
|
|
427
|
-
| "join"
|
|
428
|
-
| "race"
|
|
429
|
-
| "rollback";
|
|
427
|
+
export type TryBlockCatchKind = "step" | "join" | "race" | "rollback";
|
|
430
428
|
|
|
431
429
|
export interface TryBlockFailure {
|
|
432
430
|
source: "step" | "join" | "race" | "block";
|
|
@@ -459,7 +457,7 @@ export type LoopResult<S, T> =
|
|
|
459
457
|
* `Loop.continue(undefined)`.
|
|
460
458
|
*/
|
|
461
459
|
export type LoopIterationResult<S, T> = Promise<
|
|
462
|
-
LoopResult<S, T> | (S extends undefined ?
|
|
460
|
+
LoopResult<S, T> | (S extends undefined ? undefined : never)
|
|
463
461
|
>;
|
|
464
462
|
|
|
465
463
|
/**
|
|
@@ -473,6 +471,12 @@ export interface LoopConfig<S, T> {
|
|
|
473
471
|
historyPruneInterval?: number;
|
|
474
472
|
/** Number of past iterations to retain when pruning. Defaults to historyPruneInterval. */
|
|
475
473
|
historySize?: number;
|
|
474
|
+
/** @deprecated Use historyPruneInterval. */
|
|
475
|
+
commitInterval?: number;
|
|
476
|
+
/** @deprecated Use historyPruneInterval. */
|
|
477
|
+
historyEvery?: number;
|
|
478
|
+
/** @deprecated Use historySize. */
|
|
479
|
+
historyKeep?: number;
|
|
476
480
|
}
|
|
477
481
|
|
|
478
482
|
/**
|