@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/src/driver.ts ADDED
@@ -0,0 +1,103 @@
1
+ import type { WorkflowMessageDriver } from "./types.js";
2
+
3
+ /**
4
+ * A key-value entry returned from list operations.
5
+ */
6
+ export interface KVEntry {
7
+ key: Uint8Array;
8
+ value: Uint8Array;
9
+ }
10
+
11
+ /**
12
+ * A write operation for batch writes.
13
+ */
14
+ export interface KVWrite {
15
+ key: Uint8Array;
16
+ value: Uint8Array;
17
+ }
18
+
19
+ /**
20
+ * The engine driver provides the KV and scheduling interface.
21
+ * Implementations must provide these methods to integrate with different backends.
22
+ *
23
+ * IMPORTANT: Each workflow instance must have its own isolated driver/KV namespace.
24
+ * The workflow engine is the sole reader/writer of its KV during execution.
25
+ * KV operations do not include workflow IDs because isolation is provided externally
26
+ * by the host system (e.g., Cloudflare Durable Objects, dedicated actor processes).
27
+ *
28
+ * External systems may only enqueue messages through the configured message driver
29
+ * (via WorkflowHandle.message()).
30
+ * See architecture.md "Isolation Model" for details.
31
+ */
32
+ export interface EngineDriver {
33
+ // === KV Operations ===
34
+
35
+ /**
36
+ * Get a value by key.
37
+ * Returns null if the key doesn't exist.
38
+ */
39
+ get(key: Uint8Array): Promise<Uint8Array | null>;
40
+
41
+ /**
42
+ * Set a value by key.
43
+ */
44
+ set(key: Uint8Array, value: Uint8Array): Promise<void>;
45
+
46
+ /**
47
+ * Delete a key.
48
+ */
49
+ delete(key: Uint8Array): Promise<void>;
50
+
51
+ /**
52
+ * Delete all keys with a given prefix.
53
+ */
54
+ deletePrefix(prefix: Uint8Array): Promise<void>;
55
+
56
+ /**
57
+ * List all key-value pairs with a given prefix.
58
+ *
59
+ * IMPORTANT: Results MUST be sorted by key in lexicographic byte order.
60
+ * The workflow engine relies on this ordering for deterministic history
61
+ * replay and name registry reconstruction. Failing to sort will cause
62
+ * non-deterministic replay behavior.
63
+ */
64
+ list(prefix: Uint8Array): Promise<KVEntry[]>;
65
+
66
+ /**
67
+ * Batch write multiple key-value pairs.
68
+ * Should be atomic if possible.
69
+ */
70
+ batch(writes: KVWrite[]): Promise<void>;
71
+
72
+ // === Scheduling ===
73
+
74
+ /**
75
+ * Set an alarm to wake the workflow at a specific time.
76
+ * @param workflowId The workflow to wake
77
+ * @param wakeAt Timestamp in milliseconds when to wake
78
+ */
79
+ setAlarm(workflowId: string, wakeAt: number): Promise<void>;
80
+
81
+ /**
82
+ * Clear any pending alarm for a workflow.
83
+ */
84
+ clearAlarm(workflowId: string): Promise<void>;
85
+
86
+ /**
87
+ * How often the worker polls for work (in milliseconds).
88
+ * Affects the threshold for in-memory vs scheduled sleeps.
89
+ */
90
+ readonly workerPollInterval: number;
91
+
92
+ /** Queue-backed message driver used for workflow messaging. */
93
+ readonly messageDriver: WorkflowMessageDriver;
94
+
95
+ /**
96
+ * Wait for incoming messages when running in live mode.
97
+ * Implementations should resolve when any of the specified message names are available.
98
+ */
99
+ waitForMessages(
100
+ messageNames: string[],
101
+ abortSignal: AbortSignal,
102
+ ): Promise<void>;
103
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Thrown from steps to prevent retry.
3
+ * Use this when an error is unrecoverable and retrying would be pointless.
4
+ */
5
+ export class CriticalError extends Error {
6
+ constructor(message: string) {
7
+ super(message);
8
+ this.name = "CriticalError";
9
+ }
10
+ }
11
+
12
+ /**
13
+ * Thrown from steps to force rollback without retry.
14
+ */
15
+ export class RollbackError extends Error {
16
+ constructor(message: string) {
17
+ super(message);
18
+ this.name = "RollbackError";
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Thrown when rollback is used without a checkpoint.
24
+ */
25
+ export class RollbackCheckpointError extends Error {
26
+ constructor() {
27
+ super("Rollback requires a checkpoint before any rollback step");
28
+ this.name = "RollbackCheckpointError";
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Internal: Workflow should sleep until deadline.
34
+ * This is thrown to yield control back to the scheduler.
35
+ * Optionally, the workflow can also wake early if certain messages arrive.
36
+ */
37
+ export class SleepError extends Error {
38
+ constructor(
39
+ public readonly deadline: number,
40
+ public readonly messageNames?: string[],
41
+ ) {
42
+ super(
43
+ messageNames && messageNames.length > 0
44
+ ? `Sleeping until ${deadline} or messages: ${messageNames.join(", ")}`
45
+ : `Sleeping until ${deadline}`,
46
+ );
47
+ this.name = "SleepError";
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Internal: Workflow is waiting for messages.
53
+ * This is thrown to yield control back to the scheduler.
54
+ */
55
+ export class MessageWaitError extends Error {
56
+ constructor(public readonly messageNames: string[]) {
57
+ super(`Waiting for messages: ${messageNames.join(", ")}`);
58
+ this.name = "MessageWaitError";
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Internal: Workflow was evicted.
64
+ * This is thrown when the workflow is being gracefully stopped.
65
+ */
66
+ export class EvictedError extends Error {
67
+ constructor() {
68
+ super("Workflow evicted");
69
+ this.name = "EvictedError";
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Internal: Stop rollback traversal.
75
+ */
76
+ export class RollbackStopError extends Error {
77
+ constructor() {
78
+ super("Rollback traversal halted");
79
+ this.name = "RollbackStopError";
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Workflow code changed incompatibly.
85
+ * Thrown when history doesn't match the current workflow code.
86
+ */
87
+ export class HistoryDivergedError extends Error {
88
+ constructor(message: string) {
89
+ super(message);
90
+ this.name = "HistoryDivergedError";
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Step exhausted all retries.
96
+ */
97
+ export class StepExhaustedError extends Error {
98
+ constructor(
99
+ public readonly stepName: string,
100
+ public readonly lastError?: string,
101
+ ) {
102
+ super(
103
+ `Step "${stepName}" exhausted retries: ${lastError ?? "unknown error"}`,
104
+ );
105
+ this.name = "StepExhaustedError";
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Step failed (will be retried).
111
+ * Internal error used to trigger retry logic.
112
+ */
113
+ export class StepFailedError extends Error {
114
+ constructor(
115
+ public readonly stepName: string,
116
+ public readonly originalError: unknown,
117
+ public readonly attempts: number,
118
+ ) {
119
+ super(`Step "${stepName}" failed (attempt ${attempts})`);
120
+ this.name = "StepFailedError";
121
+ this.cause = originalError;
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Join had branch failures.
127
+ */
128
+ export class JoinError extends Error {
129
+ constructor(public readonly errors: Record<string, Error>) {
130
+ super(`Join failed: ${Object.keys(errors).join(", ")}`);
131
+ this.name = "JoinError";
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Race had all branches fail.
137
+ */
138
+ export class RaceError extends Error {
139
+ constructor(
140
+ message: string,
141
+ public readonly errors: Array<{ name: string; error: string }>,
142
+ ) {
143
+ super(message);
144
+ this.name = "RaceError";
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Branch was cancelled (used by race).
150
+ */
151
+ export class CancelledError extends Error {
152
+ constructor() {
153
+ super("Branch cancelled");
154
+ this.name = "CancelledError";
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Entry is currently being processed.
160
+ * Thrown when user forgets to await a step.
161
+ */
162
+ export class EntryInProgressError extends Error {
163
+ constructor() {
164
+ super(
165
+ "Cannot start a new workflow entry while another is in progress. " +
166
+ "Did you forget to await the previous step/loop/sleep?",
167
+ );
168
+ this.name = "EntryInProgressError";
169
+ }
170
+ }