@workflow/core 4.0.1-beta.4 → 4.0.1-beta.41
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 +201 -21
- package/dist/builtins.js +1 -1
- package/dist/class-serialization.d.ts +26 -0
- package/dist/class-serialization.d.ts.map +1 -0
- package/dist/class-serialization.js +66 -0
- package/dist/create-hook.js +1 -1
- package/dist/define-hook.d.ts +40 -25
- package/dist/define-hook.d.ts.map +1 -1
- package/dist/define-hook.js +22 -27
- package/dist/events-consumer.js +1 -1
- package/dist/flushable-stream.d.ts +82 -0
- package/dist/flushable-stream.d.ts.map +1 -0
- package/dist/flushable-stream.js +214 -0
- package/dist/global.d.ts +12 -2
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js +32 -8
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/logger.js +1 -1
- package/dist/observability.d.ts +60 -0
- package/dist/observability.d.ts.map +1 -1
- package/dist/observability.js +175 -19
- package/dist/parse-name.d.ts +12 -0
- package/dist/parse-name.d.ts.map +1 -1
- package/dist/parse-name.js +28 -3
- package/dist/private.d.ts +10 -1
- package/dist/private.d.ts.map +1 -1
- package/dist/private.js +6 -1
- package/dist/runtime/helpers.d.ts +52 -0
- package/dist/runtime/helpers.d.ts.map +1 -0
- package/dist/runtime/helpers.js +264 -0
- package/dist/runtime/resume-hook.d.ts +16 -11
- package/dist/runtime/resume-hook.d.ts.map +1 -1
- package/dist/runtime/resume-hook.js +76 -64
- package/dist/runtime/start.d.ts +10 -0
- package/dist/runtime/start.d.ts.map +1 -1
- package/dist/runtime/start.js +56 -45
- package/dist/runtime/step-handler.d.ts +7 -0
- package/dist/runtime/step-handler.d.ts.map +1 -0
- package/dist/runtime/step-handler.js +353 -0
- package/dist/runtime/suspension-handler.d.ts +20 -0
- package/dist/runtime/suspension-handler.d.ts.map +1 -0
- package/dist/runtime/suspension-handler.js +179 -0
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +22 -21
- package/dist/runtime.d.ts +3 -7
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +76 -334
- package/dist/schemas.d.ts +1 -15
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +2 -15
- package/dist/serialization.d.ts +74 -10
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +295 -56
- package/dist/sleep.d.ts +33 -0
- package/dist/sleep.d.ts.map +1 -0
- package/dist/sleep.js +10 -0
- package/dist/source-map.d.ts +10 -0
- package/dist/source-map.d.ts.map +1 -0
- package/dist/source-map.js +56 -0
- package/dist/step/context-storage.d.ts +2 -0
- package/dist/step/context-storage.d.ts.map +1 -1
- package/dist/step/context-storage.js +1 -1
- package/dist/step/get-closure-vars.d.ts +9 -0
- package/dist/step/get-closure-vars.d.ts.map +1 -0
- package/dist/step/get-closure-vars.js +16 -0
- package/dist/step/get-step-metadata.js +1 -1
- package/dist/step/get-workflow-metadata.js +1 -1
- package/dist/step/writable-stream.d.ts +14 -0
- package/dist/step/writable-stream.d.ts.map +1 -0
- package/dist/step/writable-stream.js +30 -0
- package/dist/step.d.ts +1 -1
- package/dist/step.d.ts.map +1 -1
- package/dist/step.js +51 -11
- package/dist/symbols.d.ts +7 -0
- package/dist/symbols.d.ts.map +1 -1
- package/dist/symbols.js +8 -1
- package/dist/telemetry/semantic-conventions.d.ts +66 -38
- package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
- package/dist/telemetry/semantic-conventions.js +16 -3
- package/dist/telemetry.d.ts +8 -4
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +39 -6
- package/dist/types.d.ts +0 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -26
- package/dist/util.d.ts +9 -27
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +37 -44
- package/dist/vm/index.js +2 -2
- package/dist/vm/uuid.js +1 -1
- package/dist/workflow/create-hook.js +1 -1
- package/dist/workflow/define-hook.d.ts +3 -3
- package/dist/workflow/define-hook.d.ts.map +1 -1
- package/dist/workflow/define-hook.js +1 -1
- package/dist/workflow/get-workflow-metadata.js +1 -1
- package/dist/workflow/hook.d.ts.map +1 -1
- package/dist/workflow/hook.js +6 -9
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +2 -1
- package/dist/workflow/sleep.d.ts +4 -0
- package/dist/workflow/sleep.d.ts.map +1 -0
- package/dist/workflow/sleep.js +54 -0
- package/dist/workflow/writable-stream.js +1 -1
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +60 -9
- package/dist/writable-stream.d.ts +5 -4
- package/dist/writable-stream.d.ts.map +1 -1
- package/dist/writable-stream.js +7 -6
- package/package.json +23 -18
- package/dist/builtins.js.map +0 -1
- package/dist/create-hook.js.map +0 -1
- package/dist/define-hook.js.map +0 -1
- package/dist/events-consumer.js.map +0 -1
- package/dist/global.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/observability.js.map +0 -1
- package/dist/parse-name.js.map +0 -1
- package/dist/private.js.map +0 -1
- package/dist/runtime/resume-hook.js.map +0 -1
- package/dist/runtime/start.js.map +0 -1
- package/dist/runtime/world.js.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/schemas.js.map +0 -1
- package/dist/serialization.js.map +0 -1
- package/dist/step/context-storage.js.map +0 -1
- package/dist/step/get-step-metadata.js.map +0 -1
- package/dist/step/get-workflow-metadata.js.map +0 -1
- package/dist/step.js.map +0 -1
- package/dist/symbols.js.map +0 -1
- package/dist/telemetry/semantic-conventions.js.map +0 -1
- package/dist/telemetry.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/util.js.map +0 -1
- package/dist/vm/index.js.map +0 -1
- package/dist/vm/uuid.js.map +0 -1
- package/dist/workflow/create-hook.js.map +0 -1
- package/dist/workflow/define-hook.js.map +0 -1
- package/dist/workflow/get-workflow-metadata.js.map +0 -1
- package/dist/workflow/hook.js.map +0 -1
- package/dist/workflow/index.js.map +0 -1
- package/dist/workflow/writable-stream.js.map +0 -1
- package/dist/workflow.js.map +0 -1
- package/dist/writable-stream.js.map +0 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { HealthCheckPayloadSchema } from '@workflow/world';
|
|
2
|
+
import { monotonicFactory } from 'ulid';
|
|
3
|
+
import * as Attribute from '../telemetry/semantic-conventions.js';
|
|
4
|
+
import { getSpanKind, trace } from '../telemetry.js';
|
|
5
|
+
import { getWorld } from './world.js';
|
|
6
|
+
/** Default timeout for health checks in milliseconds */
|
|
7
|
+
const DEFAULT_HEALTH_CHECK_TIMEOUT = 30_000;
|
|
8
|
+
const generateId = monotonicFactory();
|
|
9
|
+
/**
|
|
10
|
+
* Returns the stream name for a health check with the given correlation ID.
|
|
11
|
+
*/
|
|
12
|
+
function getHealthCheckStreamName(correlationId) {
|
|
13
|
+
return `__health_check__${correlationId}`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Checks if the given message is a health check payload.
|
|
17
|
+
* If so, returns the parsed payload. Otherwise returns undefined.
|
|
18
|
+
*/
|
|
19
|
+
export function parseHealthCheckPayload(message) {
|
|
20
|
+
const result = HealthCheckPayloadSchema.safeParse(message);
|
|
21
|
+
if (result.success) {
|
|
22
|
+
return result.data;
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Generates a fake runId for health check streams.
|
|
28
|
+
* This runId passes server validation but is not associated with a real run.
|
|
29
|
+
* The server skips run validation for streams starting with `__health_check__`.
|
|
30
|
+
*/
|
|
31
|
+
function generateHealthCheckRunId() {
|
|
32
|
+
return `wrun_${generateId()}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Handles a health check message by writing the result to the world's stream.
|
|
36
|
+
* The caller can listen to this stream to get the health check response.
|
|
37
|
+
*
|
|
38
|
+
* @param healthCheck - The parsed health check payload
|
|
39
|
+
* @param endpoint - Which endpoint is responding ('workflow' or 'step')
|
|
40
|
+
*/
|
|
41
|
+
export async function handleHealthCheckMessage(healthCheck, endpoint) {
|
|
42
|
+
const world = getWorld();
|
|
43
|
+
const streamName = getHealthCheckStreamName(healthCheck.correlationId);
|
|
44
|
+
const response = JSON.stringify({
|
|
45
|
+
healthy: true,
|
|
46
|
+
endpoint,
|
|
47
|
+
correlationId: healthCheck.correlationId,
|
|
48
|
+
timestamp: Date.now(),
|
|
49
|
+
});
|
|
50
|
+
// Use a fake runId that passes validation.
|
|
51
|
+
// The stream name includes the correlationId for identification.
|
|
52
|
+
// The server skips run validation for health check streams.
|
|
53
|
+
const fakeRunId = generateHealthCheckRunId();
|
|
54
|
+
await world.writeToStream(streamName, fakeRunId, response);
|
|
55
|
+
await world.closeStream(streamName, fakeRunId);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Performs a health check by sending a message through the queue pipeline
|
|
59
|
+
* and verifying it is processed by the specified endpoint.
|
|
60
|
+
*
|
|
61
|
+
* This function bypasses Deployment Protection on Vercel because it goes
|
|
62
|
+
* through the queue infrastructure rather than direct HTTP.
|
|
63
|
+
*
|
|
64
|
+
* @param world - The World instance to use for the health check
|
|
65
|
+
* @param endpoint - Which endpoint to health check: 'workflow' or 'step'
|
|
66
|
+
* @param options - Optional configuration for the health check
|
|
67
|
+
* @returns Promise resolving to health check result
|
|
68
|
+
*/
|
|
69
|
+
// Poll interval for health check retries (ms)
|
|
70
|
+
const HEALTH_CHECK_POLL_INTERVAL = 100;
|
|
71
|
+
// Per-read timeout to prevent blocking forever on local world's EventEmitter
|
|
72
|
+
// (which doesn't work across processes)
|
|
73
|
+
const HEALTH_CHECK_READ_TIMEOUT = 500;
|
|
74
|
+
/**
|
|
75
|
+
* Read chunks from a stream with a timeout per read operation.
|
|
76
|
+
* Returns { chunks, timedOut } where timedOut indicates if a read timed out.
|
|
77
|
+
*/
|
|
78
|
+
async function readStreamWithTimeout(reader, readTimeout) {
|
|
79
|
+
const chunks = [];
|
|
80
|
+
let done = false;
|
|
81
|
+
let timedOut = false;
|
|
82
|
+
while (!done && !timedOut) {
|
|
83
|
+
const readPromise = reader.read();
|
|
84
|
+
const timeoutPromise = new Promise((resolve) => setTimeout(() => {
|
|
85
|
+
timedOut = true;
|
|
86
|
+
resolve({ done: true, value: undefined });
|
|
87
|
+
}, readTimeout));
|
|
88
|
+
const result = await Promise.race([readPromise, timeoutPromise]);
|
|
89
|
+
done = result.done;
|
|
90
|
+
if (result.value)
|
|
91
|
+
chunks.push(result.value);
|
|
92
|
+
}
|
|
93
|
+
return { chunks, timedOut };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Parse and validate a health check response from stream chunks.
|
|
97
|
+
* Returns the parsed response or null if invalid.
|
|
98
|
+
*/
|
|
99
|
+
function parseHealthCheckResponse(chunks) {
|
|
100
|
+
if (chunks.length === 0)
|
|
101
|
+
return null;
|
|
102
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
103
|
+
const combined = new Uint8Array(totalLength);
|
|
104
|
+
let offset = 0;
|
|
105
|
+
for (const chunk of chunks) {
|
|
106
|
+
combined.set(chunk, offset);
|
|
107
|
+
offset += chunk.length;
|
|
108
|
+
}
|
|
109
|
+
const responseText = new TextDecoder().decode(combined);
|
|
110
|
+
let response;
|
|
111
|
+
try {
|
|
112
|
+
response = JSON.parse(responseText);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
if (typeof response !== 'object' ||
|
|
118
|
+
response === null ||
|
|
119
|
+
!('healthy' in response) ||
|
|
120
|
+
typeof response.healthy !== 'boolean') {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return { healthy: response.healthy };
|
|
124
|
+
}
|
|
125
|
+
export async function healthCheck(world, endpoint, options) {
|
|
126
|
+
const timeout = options?.timeout ?? DEFAULT_HEALTH_CHECK_TIMEOUT;
|
|
127
|
+
const correlationId = `hc_${generateId()}`;
|
|
128
|
+
const streamName = getHealthCheckStreamName(correlationId);
|
|
129
|
+
const queueName = endpoint === 'workflow'
|
|
130
|
+
? '__wkf_workflow_health_check'
|
|
131
|
+
: '__wkf_step_health_check';
|
|
132
|
+
const startTime = Date.now();
|
|
133
|
+
try {
|
|
134
|
+
await world.queue(queueName, {
|
|
135
|
+
__healthCheck: true,
|
|
136
|
+
correlationId,
|
|
137
|
+
});
|
|
138
|
+
while (Date.now() - startTime < timeout) {
|
|
139
|
+
try {
|
|
140
|
+
const stream = await world.readFromStream(streamName);
|
|
141
|
+
const reader = stream.getReader();
|
|
142
|
+
const { chunks, timedOut } = await readStreamWithTimeout(reader, HEALTH_CHECK_READ_TIMEOUT);
|
|
143
|
+
if (timedOut) {
|
|
144
|
+
try {
|
|
145
|
+
reader.cancel();
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// Ignore cancel errors
|
|
149
|
+
}
|
|
150
|
+
await new Promise((resolve) => setTimeout(resolve, HEALTH_CHECK_POLL_INTERVAL));
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const response = parseHealthCheckResponse(chunks);
|
|
154
|
+
if (response) {
|
|
155
|
+
return response;
|
|
156
|
+
}
|
|
157
|
+
await new Promise((resolve) => setTimeout(resolve, HEALTH_CHECK_POLL_INTERVAL));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
await new Promise((resolve) => setTimeout(resolve, HEALTH_CHECK_POLL_INTERVAL));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
healthy: false,
|
|
165
|
+
error: `Health check timed out after ${timeout}ms`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
return {
|
|
170
|
+
healthy: false,
|
|
171
|
+
error: error instanceof Error ? error.message : String(error),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Loads all workflow run events by iterating through all pages of paginated results.
|
|
177
|
+
* This ensures that *all* events are loaded into memory before running the workflow.
|
|
178
|
+
* Events must be in chronological order (ascending) for proper workflow replay.
|
|
179
|
+
*/
|
|
180
|
+
export async function getAllWorkflowRunEvents(runId) {
|
|
181
|
+
const allEvents = [];
|
|
182
|
+
let cursor = null;
|
|
183
|
+
let hasMore = true;
|
|
184
|
+
const world = getWorld();
|
|
185
|
+
while (hasMore) {
|
|
186
|
+
// TODO: we're currently loading all the data with resolveRef behaviour. We need to update this
|
|
187
|
+
// to lazyload the data from the world instead so that we can optimize and make the event log loading
|
|
188
|
+
// much faster and memory efficient
|
|
189
|
+
const response = await world.events.list({
|
|
190
|
+
runId,
|
|
191
|
+
pagination: {
|
|
192
|
+
sortOrder: 'asc', // Required: events must be in chronological order for replay
|
|
193
|
+
cursor: cursor ?? undefined,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
allEvents.push(...response.data);
|
|
197
|
+
hasMore = response.hasMore;
|
|
198
|
+
cursor = response.cursor;
|
|
199
|
+
}
|
|
200
|
+
return allEvents;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* CORS headers for health check responses.
|
|
204
|
+
* Allows the observability UI to check endpoint health from a different origin.
|
|
205
|
+
*/
|
|
206
|
+
const HEALTH_CHECK_CORS_HEADERS = {
|
|
207
|
+
'Access-Control-Allow-Origin': '*',
|
|
208
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS, GET, HEAD',
|
|
209
|
+
'Access-Control-Allow-Headers': 'Content-Type',
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Wraps a request/response handler and adds a health check "mode"
|
|
213
|
+
* based on the presence of a `__health` query parameter.
|
|
214
|
+
*/
|
|
215
|
+
export function withHealthCheck(handler) {
|
|
216
|
+
return async (req) => {
|
|
217
|
+
const url = new URL(req.url);
|
|
218
|
+
const isHealthCheck = url.searchParams.has('__health');
|
|
219
|
+
if (isHealthCheck) {
|
|
220
|
+
// Handle CORS preflight for health check
|
|
221
|
+
if (req.method === 'OPTIONS') {
|
|
222
|
+
return new Response(null, {
|
|
223
|
+
status: 204,
|
|
224
|
+
headers: HEALTH_CHECK_CORS_HEADERS,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return new Response(`Workflow DevKit "${url.pathname}" endpoint is healthy`, {
|
|
228
|
+
status: 200,
|
|
229
|
+
headers: {
|
|
230
|
+
'Content-Type': 'text/plain',
|
|
231
|
+
...HEALTH_CHECK_CORS_HEADERS,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return await handler(req);
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Queues a message to the specified queue with tracing.
|
|
240
|
+
*/
|
|
241
|
+
export async function queueMessage(world, ...args) {
|
|
242
|
+
const queueName = args[0];
|
|
243
|
+
await trace('queueMessage', {
|
|
244
|
+
attributes: Attribute.QueueName(queueName),
|
|
245
|
+
kind: await getSpanKind('PRODUCER'),
|
|
246
|
+
}, async (span) => {
|
|
247
|
+
const { messageId } = await world.queue(...args);
|
|
248
|
+
span?.setAttributes(Attribute.QueueMessageId(messageId));
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Calculates the queue overhead time in milliseconds for a given message.
|
|
253
|
+
*/
|
|
254
|
+
export function getQueueOverhead(message) {
|
|
255
|
+
if (!message.requestedAt)
|
|
256
|
+
return;
|
|
257
|
+
try {
|
|
258
|
+
return Attribute.QueueOverheadMs(Date.now() - message.requestedAt.getTime());
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lL2hlbHBlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBTUEsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDM0QsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3hDLE9BQU8sS0FBSyxTQUFTLE1BQU0sc0NBQXNDLENBQUM7QUFDbEUsT0FBTyxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRXRDLHdEQUF3RDtBQUN4RCxNQUFNLDRCQUE0QixHQUFHLE1BQU0sQ0FBQztBQUU1QyxNQUFNLFVBQVUsR0FBRyxnQkFBZ0IsRUFBRSxDQUFDO0FBRXRDOztHQUVHO0FBQ0gsU0FBUyx3QkFBd0IsQ0FBQyxhQUFxQjtJQUNyRCxPQUFPLG1CQUFtQixhQUFhLEVBQUUsQ0FBQztBQUM1QyxDQUFDO0FBV0Q7OztHQUdHO0FBQ0gsTUFBTSxVQUFVLHVCQUF1QixDQUNyQyxPQUFnQjtJQUVoQixNQUFNLE1BQU0sR0FBRyx3QkFBd0IsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDM0QsSUFBSSxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDbkIsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDO0lBQ3JCLENBQUM7SUFDRCxPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILFNBQVMsd0JBQXdCO0lBQy9CLE9BQU8sUUFBUSxVQUFVLEVBQUUsRUFBRSxDQUFDO0FBQ2hDLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLHdCQUF3QixDQUM1QyxXQUErQixFQUMvQixRQUE2QjtJQUU3QixNQUFNLEtBQUssR0FBRyxRQUFRLEVBQUUsQ0FBQztJ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
|
|
@@ -13,9 +13,10 @@ export declare function getHookByToken(token: string): Promise<Hook>;
|
|
|
13
13
|
* This function is called externally (e.g., from an API route or server action)
|
|
14
14
|
* to send data to a hook and resume the associated workflow run.
|
|
15
15
|
*
|
|
16
|
-
* @param
|
|
16
|
+
* @param tokenOrHook - The unique token identifying the hook, or the hook object itself
|
|
17
17
|
* @param payload - The data payload to send to the hook
|
|
18
|
-
* @returns Promise resolving to
|
|
18
|
+
* @returns Promise resolving to the hook
|
|
19
|
+
* @throws Error if the hook is not found or if there's an error during the process
|
|
19
20
|
*
|
|
20
21
|
* @example
|
|
21
22
|
*
|
|
@@ -25,17 +26,17 @@ export declare function getHookByToken(token: string): Promise<Hook>;
|
|
|
25
26
|
*
|
|
26
27
|
* export async function POST(request: Request) {
|
|
27
28
|
* const { token, data } = await request.json();
|
|
28
|
-
* const result = await resumeHook(token, data);
|
|
29
29
|
*
|
|
30
|
-
*
|
|
30
|
+
* try {
|
|
31
|
+
* const hook = await resumeHook(token, data);
|
|
32
|
+
* return Response.json({ runId: hook.runId });
|
|
33
|
+
* } catch (error) {
|
|
31
34
|
* return new Response('Hook not found', { status: 404 });
|
|
32
35
|
* }
|
|
33
|
-
*
|
|
34
|
-
* return Response.json({ runId: result.runId });
|
|
35
36
|
* }
|
|
36
37
|
* ```
|
|
37
38
|
*/
|
|
38
|
-
export declare function resumeHook<T = any>(
|
|
39
|
+
export declare function resumeHook<T = any>(tokenOrHook: string | Hook, payload: T): Promise<Hook>;
|
|
39
40
|
/**
|
|
40
41
|
* Resumes a webhook by sending a {@link https://developer.mozilla.org/en-US/docs/Web/API/Request | Request}
|
|
41
42
|
* object to a hook identified by its token.
|
|
@@ -45,7 +46,8 @@ export declare function resumeHook<T = any>(token: string, payload: T): Promise<
|
|
|
45
46
|
*
|
|
46
47
|
* @param token - The unique token identifying the hook
|
|
47
48
|
* @param request - The request to send to the hook
|
|
48
|
-
* @returns Promise resolving to the response
|
|
49
|
+
* @returns Promise resolving to the response
|
|
50
|
+
* @throws Error if the hook is not found or if there's an error during the process
|
|
49
51
|
*
|
|
50
52
|
* @example
|
|
51
53
|
*
|
|
@@ -61,9 +63,12 @@ export declare function resumeHook<T = any>(token: string, payload: T): Promise<
|
|
|
61
63
|
* return new Response('Missing token', { status: 400 });
|
|
62
64
|
* }
|
|
63
65
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
66
|
+
* try {
|
|
67
|
+
* const response = await resumeWebhook(token, request);
|
|
68
|
+
* return response;
|
|
69
|
+
* } catch (error) {
|
|
70
|
+
* return new Response('Webhook not found', { status: 404 });
|
|
71
|
+
* }
|
|
67
72
|
* }
|
|
68
73
|
* ```
|
|
69
74
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resume-hook.d.ts","sourceRoot":"","sources":["../../src/runtime/resume-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"resume-hook.d.ts","sourceRoot":"","sources":["../../src/runtime/resume-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAyB,MAAM,iBAAiB,CAAC;AAWnE;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,GAAG,EACtC,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,OAAO,EAAE,CAAC,GACT,OAAO,CAAC,IAAI,CAAC,CAkFf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,QAAQ,CAAC,CAkDnB"}
|
|
@@ -4,6 +4,7 @@ import { dehydrateStepReturnValue, hydrateStepArguments, } from '../serializatio
|
|
|
4
4
|
import { WEBHOOK_RESPONSE_WRITABLE } from '../symbols.js';
|
|
5
5
|
import * as Attribute from '../telemetry/semantic-conventions.js';
|
|
6
6
|
import { getSpanContextForTraceCarrier, trace } from '../telemetry.js';
|
|
7
|
+
import { waitedUntil } from '../util.js';
|
|
7
8
|
import { getWorld } from './world.js';
|
|
8
9
|
/**
|
|
9
10
|
* Get the hook by token to find the associated workflow run,
|
|
@@ -16,7 +17,7 @@ export async function getHookByToken(token) {
|
|
|
16
17
|
const world = getWorld();
|
|
17
18
|
const hook = await world.hooks.getByToken(token);
|
|
18
19
|
if (typeof hook.metadata !== 'undefined') {
|
|
19
|
-
hook.metadata = hydrateStepArguments(hook.metadata, [],
|
|
20
|
+
hook.metadata = hydrateStepArguments(hook.metadata, [], hook.runId);
|
|
20
21
|
}
|
|
21
22
|
return hook;
|
|
22
23
|
}
|
|
@@ -26,9 +27,10 @@ export async function getHookByToken(token) {
|
|
|
26
27
|
* This function is called externally (e.g., from an API route or server action)
|
|
27
28
|
* to send data to a hook and resume the associated workflow run.
|
|
28
29
|
*
|
|
29
|
-
* @param
|
|
30
|
+
* @param tokenOrHook - The unique token identifying the hook, or the hook object itself
|
|
30
31
|
* @param payload - The data payload to send to the hook
|
|
31
|
-
* @returns Promise resolving to
|
|
32
|
+
* @returns Promise resolving to the hook
|
|
33
|
+
* @throws Error if the hook is not found or if there's an error during the process
|
|
32
34
|
*
|
|
33
35
|
* @example
|
|
34
36
|
*
|
|
@@ -38,69 +40,75 @@ export async function getHookByToken(token) {
|
|
|
38
40
|
*
|
|
39
41
|
* export async function POST(request: Request) {
|
|
40
42
|
* const { token, data } = await request.json();
|
|
41
|
-
* const result = await resumeHook(token, data);
|
|
42
43
|
*
|
|
43
|
-
*
|
|
44
|
+
* try {
|
|
45
|
+
* const hook = await resumeHook(token, data);
|
|
46
|
+
* return Response.json({ runId: hook.runId });
|
|
47
|
+
* } catch (error) {
|
|
44
48
|
* return new Response('Hook not found', { status: 404 });
|
|
45
49
|
* }
|
|
46
|
-
*
|
|
47
|
-
* return Response.json({ runId: result.runId });
|
|
48
50
|
* }
|
|
49
51
|
* ```
|
|
50
52
|
*/
|
|
51
|
-
export async function resumeHook(
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
53
|
+
export async function resumeHook(tokenOrHook, payload) {
|
|
54
|
+
return await waitedUntil(() => {
|
|
55
|
+
return trace('HOOK.resume', async (span) => {
|
|
56
|
+
const world = getWorld();
|
|
57
|
+
try {
|
|
58
|
+
const hook = typeof tokenOrHook === 'string'
|
|
59
|
+
? await getHookByToken(tokenOrHook)
|
|
60
|
+
: tokenOrHook;
|
|
61
|
+
span?.setAttributes({
|
|
62
|
+
...Attribute.HookToken(hook.token),
|
|
63
|
+
...Attribute.HookId(hook.hookId),
|
|
64
|
+
...Attribute.WorkflowRunId(hook.runId),
|
|
65
|
+
});
|
|
66
|
+
// Dehydrate the payload for storage
|
|
67
|
+
const ops = [];
|
|
68
|
+
const dehydratedPayload = dehydrateStepReturnValue(payload, ops, hook.runId);
|
|
69
|
+
// NOTE: Workaround instead of injecting catching undefined unhandled rejections in webhook bundle
|
|
70
|
+
waitUntil(Promise.all(ops).catch((err) => {
|
|
71
|
+
if (err !== undefined)
|
|
72
|
+
throw err;
|
|
73
|
+
}));
|
|
74
|
+
// Create a hook_received event with the payload
|
|
75
|
+
await world.events.create(hook.runId, {
|
|
76
|
+
eventType: 'hook_received',
|
|
77
|
+
correlationId: hook.hookId,
|
|
78
|
+
eventData: {
|
|
79
|
+
payload: dehydratedPayload,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
const workflowRun = await world.runs.get(hook.runId);
|
|
83
|
+
span?.setAttributes({
|
|
84
|
+
...Attribute.WorkflowName(workflowRun.workflowName),
|
|
85
|
+
});
|
|
86
|
+
const traceCarrier = workflowRun.executionContext?.traceCarrier;
|
|
87
|
+
if (traceCarrier) {
|
|
88
|
+
const context = await getSpanContextForTraceCarrier(traceCarrier);
|
|
89
|
+
if (context) {
|
|
90
|
+
span?.addLink?.({ context });
|
|
91
|
+
}
|
|
82
92
|
}
|
|
93
|
+
// Re-trigger the workflow against the deployment ID associated
|
|
94
|
+
// with the workflow run that the hook belongs to
|
|
95
|
+
await world.queue(`__wkf_workflow_${workflowRun.workflowName}`, {
|
|
96
|
+
runId: hook.runId,
|
|
97
|
+
// attach the trace carrier from the workflow run
|
|
98
|
+
traceCarrier: workflowRun.executionContext?.traceCarrier ?? undefined,
|
|
99
|
+
}, {
|
|
100
|
+
deploymentId: workflowRun.deploymentId,
|
|
101
|
+
});
|
|
102
|
+
return hook;
|
|
83
103
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
});
|
|
93
|
-
return hook;
|
|
94
|
-
}
|
|
95
|
-
catch (_err) {
|
|
96
|
-
// If hook not found, return null
|
|
97
|
-
span?.setAttributes({
|
|
98
|
-
...Attribute.HookToken(token),
|
|
99
|
-
...Attribute.HookFound(false),
|
|
100
|
-
});
|
|
101
|
-
// TODO: Check for specific error types
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
span?.setAttributes({
|
|
106
|
+
...Attribute.HookToken(typeof tokenOrHook === 'string' ? tokenOrHook : tokenOrHook.token),
|
|
107
|
+
...Attribute.HookFound(false),
|
|
108
|
+
});
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
104
112
|
});
|
|
105
113
|
}
|
|
106
114
|
/**
|
|
@@ -112,7 +120,8 @@ export async function resumeHook(token, payload) {
|
|
|
112
120
|
*
|
|
113
121
|
* @param token - The unique token identifying the hook
|
|
114
122
|
* @param request - The request to send to the hook
|
|
115
|
-
* @returns Promise resolving to the response
|
|
123
|
+
* @returns Promise resolving to the response
|
|
124
|
+
* @throws Error if the hook is not found or if there's an error during the process
|
|
116
125
|
*
|
|
117
126
|
* @example
|
|
118
127
|
*
|
|
@@ -128,9 +137,12 @@ export async function resumeHook(token, payload) {
|
|
|
128
137
|
* return new Response('Missing token', { status: 400 });
|
|
129
138
|
* }
|
|
130
139
|
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
140
|
+
* try {
|
|
141
|
+
* const response = await resumeWebhook(token, request);
|
|
142
|
+
* return response;
|
|
143
|
+
* } catch (error) {
|
|
144
|
+
* return new Response('Webhook not found', { status: 404 });
|
|
145
|
+
* }
|
|
134
146
|
* }
|
|
135
147
|
* ```
|
|
136
148
|
*/
|
|
@@ -159,7 +171,7 @@ export async function resumeWebhook(token, request) {
|
|
|
159
171
|
// No `respondWith` value implies the default behavior of returning a 202
|
|
160
172
|
response = new Response(null, { status: 202 });
|
|
161
173
|
}
|
|
162
|
-
await resumeHook(hook
|
|
174
|
+
await resumeHook(hook, request);
|
|
163
175
|
if (responseReadable) {
|
|
164
176
|
// Wait for the readable stream to emit one chunk,
|
|
165
177
|
// which is the `Response` object
|
|
@@ -177,4 +189,4 @@ export async function resumeWebhook(token, request) {
|
|
|
177
189
|
}
|
|
178
190
|
return response;
|
|
179
191
|
}
|
|
180
|
-
//# sourceMappingURL=
|
|
192
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzdW1lLWhvb2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcnVudGltZS9yZXN1bWUtaG9vay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDOUMsT0FBTyxFQUFFLFdBQVcsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRXJFLE9BQU8sRUFDTCx3QkFBd0IsRUFDeEIsb0JBQW9CLEdBQ3JCLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzFELE9BQU8sS0FBSyxTQUFTLE1BQU0sc0NBQXNDLENBQUM7QUFDbEUsT0FBTyxFQUFFLDZCQUE2QixFQUFFLEtBQUssRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDekMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUV0Qzs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLGNBQWMsQ0FBQyxLQUFhO0lBQ2hELE1BQU0sS0FBSyxHQUFHLFFBQVEsRUFBRSxDQUFDO0lBQ3pCLE1BQU0sSUFBSSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDakQsSUFBSSxPQUFPLElBQUksQ0FBQyxRQUFRLEtBQUssV0FBVyxFQUFFLENBQUM7UUFDekMsSUFBSSxDQUFDLFFBQVEsR0FBRyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsUUFBZSxFQUFFLEVBQUUsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDN0UsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBNEJHO0FBQ0gsTUFBTSxDQUFDLEtB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
|
package/dist/runtime/start.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
import type { World } from '@workflow/world';
|
|
1
2
|
import { Run } from '../runtime.js';
|
|
2
3
|
export interface StartOptions {
|
|
3
4
|
/**
|
|
4
5
|
* The deployment ID to use for the workflow run.
|
|
6
|
+
*
|
|
7
|
+
* @deprecated This property should not be set in user code under normal circumstances.
|
|
8
|
+
* It is automatically inferred from environment variables when deploying to Vercel.
|
|
9
|
+
* Only set this if you are doing something advanced and know what you are doing.
|
|
5
10
|
*/
|
|
6
11
|
deploymentId?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The world to use for the workflow run creation,
|
|
14
|
+
* by default the world is inferred from the environment variables.
|
|
15
|
+
*/
|
|
16
|
+
world?: World;
|
|
7
17
|
}
|
|
8
18
|
/**
|
|
9
19
|
* Represents an imported workflow function.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/runtime/start.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/runtime/start.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAyB,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAQpC,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,IAAI,CAC/D,GAAG,IAAI,EAAE,KAAK,KACX,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EACpD,QAAQ,EAAE,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,gBAAgB,EAC7D,IAAI,EAAE,KAAK,EACX,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;AAEzB,wBAAgB,KAAK,CAAC,OAAO,EAC3B,QAAQ,EAAE,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC,GAAG,gBAAgB,EAC1D,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC"}
|