@workflow/core 4.0.1-beta.0
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.md +21 -0
- package/README.md +3 -0
- package/dist/builtins.d.ts +4 -0
- package/dist/builtins.d.ts.map +1 -0
- package/dist/builtins.js +13 -0
- package/dist/builtins.js.map +1 -0
- package/dist/create-hook.d.ts +123 -0
- package/dist/create-hook.d.ts.map +1 -0
- package/dist/create-hook.js +32 -0
- package/dist/create-hook.js.map +1 -0
- package/dist/define-hook.d.ts +53 -0
- package/dist/define-hook.d.ts.map +1 -0
- package/dist/define-hook.js +59 -0
- package/dist/define-hook.js.map +1 -0
- package/dist/events-consumer.d.ts +35 -0
- package/dist/events-consumer.d.ts.map +1 -0
- package/dist/events-consumer.js +72 -0
- package/dist/events-consumer.js.map +1 -0
- package/dist/global.d.ts +29 -0
- package/dist/global.d.ts.map +1 -0
- package/dist/global.js +50 -0
- package/dist/global.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +31 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +32 -0
- package/dist/logger.js.map +1 -0
- package/dist/observability.d.ts +24 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +126 -0
- package/dist/observability.js.map +1 -0
- package/dist/parse-name.d.ts +25 -0
- package/dist/parse-name.d.ts.map +1 -0
- package/dist/parse-name.js +40 -0
- package/dist/parse-name.js.map +1 -0
- package/dist/private.d.ts +26 -0
- package/dist/private.d.ts.map +1 -0
- package/dist/private.js +17 -0
- package/dist/private.js.map +1 -0
- package/dist/runtime/resume-hook.d.ts +71 -0
- package/dist/runtime/resume-hook.d.ts.map +1 -0
- package/dist/runtime/resume-hook.js +180 -0
- package/dist/runtime/resume-hook.js.map +1 -0
- package/dist/runtime/start.d.ts +28 -0
- package/dist/runtime/start.d.ts.map +1 -0
- package/dist/runtime/start.js +57 -0
- package/dist/runtime/start.js.map +1 -0
- package/dist/runtime/world.d.ts +24 -0
- package/dist/runtime/world.d.ts.map +1 -0
- package/dist/runtime/world.js +84 -0
- package/dist/runtime/world.js.map +1 -0
- package/dist/runtime.d.ts +121 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +554 -0
- package/dist/runtime.js.map +1 -0
- package/dist/schemas.d.ts +29 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +15 -0
- package/dist/schemas.js.map +1 -0
- package/dist/serialization.d.ts +188 -0
- package/dist/serialization.d.ts.map +1 -0
- package/dist/serialization.js +725 -0
- package/dist/serialization.js.map +1 -0
- package/dist/step/context-storage.d.ts +8 -0
- package/dist/step/context-storage.d.ts.map +1 -0
- package/dist/step/context-storage.js +3 -0
- package/dist/step/context-storage.js.map +1 -0
- package/dist/step/get-step-metadata.d.ts +38 -0
- package/dist/step/get-step-metadata.d.ts.map +1 -0
- package/dist/step/get-step-metadata.js +14 -0
- package/dist/step/get-step-metadata.js.map +1 -0
- package/dist/step/get-workflow-metadata.d.ts +7 -0
- package/dist/step/get-workflow-metadata.d.ts.map +1 -0
- package/dist/step/get-workflow-metadata.js +12 -0
- package/dist/step/get-workflow-metadata.js.map +1 -0
- package/dist/step.d.ts +4 -0
- package/dist/step.d.ts.map +1 -0
- package/dist/step.js +92 -0
- package/dist/step.js.map +1 -0
- package/dist/symbols.d.ts +9 -0
- package/dist/symbols.d.ts.map +1 -0
- package/dist/symbols.js +9 -0
- package/dist/symbols.js.map +1 -0
- package/dist/telemetry/semantic-conventions.d.ts +175 -0
- package/dist/telemetry/semantic-conventions.d.ts.map +1 -0
- package/dist/telemetry/semantic-conventions.js +121 -0
- package/dist/telemetry/semantic-conventions.js.map +1 -0
- package/dist/telemetry.d.ts +24 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +121 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +39 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +43 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +76 -0
- package/dist/util.js.map +1 -0
- package/dist/vm/index.d.ts +17 -0
- package/dist/vm/index.d.ts.map +1 -0
- package/dist/vm/index.js +93 -0
- package/dist/vm/index.js.map +1 -0
- package/dist/vm/uuid.d.ts +10 -0
- package/dist/vm/uuid.d.ts.map +1 -0
- package/dist/vm/uuid.js +30 -0
- package/dist/vm/uuid.js.map +1 -0
- package/dist/workflow/create-hook.d.ts +7 -0
- package/dist/workflow/create-hook.d.ts.map +1 -0
- package/dist/workflow/create-hook.js +22 -0
- package/dist/workflow/create-hook.js.map +1 -0
- package/dist/workflow/define-hook.d.ts +10 -0
- package/dist/workflow/define-hook.d.ts.map +1 -0
- package/dist/workflow/define-hook.js +15 -0
- package/dist/workflow/define-hook.js.map +1 -0
- package/dist/workflow/get-workflow-metadata.d.ts +17 -0
- package/dist/workflow/get-workflow-metadata.d.ts.map +1 -0
- package/dist/workflow/get-workflow-metadata.js +11 -0
- package/dist/workflow/get-workflow-metadata.js.map +1 -0
- package/dist/workflow/hook.d.ts +4 -0
- package/dist/workflow/hook.d.ts.map +1 -0
- package/dist/workflow/hook.js +101 -0
- package/dist/workflow/hook.js.map +1 -0
- package/dist/workflow/index.d.ts +10 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +14 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/writable-stream.d.ts +3 -0
- package/dist/workflow/writable-stream.d.ts.map +1 -0
- package/dist/workflow/writable-stream.js +12 -0
- package/dist/workflow/writable-stream.js.map +1 -0
- package/dist/workflow.d.ts +3 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +454 -0
- package/dist/workflow.js.map +1 -0
- package/dist/writable-stream.d.ts +22 -0
- package/dist/writable-stream.d.ts.map +1 -0
- package/dist/writable-stream.js +16 -0
- package/dist/writable-stream.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
import { WorkflowRuntimeError } from '@workflow/errors';
|
|
2
|
+
import * as devalue from 'devalue';
|
|
3
|
+
import { getWorld } from './runtime/world.js';
|
|
4
|
+
import { BODY_INIT_SYMBOL, STREAM_NAME_SYMBOL, STREAM_TYPE_SYMBOL, WEBHOOK_RESPONSE_WRITABLE, } from './symbols.js';
|
|
5
|
+
/**
|
|
6
|
+
* Detect if a readable stream is a byte stream.
|
|
7
|
+
*
|
|
8
|
+
* @param stream
|
|
9
|
+
* @returns `"bytes"` if the stream is a byte stream, `undefined` otherwise
|
|
10
|
+
*/
|
|
11
|
+
export function getStreamType(stream) {
|
|
12
|
+
try {
|
|
13
|
+
const reader = stream.getReader({ mode: 'byob' });
|
|
14
|
+
reader.releaseLock();
|
|
15
|
+
return 'bytes';
|
|
16
|
+
}
|
|
17
|
+
catch { }
|
|
18
|
+
}
|
|
19
|
+
export function getSerializeStream(reducers) {
|
|
20
|
+
const encoder = new TextEncoder();
|
|
21
|
+
const stream = new TransformStream({
|
|
22
|
+
transform(chunk, controller) {
|
|
23
|
+
try {
|
|
24
|
+
const serialized = devalue.stringify(chunk, reducers);
|
|
25
|
+
controller.enqueue(encoder.encode(`${serialized}\n`));
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
controller.error(new WorkflowRuntimeError("Failed to serialize stream chunk. Ensure you're passing serializable types (plain objects, arrays, primitives, Date, RegExp, Map, Set).", { slug: 'serialization-failed', cause: error }));
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
return stream;
|
|
33
|
+
}
|
|
34
|
+
export function getDeserializeStream(revivers) {
|
|
35
|
+
const decoder = new TextDecoder();
|
|
36
|
+
let buffer = '';
|
|
37
|
+
const stream = new TransformStream({
|
|
38
|
+
transform(chunk, controller) {
|
|
39
|
+
// Append new chunk to buffer
|
|
40
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
41
|
+
// Process all complete lines
|
|
42
|
+
while (true) {
|
|
43
|
+
const newlineIndex = buffer.indexOf('\n');
|
|
44
|
+
if (newlineIndex === -1)
|
|
45
|
+
break;
|
|
46
|
+
const line = buffer.slice(0, newlineIndex);
|
|
47
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
48
|
+
if (line.length > 0) {
|
|
49
|
+
const obj = devalue.parse(line, revivers);
|
|
50
|
+
controller.enqueue(obj);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
flush(controller) {
|
|
55
|
+
// Process any remaining data in the buffer at the end of the stream
|
|
56
|
+
if (buffer && buffer.length > 0) {
|
|
57
|
+
const obj = devalue.parse(buffer, revivers);
|
|
58
|
+
controller.enqueue(obj);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
return stream;
|
|
63
|
+
}
|
|
64
|
+
export class WorkflowServerReadableStream extends ReadableStream {
|
|
65
|
+
#reader;
|
|
66
|
+
constructor(name, startIndex) {
|
|
67
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
68
|
+
throw new Error(`"name" is required, got "${name}"`);
|
|
69
|
+
}
|
|
70
|
+
super({
|
|
71
|
+
// @ts-expect-error Not sure why TypeScript is complaining about this
|
|
72
|
+
type: 'bytes',
|
|
73
|
+
pull: async (controller) => {
|
|
74
|
+
let reader = this.#reader;
|
|
75
|
+
if (!reader) {
|
|
76
|
+
const world = getWorld();
|
|
77
|
+
const stream = await world.readFromStream(name, startIndex);
|
|
78
|
+
reader = this.#reader = stream.getReader();
|
|
79
|
+
}
|
|
80
|
+
if (!reader) {
|
|
81
|
+
controller.error(new Error('Failed to get reader'));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const result = await reader.read();
|
|
85
|
+
if (result.done) {
|
|
86
|
+
this.#reader = undefined;
|
|
87
|
+
controller.close();
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
controller.enqueue(result.value);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export class WorkflowServerWritableStream extends WritableStream {
|
|
97
|
+
constructor(name) {
|
|
98
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
99
|
+
throw new Error(`"name" is required, got "${name}"`);
|
|
100
|
+
}
|
|
101
|
+
const world = getWorld();
|
|
102
|
+
super({
|
|
103
|
+
write: async (chunk) => {
|
|
104
|
+
await world.writeToStream(name, chunk);
|
|
105
|
+
},
|
|
106
|
+
close: async () => {
|
|
107
|
+
await world.closeStream(name);
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function revive(str) {
|
|
113
|
+
// biome-ignore lint/security/noGlobalEval: Eval is safe here - we are only passing value from `devalue.stringify()`
|
|
114
|
+
// biome-ignore lint/complexity/noCommaOperator: This is how you do global scope eval
|
|
115
|
+
return (0, eval)(`(${str})`);
|
|
116
|
+
}
|
|
117
|
+
function getCommonReducers(global = globalThis) {
|
|
118
|
+
const abToBase64 = (value, offset, length) => {
|
|
119
|
+
// Avoid returning falsy value for zero-length buffers
|
|
120
|
+
if (length === 0)
|
|
121
|
+
return '.';
|
|
122
|
+
return Buffer.from(value, offset, length).toString('base64');
|
|
123
|
+
};
|
|
124
|
+
const viewToBase64 = (value) => abToBase64(value.buffer, value.byteOffset, value.byteLength);
|
|
125
|
+
return {
|
|
126
|
+
ArrayBuffer: (value) => value instanceof global.ArrayBuffer &&
|
|
127
|
+
abToBase64(value, 0, value.byteLength),
|
|
128
|
+
BigInt64Array: (value) => value instanceof global.BigInt64Array && viewToBase64(value),
|
|
129
|
+
BigUint64Array: (value) => value instanceof global.BigUint64Array && viewToBase64(value),
|
|
130
|
+
Date: (value) => {
|
|
131
|
+
if (!(value instanceof global.Date))
|
|
132
|
+
return false;
|
|
133
|
+
const valid = !Number.isNaN(value.getDate());
|
|
134
|
+
// Note: "." is to avoid returning a falsy value when the date is invalid
|
|
135
|
+
return valid ? value.toISOString() : '.';
|
|
136
|
+
},
|
|
137
|
+
Error: (value) => {
|
|
138
|
+
if (!(value instanceof global.Error))
|
|
139
|
+
return false;
|
|
140
|
+
return {
|
|
141
|
+
name: value.name,
|
|
142
|
+
message: value.message,
|
|
143
|
+
stack: value.stack,
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
Float32Array: (value) => value instanceof global.Float32Array && viewToBase64(value),
|
|
147
|
+
Float64Array: (value) => value instanceof global.Float64Array && viewToBase64(value),
|
|
148
|
+
Headers: (value) => value instanceof global.Headers && Array.from(value),
|
|
149
|
+
Int8Array: (value) => value instanceof global.Int8Array && viewToBase64(value),
|
|
150
|
+
Int16Array: (value) => value instanceof global.Int16Array && viewToBase64(value),
|
|
151
|
+
Int32Array: (value) => value instanceof global.Int32Array && viewToBase64(value),
|
|
152
|
+
Map: (value) => value instanceof global.Map && Array.from(value),
|
|
153
|
+
RegExp: (value) => value instanceof global.RegExp && {
|
|
154
|
+
source: value.source,
|
|
155
|
+
flags: value.flags,
|
|
156
|
+
},
|
|
157
|
+
Request: (value) => {
|
|
158
|
+
if (!(value instanceof global.Request))
|
|
159
|
+
return false;
|
|
160
|
+
const data = {
|
|
161
|
+
method: value.method,
|
|
162
|
+
url: value.url,
|
|
163
|
+
headers: value.headers,
|
|
164
|
+
body: value.body,
|
|
165
|
+
duplex: value.duplex,
|
|
166
|
+
};
|
|
167
|
+
const responseWritable = value[WEBHOOK_RESPONSE_WRITABLE];
|
|
168
|
+
if (responseWritable) {
|
|
169
|
+
data.responseWritable = responseWritable;
|
|
170
|
+
}
|
|
171
|
+
return data;
|
|
172
|
+
},
|
|
173
|
+
Response: (value) => {
|
|
174
|
+
if (!(value instanceof global.Response))
|
|
175
|
+
return false;
|
|
176
|
+
return {
|
|
177
|
+
type: value.type,
|
|
178
|
+
url: value.url,
|
|
179
|
+
status: value.status,
|
|
180
|
+
statusText: value.statusText,
|
|
181
|
+
headers: value.headers,
|
|
182
|
+
body: value.body,
|
|
183
|
+
redirected: value.redirected,
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
Set: (value) => value instanceof global.Set && Array.from(value),
|
|
187
|
+
URL: (value) => value instanceof global.URL && value.href,
|
|
188
|
+
URLSearchParams: (value) => {
|
|
189
|
+
if (!(value instanceof global.URLSearchParams))
|
|
190
|
+
return false;
|
|
191
|
+
// Avoid returning a falsy value when the URLSearchParams is empty
|
|
192
|
+
if (value.size === 0)
|
|
193
|
+
return '.';
|
|
194
|
+
return String(value);
|
|
195
|
+
},
|
|
196
|
+
Uint8Array: (value) => value instanceof global.Uint8Array && viewToBase64(value),
|
|
197
|
+
Uint8ClampedArray: (value) => value instanceof global.Uint8ClampedArray && viewToBase64(value),
|
|
198
|
+
Uint16Array: (value) => value instanceof global.Uint16Array && viewToBase64(value),
|
|
199
|
+
Uint32Array: (value) => value instanceof global.Uint32Array && viewToBase64(value),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Reducers for serialization boundary from the client side, passing arguments
|
|
204
|
+
* to the workflow handler.
|
|
205
|
+
*
|
|
206
|
+
* @param global
|
|
207
|
+
* @param ops
|
|
208
|
+
* @returns
|
|
209
|
+
*/
|
|
210
|
+
export function getExternalReducers(global = globalThis, ops) {
|
|
211
|
+
return {
|
|
212
|
+
...getCommonReducers(global),
|
|
213
|
+
ReadableStream: (value) => {
|
|
214
|
+
if (!(value instanceof global.ReadableStream))
|
|
215
|
+
return false;
|
|
216
|
+
// Stream must not be locked when passing across execution boundary
|
|
217
|
+
if (value.locked) {
|
|
218
|
+
throw new Error('ReadableStream is locked');
|
|
219
|
+
}
|
|
220
|
+
const name = global.crypto.randomUUID();
|
|
221
|
+
const type = getStreamType(value);
|
|
222
|
+
const writable = new WorkflowServerWritableStream(name);
|
|
223
|
+
if (type === 'bytes') {
|
|
224
|
+
ops.push(value.pipeTo(writable));
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
ops.push(value
|
|
228
|
+
.pipeThrough(getSerializeStream(getExternalReducers(global, ops)))
|
|
229
|
+
.pipeTo(writable));
|
|
230
|
+
}
|
|
231
|
+
const s = { name };
|
|
232
|
+
if (type)
|
|
233
|
+
s.type = type;
|
|
234
|
+
return s;
|
|
235
|
+
},
|
|
236
|
+
WritableStream: (value) => {
|
|
237
|
+
if (!(value instanceof global.WritableStream))
|
|
238
|
+
return false;
|
|
239
|
+
const name = global.crypto.randomUUID();
|
|
240
|
+
ops.push(new WorkflowServerReadableStream(name)
|
|
241
|
+
.pipeThrough(getDeserializeStream(getExternalRevivers(global, ops)))
|
|
242
|
+
.pipeTo(value));
|
|
243
|
+
return { name };
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Reducers for serialization boundary from within the workflow execution
|
|
249
|
+
* environment, passing return value to the client side and into step arguments.
|
|
250
|
+
*
|
|
251
|
+
* @param global
|
|
252
|
+
* @returns
|
|
253
|
+
*/
|
|
254
|
+
export function getWorkflowReducers(global = globalThis) {
|
|
255
|
+
return {
|
|
256
|
+
...getCommonReducers(global),
|
|
257
|
+
// Readable/Writable streams from within the workflow execution environment
|
|
258
|
+
// are simply "handles" that can be passed around to other steps.
|
|
259
|
+
ReadableStream: (value) => {
|
|
260
|
+
if (!(value instanceof global.ReadableStream))
|
|
261
|
+
return false;
|
|
262
|
+
// Check if this is a fake stream storing BodyInit from Request/Response constructor
|
|
263
|
+
const bodyInit = value[BODY_INIT_SYMBOL];
|
|
264
|
+
if (bodyInit !== undefined) {
|
|
265
|
+
// This is a fake stream - serialize the BodyInit directly
|
|
266
|
+
// devalue will handle serializing strings, Uint8Array, etc.
|
|
267
|
+
return { bodyInit };
|
|
268
|
+
}
|
|
269
|
+
const name = value[STREAM_NAME_SYMBOL];
|
|
270
|
+
if (!name) {
|
|
271
|
+
throw new Error('ReadableStream `name` is not set');
|
|
272
|
+
}
|
|
273
|
+
const s = { name };
|
|
274
|
+
const type = value[STREAM_TYPE_SYMBOL];
|
|
275
|
+
if (type)
|
|
276
|
+
s.type = type;
|
|
277
|
+
return s;
|
|
278
|
+
},
|
|
279
|
+
WritableStream: (value) => {
|
|
280
|
+
if (!(value instanceof global.WritableStream))
|
|
281
|
+
return false;
|
|
282
|
+
const name = value[STREAM_NAME_SYMBOL];
|
|
283
|
+
if (!name) {
|
|
284
|
+
throw new Error('WritableStream `name` is not set');
|
|
285
|
+
}
|
|
286
|
+
return { name };
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Reducers for serialization boundary from within the step execution
|
|
292
|
+
* environment, passing return value to the workflow handler.
|
|
293
|
+
*
|
|
294
|
+
* @param global
|
|
295
|
+
* @param ops
|
|
296
|
+
* @returns
|
|
297
|
+
*/
|
|
298
|
+
function getStepReducers(global = globalThis, ops) {
|
|
299
|
+
return {
|
|
300
|
+
...getCommonReducers(global),
|
|
301
|
+
ReadableStream: (value) => {
|
|
302
|
+
if (!(value instanceof global.ReadableStream))
|
|
303
|
+
return false;
|
|
304
|
+
// Stream must not be locked when passing across execution boundary
|
|
305
|
+
if (value.locked) {
|
|
306
|
+
throw new Error('ReadableStream is locked');
|
|
307
|
+
}
|
|
308
|
+
// Check if the stream already has the name symbol set, in which case
|
|
309
|
+
// it's already being sunk to the server and we can just return the
|
|
310
|
+
// name and type.
|
|
311
|
+
let name = value[STREAM_NAME_SYMBOL];
|
|
312
|
+
let type = value[STREAM_TYPE_SYMBOL];
|
|
313
|
+
if (!name) {
|
|
314
|
+
name = global.crypto.randomUUID();
|
|
315
|
+
type = getStreamType(value);
|
|
316
|
+
const writable = new WorkflowServerWritableStream(name);
|
|
317
|
+
if (type === 'bytes') {
|
|
318
|
+
ops.push(value.pipeTo(writable));
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
ops.push(value
|
|
322
|
+
.pipeThrough(getSerializeStream(getStepReducers(global, ops)))
|
|
323
|
+
.pipeTo(writable));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const s = { name };
|
|
327
|
+
if (type)
|
|
328
|
+
s.type = type;
|
|
329
|
+
return s;
|
|
330
|
+
},
|
|
331
|
+
WritableStream: (value) => {
|
|
332
|
+
if (!(value instanceof global.WritableStream))
|
|
333
|
+
return false;
|
|
334
|
+
let name = value[STREAM_NAME_SYMBOL];
|
|
335
|
+
if (!name) {
|
|
336
|
+
name = global.crypto.randomUUID();
|
|
337
|
+
ops.push(new WorkflowServerReadableStream(name)
|
|
338
|
+
.pipeThrough(getDeserializeStream(getStepRevivers(global, ops)))
|
|
339
|
+
.pipeTo(value));
|
|
340
|
+
}
|
|
341
|
+
return { name };
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function getCommonRevivers(global = globalThis) {
|
|
346
|
+
function reviveArrayBuffer(value) {
|
|
347
|
+
// Handle sentinel value for zero-length buffers
|
|
348
|
+
const base64 = value === '.' ? '' : value;
|
|
349
|
+
const buffer = Buffer.from(base64, 'base64');
|
|
350
|
+
const arrayBuffer = new global.ArrayBuffer(buffer.length);
|
|
351
|
+
const uint8Array = new global.Uint8Array(arrayBuffer);
|
|
352
|
+
uint8Array.set(buffer);
|
|
353
|
+
return arrayBuffer;
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
ArrayBuffer: reviveArrayBuffer,
|
|
357
|
+
BigInt64Array: (value) => {
|
|
358
|
+
const ab = reviveArrayBuffer(value);
|
|
359
|
+
return new global.BigInt64Array(ab);
|
|
360
|
+
},
|
|
361
|
+
BigUint64Array: (value) => {
|
|
362
|
+
const ab = reviveArrayBuffer(value);
|
|
363
|
+
return new global.BigUint64Array(ab);
|
|
364
|
+
},
|
|
365
|
+
Date: (value) => new global.Date(value),
|
|
366
|
+
Error: (value) => {
|
|
367
|
+
const error = new global.Error(value.message);
|
|
368
|
+
error.name = value.name;
|
|
369
|
+
error.stack = value.stack;
|
|
370
|
+
return error;
|
|
371
|
+
},
|
|
372
|
+
Float32Array: (value) => {
|
|
373
|
+
const ab = reviveArrayBuffer(value);
|
|
374
|
+
return new global.Float32Array(ab);
|
|
375
|
+
},
|
|
376
|
+
Float64Array: (value) => {
|
|
377
|
+
const ab = reviveArrayBuffer(value);
|
|
378
|
+
return new global.Float64Array(ab);
|
|
379
|
+
},
|
|
380
|
+
Headers: (value) => new global.Headers(value),
|
|
381
|
+
Int8Array: (value) => {
|
|
382
|
+
const ab = reviveArrayBuffer(value);
|
|
383
|
+
return new global.Int8Array(ab);
|
|
384
|
+
},
|
|
385
|
+
Int16Array: (value) => {
|
|
386
|
+
const ab = reviveArrayBuffer(value);
|
|
387
|
+
return new global.Int16Array(ab);
|
|
388
|
+
},
|
|
389
|
+
Int32Array: (value) => {
|
|
390
|
+
const ab = reviveArrayBuffer(value);
|
|
391
|
+
return new global.Int32Array(ab);
|
|
392
|
+
},
|
|
393
|
+
Map: (value) => new global.Map(value),
|
|
394
|
+
RegExp: (value) => new global.RegExp(value.source, value.flags),
|
|
395
|
+
Set: (value) => new global.Set(value),
|
|
396
|
+
URL: (value) => new global.URL(value),
|
|
397
|
+
URLSearchParams: (value) => new global.URLSearchParams(value === '.' ? '' : value),
|
|
398
|
+
Uint8Array: (value) => {
|
|
399
|
+
const ab = reviveArrayBuffer(value);
|
|
400
|
+
return new global.Uint8Array(ab);
|
|
401
|
+
},
|
|
402
|
+
Uint8ClampedArray: (value) => {
|
|
403
|
+
const ab = reviveArrayBuffer(value);
|
|
404
|
+
return new global.Uint8ClampedArray(ab);
|
|
405
|
+
},
|
|
406
|
+
Uint16Array: (value) => {
|
|
407
|
+
const ab = reviveArrayBuffer(value);
|
|
408
|
+
return new global.Uint16Array(ab);
|
|
409
|
+
},
|
|
410
|
+
Uint32Array: (value) => {
|
|
411
|
+
const ab = reviveArrayBuffer(value);
|
|
412
|
+
return new global.Uint32Array(ab);
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Revivers for deserialization boundary from the client side,
|
|
418
|
+
* receiving the return value from the workflow handler.
|
|
419
|
+
*
|
|
420
|
+
* @param global
|
|
421
|
+
* @param ops
|
|
422
|
+
*/
|
|
423
|
+
export function getExternalRevivers(global = globalThis, ops) {
|
|
424
|
+
return {
|
|
425
|
+
...getCommonRevivers(global),
|
|
426
|
+
Request: (value) => {
|
|
427
|
+
return new global.Request(value.url, {
|
|
428
|
+
method: value.method,
|
|
429
|
+
headers: new global.Headers(value.headers),
|
|
430
|
+
body: value.body,
|
|
431
|
+
duplex: value.duplex,
|
|
432
|
+
});
|
|
433
|
+
},
|
|
434
|
+
Response: (value) => {
|
|
435
|
+
// Note: Response constructor only accepts status, statusText, and headers
|
|
436
|
+
// The type, url, and redirected properties are read-only and set by the constructor
|
|
437
|
+
return new global.Response(value.body, {
|
|
438
|
+
status: value.status,
|
|
439
|
+
statusText: value.statusText,
|
|
440
|
+
headers: new global.Headers(value.headers),
|
|
441
|
+
});
|
|
442
|
+
},
|
|
443
|
+
ReadableStream: (value) => {
|
|
444
|
+
// If this has bodyInit, it came from a Response constructor
|
|
445
|
+
// Convert it to a REAL stream now that we're outside the workflow
|
|
446
|
+
if ('bodyInit' in value) {
|
|
447
|
+
const bodyInit = value.bodyInit;
|
|
448
|
+
// Use the native Response constructor to properly convert BodyInit to ReadableStream
|
|
449
|
+
const response = new global.Response(bodyInit);
|
|
450
|
+
return response.body;
|
|
451
|
+
}
|
|
452
|
+
const readable = new WorkflowServerReadableStream(value.name, value.startIndex);
|
|
453
|
+
if (value.type === 'bytes') {
|
|
454
|
+
return readable;
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
const transform = getDeserializeStream(getExternalRevivers(global, ops));
|
|
458
|
+
ops.push(readable.pipeTo(transform.writable));
|
|
459
|
+
return transform.readable;
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
WritableStream: (value) => {
|
|
463
|
+
const serialize = getSerializeStream(getExternalReducers(global, ops));
|
|
464
|
+
ops.push(serialize.readable.pipeTo(new WorkflowServerWritableStream(value.name)));
|
|
465
|
+
return serialize.writable;
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Revivers for deserialization boundary from within the workflow execution
|
|
471
|
+
* environment, receiving arguments from the client side, and return values
|
|
472
|
+
* from the steps.
|
|
473
|
+
*
|
|
474
|
+
* @param global
|
|
475
|
+
* @returns
|
|
476
|
+
*/
|
|
477
|
+
export function getWorkflowRevivers(global = globalThis) {
|
|
478
|
+
return {
|
|
479
|
+
...getCommonRevivers(global),
|
|
480
|
+
Request: (value) => {
|
|
481
|
+
Object.setPrototypeOf(value, global.Request.prototype);
|
|
482
|
+
const responseWritable = value.responseWritable;
|
|
483
|
+
if (responseWritable) {
|
|
484
|
+
value[WEBHOOK_RESPONSE_WRITABLE] = responseWritable;
|
|
485
|
+
delete value.responseWritable;
|
|
486
|
+
value.respondWith = () => {
|
|
487
|
+
throw new Error('`respondWith()` must be called from within a step function');
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
return value;
|
|
491
|
+
},
|
|
492
|
+
Response: (value) => {
|
|
493
|
+
Object.setPrototypeOf(value, global.Response.prototype);
|
|
494
|
+
return value;
|
|
495
|
+
},
|
|
496
|
+
ReadableStream: (value) => {
|
|
497
|
+
// Check if this is a BodyInit that should be wrapped in a fake stream
|
|
498
|
+
if ('bodyInit' in value) {
|
|
499
|
+
// Recreate the fake stream with the BodyInit
|
|
500
|
+
return Object.create(global.ReadableStream.prototype, {
|
|
501
|
+
[BODY_INIT_SYMBOL]: {
|
|
502
|
+
value: value.bodyInit,
|
|
503
|
+
writable: false,
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
// Regular stream handling
|
|
508
|
+
return Object.create(global.ReadableStream.prototype, {
|
|
509
|
+
[STREAM_NAME_SYMBOL]: {
|
|
510
|
+
value: value.name,
|
|
511
|
+
writable: false,
|
|
512
|
+
},
|
|
513
|
+
[STREAM_TYPE_SYMBOL]: {
|
|
514
|
+
value: value.type,
|
|
515
|
+
writable: false,
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
},
|
|
519
|
+
WritableStream: (value) => {
|
|
520
|
+
return Object.create(global.WritableStream.prototype, {
|
|
521
|
+
[STREAM_NAME_SYMBOL]: {
|
|
522
|
+
value: value.name,
|
|
523
|
+
writable: false,
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
},
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Revivers for deserialization boundary from within the step execution
|
|
531
|
+
* environment, receiving arguments from the workflow handler.
|
|
532
|
+
*
|
|
533
|
+
* @param global
|
|
534
|
+
* @param ops
|
|
535
|
+
* @returns
|
|
536
|
+
*/
|
|
537
|
+
function getStepRevivers(global = globalThis, ops) {
|
|
538
|
+
return {
|
|
539
|
+
...getCommonRevivers(global),
|
|
540
|
+
Request: (value) => {
|
|
541
|
+
const responseWritable = value.responseWritable;
|
|
542
|
+
const request = new global.Request(value.url, {
|
|
543
|
+
method: value.method,
|
|
544
|
+
headers: new global.Headers(value.headers),
|
|
545
|
+
body: value.body,
|
|
546
|
+
duplex: value.duplex,
|
|
547
|
+
});
|
|
548
|
+
if (responseWritable) {
|
|
549
|
+
request.respondWith = async (response) => {
|
|
550
|
+
const writer = responseWritable.getWriter();
|
|
551
|
+
await writer.write(response);
|
|
552
|
+
await writer.close();
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
return request;
|
|
556
|
+
},
|
|
557
|
+
Response: (value) => {
|
|
558
|
+
// Note: Response constructor only accepts status, statusText, and headers
|
|
559
|
+
// The type, url, and redirected properties are read-only and set by the constructor
|
|
560
|
+
return new global.Response(value.body, {
|
|
561
|
+
status: value.status,
|
|
562
|
+
statusText: value.statusText,
|
|
563
|
+
headers: new global.Headers(value.headers),
|
|
564
|
+
});
|
|
565
|
+
},
|
|
566
|
+
ReadableStream: (value) => {
|
|
567
|
+
// If this has bodyInit, it came from a Response constructor
|
|
568
|
+
// Convert it to a REAL stream now that we're in the step environment
|
|
569
|
+
if ('bodyInit' in value) {
|
|
570
|
+
const bodyInit = value.bodyInit;
|
|
571
|
+
// Use the native Response constructor to properly convert BodyInit to ReadableStream
|
|
572
|
+
const response = new global.Response(bodyInit);
|
|
573
|
+
return response.body;
|
|
574
|
+
}
|
|
575
|
+
const readable = new WorkflowServerReadableStream(value.name);
|
|
576
|
+
if (value.type === 'bytes') {
|
|
577
|
+
return readable;
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
const transform = getDeserializeStream(getStepRevivers(global, ops));
|
|
581
|
+
ops.push(readable.pipeTo(transform.writable));
|
|
582
|
+
return transform.readable;
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
WritableStream: (value) => {
|
|
586
|
+
const serialize = getSerializeStream(getStepReducers(global, ops));
|
|
587
|
+
ops.push(serialize.readable.pipeTo(new WorkflowServerWritableStream(value.name)));
|
|
588
|
+
return serialize.writable;
|
|
589
|
+
},
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Called from the `start()` function to serialize the workflow arguments
|
|
594
|
+
* into a format that can be saved to the database and then hydrated from
|
|
595
|
+
* within the workflow execution environment.
|
|
596
|
+
*
|
|
597
|
+
* @param value
|
|
598
|
+
* @param global
|
|
599
|
+
* @returns The dehydrated value, ready to be inserted into the database
|
|
600
|
+
*/
|
|
601
|
+
export function dehydrateWorkflowArguments(value, ops, global = globalThis) {
|
|
602
|
+
try {
|
|
603
|
+
const str = devalue.stringify(value, getExternalReducers(global, ops));
|
|
604
|
+
return revive(str);
|
|
605
|
+
}
|
|
606
|
+
catch (error) {
|
|
607
|
+
throw new WorkflowRuntimeError(`Failed to serialize workflow arguments. Ensure you're passing serializable types (plain objects, arrays, primitives, Date, RegExp, Map, Set).`, { slug: 'serialization-failed', cause: error });
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Called from workflow execution environment to hydrate the workflow
|
|
612
|
+
* arguments from the database at the start of workflow execution.
|
|
613
|
+
*
|
|
614
|
+
* @param value
|
|
615
|
+
* @param ops
|
|
616
|
+
* @param global
|
|
617
|
+
* @returns The hydrated value
|
|
618
|
+
*/
|
|
619
|
+
export function hydrateWorkflowArguments(value, global = globalThis, extraRevivers = {}) {
|
|
620
|
+
const obj = devalue.unflatten(value, {
|
|
621
|
+
...getWorkflowRevivers(global),
|
|
622
|
+
...extraRevivers,
|
|
623
|
+
});
|
|
624
|
+
return obj;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Called at the end of a completed workflow execution to serialize the
|
|
628
|
+
* return value into a format that can be saved to the database.
|
|
629
|
+
*
|
|
630
|
+
* @param value
|
|
631
|
+
* @param global
|
|
632
|
+
* @returns The dehydrated value, ready to be inserted into the database
|
|
633
|
+
*/
|
|
634
|
+
export function dehydrateWorkflowReturnValue(value, global = globalThis) {
|
|
635
|
+
try {
|
|
636
|
+
const str = devalue.stringify(value, getWorkflowReducers(global));
|
|
637
|
+
return revive(str);
|
|
638
|
+
}
|
|
639
|
+
catch (error) {
|
|
640
|
+
throw new WorkflowRuntimeError(`Failed to serialize workflow return value. Ensure you're returning serializable types (plain objects, arrays, primitives, Date, RegExp, Map, Set).`, { slug: 'serialization-failed', cause: error });
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Called from the client side (i.e. the execution environment where
|
|
645
|
+
* the workflow run was initiated from) to hydrate the workflow
|
|
646
|
+
* return value of a completed workflow run.
|
|
647
|
+
*
|
|
648
|
+
* @param value
|
|
649
|
+
* @param global
|
|
650
|
+
* @returns The hydrated return value, ready to be consumed by the client
|
|
651
|
+
*/
|
|
652
|
+
export function hydrateWorkflowReturnValue(value, ops, global = globalThis, extraRevivers = {}) {
|
|
653
|
+
const obj = devalue.unflatten(value, {
|
|
654
|
+
...getExternalRevivers(global, ops),
|
|
655
|
+
...extraRevivers,
|
|
656
|
+
});
|
|
657
|
+
return obj;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Called from the workflow handler when a step is being created.
|
|
661
|
+
* Dehydrates values from within the workflow execution environment
|
|
662
|
+
* into a format that can be saved to the database.
|
|
663
|
+
*
|
|
664
|
+
* @param value
|
|
665
|
+
* @param global
|
|
666
|
+
* @returns The dehydrated value, ready to be inserted into the database
|
|
667
|
+
*/
|
|
668
|
+
export function dehydrateStepArguments(value, global) {
|
|
669
|
+
try {
|
|
670
|
+
const str = devalue.stringify(value, getWorkflowReducers(global));
|
|
671
|
+
return revive(str);
|
|
672
|
+
}
|
|
673
|
+
catch (error) {
|
|
674
|
+
throw new WorkflowRuntimeError(`Failed to serialize step arguments. Ensure you're passing serializable types (plain objects, arrays, primitives, Date, RegExp, Map, Set).`, { slug: 'serialization-failed', cause: error });
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Called from the step handler to hydrate the arguments of a step
|
|
679
|
+
* from the database at the start of the step execution.
|
|
680
|
+
*
|
|
681
|
+
* @param value
|
|
682
|
+
* @param global
|
|
683
|
+
* @returns The hydrated value, ready to be consumed by the step user-code function
|
|
684
|
+
*/
|
|
685
|
+
export function hydrateStepArguments(value, ops, global = globalThis, extraRevivers = {}) {
|
|
686
|
+
const obj = devalue.unflatten(value, {
|
|
687
|
+
...getStepRevivers(global, ops),
|
|
688
|
+
...extraRevivers,
|
|
689
|
+
});
|
|
690
|
+
return obj;
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Called from the step handler when a step has completed.
|
|
694
|
+
* Dehydrates values from within the step execution environment
|
|
695
|
+
* into a format that can be saved to the database.
|
|
696
|
+
*
|
|
697
|
+
* @param value
|
|
698
|
+
* @param global
|
|
699
|
+
* @returns The dehydrated value, ready to be inserted into the database
|
|
700
|
+
*/
|
|
701
|
+
export function dehydrateStepReturnValue(value, ops, global = globalThis) {
|
|
702
|
+
try {
|
|
703
|
+
const str = devalue.stringify(value, getStepReducers(global, ops));
|
|
704
|
+
return revive(str);
|
|
705
|
+
}
|
|
706
|
+
catch (error) {
|
|
707
|
+
throw new WorkflowRuntimeError(`Failed to serialize step return value. Ensure you're returning serializable types (plain objects, arrays, primitives, Date, RegExp, Map, Set).`, { slug: 'serialization-failed', cause: error });
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Called from the workflow handler when replaying the event log of a `step_completed` event.
|
|
712
|
+
* Hydrates the return value of a step from the database.
|
|
713
|
+
*
|
|
714
|
+
* @param value
|
|
715
|
+
* @param global
|
|
716
|
+
* @returns The hydrated return value of a step, ready to be consumed by the workflow handler
|
|
717
|
+
*/
|
|
718
|
+
export function hydrateStepReturnValue(value, global = globalThis, extraRevivers = {}) {
|
|
719
|
+
const obj = devalue.unflatten(value, {
|
|
720
|
+
...getWorkflowRevivers(global),
|
|
721
|
+
...extraRevivers,
|
|
722
|
+
});
|
|
723
|
+
return obj;
|
|
724
|
+
}
|
|
725
|
+
//# sourceMappingURL=serialization.js.map
|