@vite-hub/workflow 0.0.3 → 0.1.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 +201 -0
- package/README.md +187 -56
- package/dist/{_app-DgwN4ulW.d.ts → app-BojnC_yf.d.ts} +1 -4
- package/dist/{app-BZpq2cwC.js → app-CzPoP84f.js} +2 -1
- package/dist/client-DsGM3M4U.js +389 -0
- package/dist/cloudflare-env-BNrYo29x.js +113 -0
- package/dist/cloudflare-nF4abPd4.js +59 -0
- package/dist/cloudflare-shared-CaxrMD1A.d.ts +1 -0
- package/dist/{cloudflare-vite-B0zel2Do.js → cloudflare-vite-B3IabBsv.js} +18 -10
- package/dist/{config-BDqB6I4E.js → config-IJMb0eeZ.js} +39 -25
- package/dist/error-diagnostics-A_8Kp-48.js +116 -0
- package/dist/error-diagnostics-pwNFvAHP.js +67 -0
- package/dist/index.d.ts +24 -20
- package/dist/index.js +25 -334
- package/dist/inspect-B72iFyj-.js +70 -0
- package/dist/operations-B_rYG71W.js +230 -0
- package/dist/operations-tNUdj3G4.d.ts +212 -0
- package/dist/operations.d.ts +37 -0
- package/dist/operations.js +887 -0
- package/dist/out-CsaJxg8m.js +21755 -0
- package/dist/{provider-operation-CWIV4k42.js → provider-operation-CW2cYfVy.js} +13 -10
- package/dist/rolldown-runtime-CMFfr-1z.js +26 -0
- package/dist/runtime/cloudflare-runner.d.ts +8 -14
- package/dist/runtime/cloudflare-runner.js +77 -14
- package/dist/runtime/cloudflare-shared.d.ts +1 -0
- package/dist/runtime/cloudflare-shared.js +1 -1
- package/dist/runtime/cloudflare-vite.d.ts +8 -9
- package/dist/runtime/cloudflare-vite.js +2 -2
- package/dist/runtime/execute.d.ts +8 -7
- package/dist/runtime/execute.js +28 -5
- package/dist/runtime/openworkflow-worker.d.ts +6 -8
- package/dist/runtime/openworkflow-worker.js +43 -6
- package/dist/runtime/openworkflow.d.ts +8 -10
- package/dist/runtime/openworkflow.js +50 -32
- package/dist/runtime/state.d.ts +20 -21
- package/dist/runtime/state.js +90 -20
- package/dist/runtime/vercel-vite.d.ts +10 -11
- package/dist/runtime/vercel-vite.js +11 -8
- package/dist/runtime-type-DBCvIh5x.js +7 -0
- package/dist/{types-BUlYBZwH.d.ts → types-D82WmfZa.d.ts} +35 -18
- package/dist/{vercel-D2_J5D_Y.js → vercel-D-ZNtB_u.js} +59 -26
- package/dist/vite.d.ts +365 -6
- package/dist/vite.js +4667 -910
- package/package.json +78 -28
- package/dist/cloudflare-Cds1VIWa.js +0 -42
- package/dist/cloudflare-env-mFp2Uj6Z.js +0 -53
- package/dist/cloudflare-shared-BVVgDSdq.d.ts +0 -1
package/dist/index.js
CHANGED
|
@@ -1,354 +1,45 @@
|
|
|
1
|
-
import { t as normalizeWorkflowOptions } from "./config-
|
|
2
|
-
import {
|
|
3
|
-
import { i as
|
|
4
|
-
import { n as
|
|
5
|
-
import {
|
|
6
|
-
import { getOpenWorkflowRun, runOpenWorkflow } from "./runtime/openworkflow.js";
|
|
7
|
-
import { getWorkflowRunState, getWorkflowRuntimeConfig, getWorkflowRuntimeEvent, loadWorkflowDefinition, registerInlineWorkflowDefinition, runWithWorkflowRuntimeEvent, setWorkflowRun } from "./runtime/state.js";
|
|
8
|
-
import { a as startVercelWorkflow, n as inspectVercelWorkflowRun, r as resumeVercelWorkflowSignal, t as cancelVercelWorkflow } from "./vercel-D2_J5D_Y.js";
|
|
9
|
-
import { t as createWorkflowCloudflareWorker } from "./cloudflare-vite-B0zel2Do.js";
|
|
1
|
+
import { t as normalizeWorkflowOptions } from "./config-IJMb0eeZ.js";
|
|
2
|
+
import { t as workflowErrorDiagnostics } from "./error-diagnostics-pwNFvAHP.js";
|
|
3
|
+
import { i as getCloudflareWorkflowName, n as getCloudflareWorkflowBindingName, r as getCloudflareWorkflowClassName } from "./cloudflare-nF4abPd4.js";
|
|
4
|
+
import { a as resumeWorkflowSignal, i as getWorkflowRun, n as createWorkflow, o as runWorkflow, r as deferWorkflow, s as getVercelWorkflowName, t as cancelWorkflow } from "./client-DsGM3M4U.js";
|
|
5
|
+
import { t as createWorkflowCloudflareWorker } from "./cloudflare-vite-B3IabBsv.js";
|
|
10
6
|
//#region src/definition.ts
|
|
11
7
|
function defineWorkflow(handler, options) {
|
|
12
|
-
if (typeof handler !== "function") throw
|
|
13
|
-
if (typeof options !== "undefined" && (!options || typeof options !== "object" || Array.isArray(options))) throw
|
|
14
|
-
if (options?.native !== void 0 && typeof options.native !== "function") throw
|
|
8
|
+
if (typeof handler !== "function") throw workflowErrorDiagnostics.WORKFLOW_C0014({ message: "`defineWorkflow()` requires a workflow handler." });
|
|
9
|
+
if (typeof options !== "undefined" && (!options || typeof options !== "object" || Array.isArray(options))) throw workflowErrorDiagnostics.WORKFLOW_C0015({ message: "`defineWorkflow()` options must be a plain object." });
|
|
10
|
+
if (options?.native !== void 0 && typeof options.native !== "function") throw workflowErrorDiagnostics.WORKFLOW_C0016({ message: "`defineWorkflow()` native entry must be a workflow handler." });
|
|
15
11
|
return {
|
|
16
12
|
handler,
|
|
17
13
|
options
|
|
18
14
|
};
|
|
19
15
|
}
|
|
20
16
|
//#endregion
|
|
21
|
-
//#region src/integrations/vercel.ts
|
|
22
|
-
function getVercelWorkflowName(name) {
|
|
23
|
-
return `workflow--${encodeNameHex(name)}`;
|
|
24
|
-
}
|
|
25
|
-
//#endregion
|
|
26
|
-
//#region ../internal/src/runtime/random.ts
|
|
27
|
-
function randomId(prefix) {
|
|
28
|
-
const body = `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
29
|
-
return prefix ? `${prefix}_${body}` : body;
|
|
30
|
-
}
|
|
31
|
-
//#endregion
|
|
32
|
-
//#region src/runtime/adapters.ts
|
|
33
|
-
function unsupportedOperation(provider, operation) {
|
|
34
|
-
throw createWorkflowError({
|
|
35
|
-
code: "WORKFLOW_OPERATION_UNSUPPORTED",
|
|
36
|
-
details: {
|
|
37
|
-
operation,
|
|
38
|
-
provider
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
function resolveCloudflareBinding(event, binding, name) {
|
|
43
|
-
const bindingName = binding || getCloudflareWorkflowBindingName(name);
|
|
44
|
-
return getCloudflareEnv(event)?.[bindingName];
|
|
45
|
-
}
|
|
46
|
-
const cloudflareStatusMap = {
|
|
47
|
-
cancelled: "cancelled",
|
|
48
|
-
complete: "completed",
|
|
49
|
-
completed: "completed",
|
|
50
|
-
errored: "failed",
|
|
51
|
-
failed: "failed",
|
|
52
|
-
queued: "queued",
|
|
53
|
-
running: "running",
|
|
54
|
-
success: "completed",
|
|
55
|
-
terminated: "failed"
|
|
56
|
-
};
|
|
57
|
-
function normalizeCloudflareStatus(status) {
|
|
58
|
-
const value = typeof status === "object" && status ? status.status : status;
|
|
59
|
-
return cloudflareStatusMap[String(value || "").toLowerCase()] || "unknown";
|
|
60
|
-
}
|
|
61
|
-
function createCloudflareAdapter(config) {
|
|
62
|
-
return {
|
|
63
|
-
cancel: () => unsupportedOperation("cloudflare", "cancellation"),
|
|
64
|
-
async get({ event, id, name }) {
|
|
65
|
-
const binding = resolveCloudflareBinding(event, config.binding, name);
|
|
66
|
-
if (binding) {
|
|
67
|
-
const instance = await runWorkflowProviderOperation("cloudflare", "get", () => binding.get(id));
|
|
68
|
-
const metadata = await runWorkflowProviderOperation("cloudflare", "status", () => instance.status());
|
|
69
|
-
return {
|
|
70
|
-
id,
|
|
71
|
-
metadata,
|
|
72
|
-
provider: "cloudflare",
|
|
73
|
-
status: normalizeCloudflareStatus(metadata)
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
return await inlineAdapter(config).get({
|
|
77
|
-
event,
|
|
78
|
-
id,
|
|
79
|
-
name
|
|
80
|
-
});
|
|
81
|
-
},
|
|
82
|
-
async run({ definition, event, id, name, options, payload }) {
|
|
83
|
-
const binding = resolveCloudflareBinding(event, config.binding, name);
|
|
84
|
-
if (binding) {
|
|
85
|
-
const start = runWorkflowProviderOperation("cloudflare", "create", () => binding.create({
|
|
86
|
-
id,
|
|
87
|
-
params: payload
|
|
88
|
-
}));
|
|
89
|
-
const waitUntil = options.deferred ? resolveWaitUntil(event) : void 0;
|
|
90
|
-
if (waitUntil) waitUntil(start);
|
|
91
|
-
const instance = await start;
|
|
92
|
-
return {
|
|
93
|
-
id: instance.id,
|
|
94
|
-
metadata: await runWorkflowProviderOperation("cloudflare", "status", () => instance.status()),
|
|
95
|
-
payload,
|
|
96
|
-
provider: "cloudflare",
|
|
97
|
-
status: "queued"
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return await inlineAdapter(config).run({
|
|
101
|
-
definition,
|
|
102
|
-
event,
|
|
103
|
-
id,
|
|
104
|
-
name,
|
|
105
|
-
options,
|
|
106
|
-
payload
|
|
107
|
-
});
|
|
108
|
-
},
|
|
109
|
-
resume: () => unsupportedOperation("cloudflare", "signals")
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
function createOpenWorkflowAdapter(config) {
|
|
113
|
-
return {
|
|
114
|
-
cancel: () => unsupportedOperation("openworkflow", "cancellation"),
|
|
115
|
-
get: async ({ id, name }) => await getOpenWorkflowRun(config, name, id),
|
|
116
|
-
resume: () => unsupportedOperation("openworkflow", "signals"),
|
|
117
|
-
run: async ({ definition, name, options, payload }) => await runOpenWorkflow(config, name, payload, definition, options)
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
function inlineAdapter(config) {
|
|
121
|
-
return {
|
|
122
|
-
cancel: () => unsupportedOperation(config.provider, "cancellation"),
|
|
123
|
-
async get({ id, name }) {
|
|
124
|
-
const run = getWorkflowRunState(name, id);
|
|
125
|
-
if (run) {
|
|
126
|
-
if (run.status === "running") return {
|
|
127
|
-
id,
|
|
128
|
-
provider: config.provider,
|
|
129
|
-
status: "running"
|
|
130
|
-
};
|
|
131
|
-
return {
|
|
132
|
-
id,
|
|
133
|
-
metadata: run.error,
|
|
134
|
-
provider: config.provider,
|
|
135
|
-
result: run.result,
|
|
136
|
-
status: run.status
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
return {
|
|
140
|
-
id,
|
|
141
|
-
provider: config.provider,
|
|
142
|
-
status: "unknown"
|
|
143
|
-
};
|
|
144
|
-
},
|
|
145
|
-
async run({ definition, event, id, name, payload }) {
|
|
146
|
-
const runState = setWorkflowRun(name, id, Promise.resolve().then(() => runWorkflowHandler({
|
|
147
|
-
id,
|
|
148
|
-
name,
|
|
149
|
-
payload,
|
|
150
|
-
provider: config.provider
|
|
151
|
-
}, definition)).then((result) => ({
|
|
152
|
-
result,
|
|
153
|
-
status: "completed"
|
|
154
|
-
})).catch((error) => ({
|
|
155
|
-
error,
|
|
156
|
-
status: "failed"
|
|
157
|
-
})));
|
|
158
|
-
const waitUntil = resolveWaitUntil(event);
|
|
159
|
-
if (waitUntil) waitUntil(runState.promise);
|
|
160
|
-
return {
|
|
161
|
-
id,
|
|
162
|
-
metadata: config.provider === "vercel" ? {
|
|
163
|
-
mode: "inline",
|
|
164
|
-
workflow: getVercelWorkflowName(name)
|
|
165
|
-
} : void 0,
|
|
166
|
-
payload,
|
|
167
|
-
provider: config.provider,
|
|
168
|
-
status: "queued"
|
|
169
|
-
};
|
|
170
|
-
},
|
|
171
|
-
resume: () => unsupportedOperation(config.provider, "signals")
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
function createVercelAdapter(config) {
|
|
175
|
-
const fallback = inlineAdapter(config);
|
|
176
|
-
return {
|
|
177
|
-
async cancel({ id, name }) {
|
|
178
|
-
const definition = await loadWorkflowDefinition(name);
|
|
179
|
-
if (!definition?.options?.native) return await fallback.cancel({
|
|
180
|
-
event: void 0,
|
|
181
|
-
id,
|
|
182
|
-
name
|
|
183
|
-
});
|
|
184
|
-
return await cancelVercelWorkflow(name, definition, id);
|
|
185
|
-
},
|
|
186
|
-
async get({ id, name }) {
|
|
187
|
-
const definition = await loadWorkflowDefinition(name);
|
|
188
|
-
return definition?.options?.native ? await inspectVercelWorkflowRun(name, definition, id) : await fallback.get({
|
|
189
|
-
event: void 0,
|
|
190
|
-
id,
|
|
191
|
-
name
|
|
192
|
-
});
|
|
193
|
-
},
|
|
194
|
-
resume: async (token, payload) => await resumeVercelWorkflowSignal(token, payload),
|
|
195
|
-
async run({ definition, event, id, name, options, payload }) {
|
|
196
|
-
if (!definition.options?.native) return await fallback.run({
|
|
197
|
-
definition,
|
|
198
|
-
event,
|
|
199
|
-
id,
|
|
200
|
-
name,
|
|
201
|
-
options,
|
|
202
|
-
payload
|
|
203
|
-
});
|
|
204
|
-
if (options.id) throw createWorkflowError({
|
|
205
|
-
code: "WORKFLOW_RUN_ID_UNSUPPORTED",
|
|
206
|
-
details: {
|
|
207
|
-
...safeWorkflowName(name) ? { name } : {},
|
|
208
|
-
provider: "vercel"
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
return await startVercelWorkflow(name, definition, payload);
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
function getWorkflowRuntimeAdapter(config) {
|
|
216
|
-
if (config.provider === "cloudflare") return createCloudflareAdapter(config);
|
|
217
|
-
if (config.provider === "openworkflow") return createOpenWorkflowAdapter(config);
|
|
218
|
-
return createVercelAdapter(config);
|
|
219
|
-
}
|
|
220
|
-
//#endregion
|
|
221
|
-
//#region src/runtime/client.ts
|
|
222
|
-
function getActiveWorkflowConfig() {
|
|
223
|
-
const config = getWorkflowRuntimeConfig();
|
|
224
|
-
if (config === false) return false;
|
|
225
|
-
return config || normalizeWorkflowOptions(void 0, { hosting: "vercel" });
|
|
226
|
-
}
|
|
227
|
-
async function loadRequiredWorkflowDefinition(name) {
|
|
228
|
-
const definition = await loadWorkflowDefinition(name);
|
|
229
|
-
if (!definition) throw createWorkflowError({
|
|
230
|
-
code: "WORKFLOW_DEFINITION_NOT_FOUND",
|
|
231
|
-
details: safeWorkflowName(name) ? { name } : void 0
|
|
232
|
-
});
|
|
233
|
-
return definition;
|
|
234
|
-
}
|
|
235
|
-
function normalizeWorkflowRunIdValue(value) {
|
|
236
|
-
if (value === void 0) return null;
|
|
237
|
-
if (value === null || typeof value !== "object") return value;
|
|
238
|
-
if (Array.isArray(value)) return value.map((item) => normalizeWorkflowRunIdValue(item));
|
|
239
|
-
const record = value;
|
|
240
|
-
return Object.fromEntries(Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => [key, normalizeWorkflowRunIdValue(record[key])]));
|
|
241
|
-
}
|
|
242
|
-
async function sha256Hex(value) {
|
|
243
|
-
const data = new TextEncoder().encode(value);
|
|
244
|
-
const digest = await crypto.subtle.digest("SHA-256", data);
|
|
245
|
-
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
246
|
-
}
|
|
247
|
-
async function createStableWorkflowRunId(name, value) {
|
|
248
|
-
const hash = await sha256Hex(`${name}:${JSON.stringify(normalizeWorkflowRunIdValue(value))}`);
|
|
249
|
-
return `${name.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "workflow"}-${hash.slice(0, 32)}`;
|
|
250
|
-
}
|
|
251
|
-
async function resolveWorkflowStartOptions(name, payload, createOptions, startOptions) {
|
|
252
|
-
if (startOptions.id || !createOptions?.id) return startOptions;
|
|
253
|
-
return {
|
|
254
|
-
...startOptions,
|
|
255
|
-
id: await createStableWorkflowRunId(name, await createOptions.id({
|
|
256
|
-
name,
|
|
257
|
-
payload
|
|
258
|
-
}))
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
function createWorkflow(nameOrOptions, handlerOrOptions, options) {
|
|
262
|
-
if (typeof nameOrOptions === "object" && nameOrOptions !== null) {
|
|
263
|
-
const createOptions = nameOrOptions;
|
|
264
|
-
const { handler, name } = createOptions;
|
|
265
|
-
if (!name || typeof name !== "string") throw new TypeError("`createWorkflow()` requires a workflow name.");
|
|
266
|
-
if (typeof handler !== "function") throw new TypeError("`createWorkflow()` requires a workflow handler.");
|
|
267
|
-
registerInlineWorkflowDefinition(name, { handler });
|
|
268
|
-
return {
|
|
269
|
-
cancel: (id) => cancelWorkflow(name, id),
|
|
270
|
-
name,
|
|
271
|
-
defer: async (payload, options = {}) => deferWorkflow(name, payload, await resolveWorkflowStartOptions(name, payload, createOptions, options)),
|
|
272
|
-
getRun: (id) => getWorkflowRun(name, id),
|
|
273
|
-
run: async (payload, options = {}) => runWorkflow(name, payload, await resolveWorkflowStartOptions(name, payload, createOptions, options))
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
const name = nameOrOptions;
|
|
277
|
-
if (!name || typeof name !== "string") throw new TypeError("`createWorkflow()` requires a workflow name.");
|
|
278
|
-
const handler = typeof handlerOrOptions === "function" ? handlerOrOptions : typeof handlerOrOptions === "object" && handlerOrOptions !== null ? handlerOrOptions.handler : void 0;
|
|
279
|
-
const createOptions = typeof handlerOrOptions === "function" ? options : handlerOrOptions;
|
|
280
|
-
if (handler !== void 0) {
|
|
281
|
-
if (typeof handler !== "function") throw new TypeError("`createWorkflow()` requires a workflow handler.");
|
|
282
|
-
registerInlineWorkflowDefinition(name, { handler });
|
|
283
|
-
} else if (handlerOrOptions !== void 0 && (typeof handlerOrOptions !== "object" || handlerOrOptions === null)) throw new TypeError("`createWorkflow()` options must be an object.");
|
|
284
|
-
return {
|
|
285
|
-
cancel: (id) => cancelWorkflow(name, id),
|
|
286
|
-
name,
|
|
287
|
-
defer: async (payload, options = {}) => deferWorkflow(name, payload, await resolveWorkflowStartOptions(name, payload, createOptions, options)),
|
|
288
|
-
getRun: (id) => getWorkflowRun(name, id),
|
|
289
|
-
run: async (payload, options = {}) => runWorkflow(name, payload, await resolveWorkflowStartOptions(name, payload, createOptions, options))
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
async function runWorkflow(name, payload, options = {}) {
|
|
293
|
-
const config = getActiveWorkflowConfig();
|
|
294
|
-
if (config === false) throw createWorkflowError({ code: "WORKFLOW_DISABLED" });
|
|
295
|
-
const id = options.id || randomId("wrun");
|
|
296
|
-
const definition = await loadRequiredWorkflowDefinition(name);
|
|
297
|
-
return await getWorkflowRuntimeAdapter(config).run({
|
|
298
|
-
definition,
|
|
299
|
-
event: getWorkflowRuntimeEvent(),
|
|
300
|
-
id,
|
|
301
|
-
name,
|
|
302
|
-
options,
|
|
303
|
-
payload
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
async function getWorkflowRun(name, id) {
|
|
307
|
-
const config = getActiveWorkflowConfig();
|
|
308
|
-
if (config === false) throw createWorkflowError({ code: "WORKFLOW_DISABLED" });
|
|
309
|
-
return await getWorkflowRuntimeAdapter(config).get({
|
|
310
|
-
event: getWorkflowRuntimeEvent(),
|
|
311
|
-
id,
|
|
312
|
-
name
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
async function cancelWorkflow(name, id) {
|
|
316
|
-
const config = getActiveWorkflowConfig();
|
|
317
|
-
if (config === false) throw createWorkflowError({ code: "WORKFLOW_DISABLED" });
|
|
318
|
-
await loadRequiredWorkflowDefinition(name);
|
|
319
|
-
return await getWorkflowRuntimeAdapter(config).cancel({
|
|
320
|
-
event: getWorkflowRuntimeEvent(),
|
|
321
|
-
id,
|
|
322
|
-
name
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
async function resumeWorkflowSignal(token, payload) {
|
|
326
|
-
if (!token || typeof token !== "string") throw new TypeError("`resumeWorkflowSignal()` requires an opaque signal token.");
|
|
327
|
-
const config = getActiveWorkflowConfig();
|
|
328
|
-
if (config === false) throw createWorkflowError({ code: "WORKFLOW_DISABLED" });
|
|
329
|
-
return await getWorkflowRuntimeAdapter(config).resume(token, payload);
|
|
330
|
-
}
|
|
331
|
-
function deferWorkflow(name, payload, options = {}) {
|
|
332
|
-
return runWithWorkflowRuntimeEvent(getWorkflowRuntimeEvent(), () => runWorkflow(name, payload, {
|
|
333
|
-
...options,
|
|
334
|
-
deferred: true
|
|
335
|
-
}));
|
|
336
|
-
}
|
|
337
|
-
//#endregion
|
|
338
17
|
//#region src/runtime/payload.ts
|
|
339
18
|
async function readRequestPayload(request) {
|
|
340
|
-
if (
|
|
19
|
+
if (request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() === "application/json") return await request.json();
|
|
341
20
|
return await request.text();
|
|
342
21
|
}
|
|
343
22
|
async function validatePayload(payload, schema) {
|
|
344
23
|
if (typeof schema === "function") return await schema(payload);
|
|
345
|
-
if ("safeParse"
|
|
24
|
+
if (hasSchemaMethod(schema, "safeParse") && typeof schema.safeParse === "function") {
|
|
346
25
|
const result = schema.safeParse(payload);
|
|
347
|
-
if (!result.success) throw result.error ||
|
|
26
|
+
if (!result.success) throw result.error || workflowErrorDiagnostics.WORKFLOW_R0021({ message: "Invalid workflow payload." });
|
|
348
27
|
return result.data;
|
|
349
28
|
}
|
|
350
|
-
if ("parse"
|
|
351
|
-
throw
|
|
29
|
+
if (hasSchemaMethod(schema, "parse") && typeof schema.parse === "function") return schema.parse(payload);
|
|
30
|
+
throw workflowErrorDiagnostics.WORKFLOW_R0022({ message: "Invalid workflow payload schema." });
|
|
31
|
+
}
|
|
32
|
+
function hasSchemaMethod(value, key) {
|
|
33
|
+
if (Object.hasOwn(value, key)) return true;
|
|
34
|
+
let prototype = Object.getPrototypeOf(value);
|
|
35
|
+
while (prototype !== null) {
|
|
36
|
+
if (Object.hasOwn(prototype, key)) {
|
|
37
|
+
const constructor = Object.hasOwn(prototype, "constructor") ? prototype.constructor : void 0;
|
|
38
|
+
return typeof constructor === "function" && constructor.prototype === prototype && Function.prototype.toString.call(constructor) !== Function.prototype.toString.call(Object);
|
|
39
|
+
}
|
|
40
|
+
prototype = Object.getPrototypeOf(prototype);
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
352
43
|
}
|
|
353
44
|
async function readValidatedPayload(request, schema) {
|
|
354
45
|
return await validatePayload(await readRequestPayload(request), schema);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { relative } from "pathe";
|
|
2
|
+
//#region ../internal/src/inspect.ts
|
|
3
|
+
function relativeDefinitionFile(projectRoot, file) {
|
|
4
|
+
return relative(projectRoot, file).replaceAll("\\", "/");
|
|
5
|
+
}
|
|
6
|
+
const redactedInspectionValue = "[redacted]";
|
|
7
|
+
const secretKeyPattern = /secret|token|passw(?:or)?d|credential|api[-_\s]?key|private[-_\s]?key|authorization|cookie|signature|dsn|connection[-_\s]?string/i;
|
|
8
|
+
const secretValuePattern = /^(?:[a-z][a-z0-9+.-]*:\/\/[^/\s?#]*@|bearer\s)/i;
|
|
9
|
+
/** Whether an inspection field names a credential, regardless of its value. */
|
|
10
|
+
function isSecretInspectionKey(key) {
|
|
11
|
+
return secretKeyPattern.test(key);
|
|
12
|
+
}
|
|
13
|
+
const embeddedUrlCredentialPattern = /\b([a-z][a-z0-9+.-]*:\/\/)[^/\s?#]*@/gi;
|
|
14
|
+
const embeddedCookiePattern = /\b([\w-]*cookie[\w-]*["']?\s*[:=]\s*)(?:"(?:\\[^\r\n]|[^"\\\r\n])*"(?:\s*;\s*[\w.-]+\s*=[^;\r\n]*)*|'(?:\\[^\r\n]|[^'\\\r\n])*'(?:\s*;\s*[\w.-]+\s*=[^;\r\n]*)*|[^\r\n]+?(?=\s*(?:;\s*[^;=\r\n/]+\s*(?:\r?\n|$)|&|\r?\n|$)))/gi;
|
|
15
|
+
const embeddedBearerPattern = /\bbearer\s+[^\s,;]+/gi;
|
|
16
|
+
const embeddedAuthorizationPattern = new RegExp(String.raw`\b([\w-]*authorization[\w-]*["']?\s*[:=]\s*)(?!\[redacted\](?=$|[\s,;}&]))(?:"(?:\\[^\r\n]|[^"\\\r\n])*"|'(?:\\[^\r\n]|[^'\\\r\n])*'|[^\r\n]+?(?=\s*(?:[,;])?\s*(?:${secretKeyPattern.source})[\w-]*\s*[:=]|\s+rejected\b|$))`, "gi");
|
|
17
|
+
const embeddedSecretAssignmentPattern = new RegExp(String.raw`\b([\w-]*(?:${secretKeyPattern.source})[\w-]*["']?\s*[:=]\s*)(?!\[redacted\](?=$|[\s,;}&]))(?:"(?:\\[^\r\n]|[^"\\\r\n])*"|'(?:\\[^\r\n]|[^'\\\r\n])*'|[^\s,;&#&]+)`, "gi");
|
|
18
|
+
/**
|
|
19
|
+
* Removes credentials inside free text, for example an error message. URL credentials, bearer tokens, and
|
|
20
|
+
* `name=value` pairs with a secret name are replaced with `[redacted]`.
|
|
21
|
+
*/
|
|
22
|
+
function redactInspectionText(value) {
|
|
23
|
+
return value.replace(embeddedUrlCredentialPattern, `$1${redactedInspectionValue}@`).replace(embeddedAuthorizationPattern, `$1${redactedInspectionValue}`).replace(embeddedCookiePattern, `$1${redactedInspectionValue}`).replace(embeddedBearerPattern, `Bearer ${redactedInspectionValue}`).replace(embeddedSecretAssignmentPattern, `$1${redactedInspectionValue}`);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Removes values that can carry credentials before inspection output leaves the process.
|
|
27
|
+
* Keys that name secrets, plaintext Worker `vars`, and URLs with embedded credentials are redacted.
|
|
28
|
+
*/
|
|
29
|
+
function redactInspectionValue(value, key) {
|
|
30
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
31
|
+
let remaining = 1e3;
|
|
32
|
+
function visit(entry, entryKey, depth = 0) {
|
|
33
|
+
if (entryKey && isSecretInspectionKey(entryKey)) return redactedInspectionValue;
|
|
34
|
+
if (--remaining < 0 || depth > 10) return "[truncated]";
|
|
35
|
+
if (typeof entry === "string") {
|
|
36
|
+
const text = entry.slice(0, 1e4);
|
|
37
|
+
return secretValuePattern.test(text) ? redactedInspectionValue : redactInspectionText(text);
|
|
38
|
+
}
|
|
39
|
+
if (typeof entry === "bigint") return entry.toString();
|
|
40
|
+
if (typeof entry === "number") return Number.isFinite(entry) ? entry : String(entry);
|
|
41
|
+
if (entry === null || typeof entry === "boolean") return entry;
|
|
42
|
+
if (typeof entry !== "object") return entry === void 0 ? null : "[unsupported]";
|
|
43
|
+
if (seen.has(entry)) return "[circular]";
|
|
44
|
+
seen.add(entry);
|
|
45
|
+
try {
|
|
46
|
+
let dateTime;
|
|
47
|
+
try {
|
|
48
|
+
dateTime = Date.prototype.getTime.call(entry);
|
|
49
|
+
} catch {}
|
|
50
|
+
if (dateTime !== void 0) return Number.isNaN(dateTime) ? "Invalid Date" : Date.prototype.toISOString.call(entry);
|
|
51
|
+
if (Array.isArray(entry)) return entry.slice(0, 100).map((item) => visit(item, void 0, depth + 1));
|
|
52
|
+
let setValues;
|
|
53
|
+
try {
|
|
54
|
+
setValues = [...Set.prototype.values.call(entry)];
|
|
55
|
+
} catch {}
|
|
56
|
+
if (setValues) return setValues.slice(0, 100).map((item) => visit(item, void 0, depth + 1));
|
|
57
|
+
let mapEntries;
|
|
58
|
+
try {
|
|
59
|
+
mapEntries = [...Map.prototype.entries.call(entry)];
|
|
60
|
+
} catch {}
|
|
61
|
+
const entries = mapEntries ? mapEntries.map(([name, item]) => [String(name), item]) : Object.entries(entry);
|
|
62
|
+
return Object.fromEntries(entries.slice(0, 100).map(([name, item]) => [name, name === "vars" && item && typeof item === "object" && !Array.isArray(item) ? Object.fromEntries(Object.keys(item).slice(0, 100).map((variable) => [variable, redactedInspectionValue])) : visit(item, name, depth + 1)]));
|
|
63
|
+
} finally {
|
|
64
|
+
seen.delete(entry);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return visit(value, key);
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { redactInspectionValue as n, relativeDefinitionFile as r, redactInspectionText as t };
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { a as resumeWorkflowSignal, i as getWorkflowRun, o as runWorkflow, t as cancelWorkflow } from "./client-DsGM3M4U.js";
|
|
2
|
+
import { getInlineWorkflowDefinitions, getWorkflowRuntimeConfig, getWorkflowRuntimeRegistry } from "./runtime/state.js";
|
|
3
|
+
import { n as redactInspectionValue, t as redactInspectionText } from "./inspect-B72iFyj-.js";
|
|
4
|
+
//#region src/results.ts
|
|
5
|
+
const inlineRunNote = "Runs the Workflow inline in the Nitro runtime, as the app does in development. Inline runs are not durable and have no retries.";
|
|
6
|
+
function unsupported(note) {
|
|
7
|
+
return {
|
|
8
|
+
note,
|
|
9
|
+
supported: false
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Operations that each provider supports. A supported operation can still fail when the runtime decides, for example
|
|
14
|
+
* cancel on an inline Vercel run.
|
|
15
|
+
*/
|
|
16
|
+
function resolveWorkflowOperationSupport(provider) {
|
|
17
|
+
if (provider === "cloudflare") return {
|
|
18
|
+
cancel: unsupported("Cloudflare Workflows do not support cancellation through ViteHub."),
|
|
19
|
+
get: {
|
|
20
|
+
note: "Reads the run from the Cloudflare Workflow binding when the runtime has one.",
|
|
21
|
+
supported: true
|
|
22
|
+
},
|
|
23
|
+
resume: unsupported("Cloudflare Workflows do not support ViteHub signals."),
|
|
24
|
+
start: {
|
|
25
|
+
note: `Uses the Cloudflare Workflow binding when the runtime has one. Otherwise: ${inlineRunNote}`,
|
|
26
|
+
supported: true
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
if (provider === "openworkflow") return {
|
|
30
|
+
cancel: unsupported("OpenWorkflow does not support cancellation through ViteHub."),
|
|
31
|
+
get: {
|
|
32
|
+
note: "Reads the run from OpenWorkflow storage.",
|
|
33
|
+
supported: true
|
|
34
|
+
},
|
|
35
|
+
resume: unsupported("OpenWorkflow does not support ViteHub signals."),
|
|
36
|
+
start: {
|
|
37
|
+
note: "Enqueues the run in OpenWorkflow storage. The run stays queued until an OpenWorkflow worker processes the same storage.",
|
|
38
|
+
supported: true
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
cancel: {
|
|
43
|
+
note: "Only native Vercel Workflow runs can be cancelled. Inline runs return an unsupported error.",
|
|
44
|
+
supported: true
|
|
45
|
+
},
|
|
46
|
+
get: {
|
|
47
|
+
note: "Reads inline runs, which expire 5 minutes after they finish. Native Vercel Workflow runs need the Workflow DevKit runtime.",
|
|
48
|
+
supported: true
|
|
49
|
+
},
|
|
50
|
+
resume: {
|
|
51
|
+
note: "Signals need a native Vercel Workflow run and the Workflow DevKit runtime.",
|
|
52
|
+
supported: true
|
|
53
|
+
},
|
|
54
|
+
start: {
|
|
55
|
+
note: `${inlineRunNote} Native Vercel Workflow Definitions need the Workflow DevKit runtime.`,
|
|
56
|
+
supported: true
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/runtime/operations.ts
|
|
62
|
+
const maxRememberedRuns = 1024;
|
|
63
|
+
/** A failure of a Workflow operation, with the status and code that every surface reports. */
|
|
64
|
+
var WorkflowOperationError = class extends Error {
|
|
65
|
+
code;
|
|
66
|
+
fix;
|
|
67
|
+
status;
|
|
68
|
+
constructor(message, status, code, fix) {
|
|
69
|
+
super(message);
|
|
70
|
+
this.name = "WorkflowOperationError";
|
|
71
|
+
this.code = code;
|
|
72
|
+
this.status = status;
|
|
73
|
+
if (fix) this.fix = fix;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
function isRecord(value) {
|
|
77
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
78
|
+
}
|
|
79
|
+
function isResponse(value) {
|
|
80
|
+
if (value instanceof Response) return true;
|
|
81
|
+
const statusGetter = Object.getOwnPropertyDescriptor(Response.prototype, "status")?.get;
|
|
82
|
+
if (!statusGetter) return false;
|
|
83
|
+
try {
|
|
84
|
+
statusGetter.call(value);
|
|
85
|
+
return true;
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function jsonReplacer(_key, value) {
|
|
91
|
+
if (typeof value === "bigint") return value.toString();
|
|
92
|
+
if (value instanceof Error) return serializeError(value);
|
|
93
|
+
if (isResponse(value)) return { response: {
|
|
94
|
+
status: value.status,
|
|
95
|
+
statusText: value.statusText
|
|
96
|
+
} };
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
function toJsonValue(value) {
|
|
100
|
+
if (value === void 0) return void 0;
|
|
101
|
+
try {
|
|
102
|
+
const text = JSON.stringify(value, jsonReplacer);
|
|
103
|
+
return text === void 0 ? void 0 : redactInspectionValue(JSON.parse(text));
|
|
104
|
+
} catch {
|
|
105
|
+
return "[unserializable]";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function errorCode(error) {
|
|
109
|
+
if (!error || typeof error !== "object") return;
|
|
110
|
+
const code = Reflect.get(error, "code");
|
|
111
|
+
return typeof code === "string" ? code : void 0;
|
|
112
|
+
}
|
|
113
|
+
function serializeError(error) {
|
|
114
|
+
const code = errorCode(error);
|
|
115
|
+
return {
|
|
116
|
+
...code ? { code } : {},
|
|
117
|
+
message: redactInspectionText(error.message),
|
|
118
|
+
...error.name && error.name !== "Error" ? { name: error.name } : {}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function toIsoDate(value) {
|
|
122
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? void 0 : value.toISOString();
|
|
123
|
+
return typeof value === "string" ? value : void 0;
|
|
124
|
+
}
|
|
125
|
+
function isErrorLike(value) {
|
|
126
|
+
return value instanceof Error || isRecord(value) && typeof value.message === "string";
|
|
127
|
+
}
|
|
128
|
+
async function readResult(result) {
|
|
129
|
+
if (!isResponse(result)) return result;
|
|
130
|
+
const text = await result.clone().text();
|
|
131
|
+
if (!text) return void 0;
|
|
132
|
+
try {
|
|
133
|
+
return JSON.parse(text);
|
|
134
|
+
} catch {
|
|
135
|
+
return text;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Converts a runtime Workflow run to JSON data. The view omits the start
|
|
140
|
+
* payload and redacts credentials in the metadata, result, and error message.
|
|
141
|
+
*/
|
|
142
|
+
async function toWorkflowRunView(run, workflow) {
|
|
143
|
+
const createdAt = toIsoDate(run.createdAt);
|
|
144
|
+
const startedAt = toIsoDate(run.startedAt);
|
|
145
|
+
const completedAt = toIsoDate(run.completedAt);
|
|
146
|
+
const error = run.status === "failed" && isErrorLike(run.metadata) ? serializeError(run.metadata) : void 0;
|
|
147
|
+
const metadata = error ? void 0 : toJsonValue(run.metadata);
|
|
148
|
+
const result = toJsonValue(await readResult(run.result));
|
|
149
|
+
return {
|
|
150
|
+
...completedAt ? { completedAt } : {},
|
|
151
|
+
...createdAt ? { createdAt } : {},
|
|
152
|
+
...error ? { error } : {},
|
|
153
|
+
id: run.id,
|
|
154
|
+
...metadata === void 0 ? {} : { metadata },
|
|
155
|
+
provider: run.provider,
|
|
156
|
+
...result === void 0 ? {} : { result },
|
|
157
|
+
...startedAt ? { startedAt } : {},
|
|
158
|
+
status: run.status,
|
|
159
|
+
workflow
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function runtimeFailure(operation, provider, error) {
|
|
163
|
+
if (error instanceof WorkflowOperationError) return error;
|
|
164
|
+
const code = errorCode(error);
|
|
165
|
+
if (code === "WORKFLOW_OPERATION_UNSUPPORTED") return new WorkflowOperationError(`workflow ${operation} is not supported by the ${provider} provider for this run.`, 501, code);
|
|
166
|
+
if (code === "WORKFLOW_DISABLED") return new WorkflowOperationError("Workflow is disabled in this runtime.", 409, code, "Enable Workflow with `vitehub({ workflow: true })` or `hubWorkflow()`.");
|
|
167
|
+
if (code === "WORKFLOW_DEFINITION_NOT_FOUND") return new WorkflowOperationError("Workflow definition was not found.", 404, code);
|
|
168
|
+
if (code === "VERCEL_WORKFLOW_SDK_LOAD_FAILED" || code === "WORKFLOW_NATIVE_ENTRY_INVALID" || code === "WORKFLOW_NATIVE_ENTRY_REQUIRED") return new WorkflowOperationError(`workflow ${operation} needs the Vercel Workflow DevKit runtime, which the runtime could not load.`, 501, code);
|
|
169
|
+
if (code === "WORKFLOW_PROVIDER_OPERATION_FAILED") {
|
|
170
|
+
const cause = error instanceof Error ? error.cause : void 0;
|
|
171
|
+
return new WorkflowOperationError(`workflow ${operation} failed in the ${provider} provider.${cause instanceof Error && cause.message ? ` ${redactInspectionText(cause.message)}` : ""}`, 502, code);
|
|
172
|
+
}
|
|
173
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
174
|
+
return new WorkflowOperationError(redactInspectionText(message || "The Workflow operation failed."), 500, code || "WORKFLOW_OPERATION_FAILED");
|
|
175
|
+
}
|
|
176
|
+
function startableWorkflows() {
|
|
177
|
+
return [.../* @__PURE__ */ new Set([...Object.keys(getWorkflowRuntimeRegistry() ?? {}), ...getInlineWorkflowDefinitions().keys()])].sort();
|
|
178
|
+
}
|
|
179
|
+
const startedRuns = /* @__PURE__ */ new Map();
|
|
180
|
+
function rememberRun(id, workflow) {
|
|
181
|
+
startedRuns.delete(id);
|
|
182
|
+
startedRuns.set(id, workflow);
|
|
183
|
+
if (startedRuns.size > maxRememberedRuns) {
|
|
184
|
+
const oldest = startedRuns.keys().next().value;
|
|
185
|
+
if (oldest !== void 0) startedRuns.delete(oldest);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Runs one Workflow operation in the runtime of the app, so it uses the same Workflow state and registry as the app.
|
|
190
|
+
* It redacts credentials in run data and error messages. Failures are `WorkflowOperationError`s.
|
|
191
|
+
*/
|
|
192
|
+
async function runWorkflowOperation(operation, input) {
|
|
193
|
+
const runtimeConfig = getWorkflowRuntimeConfig();
|
|
194
|
+
if (runtimeConfig === false) throw new WorkflowOperationError("Workflow is disabled in this runtime.", 409, "WORKFLOW_DISABLED", "Enable Workflow with `vitehub({ workflow: true })` or `hubWorkflow()`.");
|
|
195
|
+
const provider = runtimeConfig?.provider ?? "vercel";
|
|
196
|
+
const support = resolveWorkflowOperationSupport(provider)[operation];
|
|
197
|
+
if (!support.supported) throw new WorkflowOperationError(`workflow ${operation} is not supported by the ${provider} provider. ${support.note}`, 501, "WORKFLOW_OPERATION_UNSUPPORTED");
|
|
198
|
+
try {
|
|
199
|
+
return await run(operation, input);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
throw runtimeFailure(operation, provider, error);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function run(operation, input) {
|
|
205
|
+
if (operation === "start") {
|
|
206
|
+
const { payload, workflow } = input;
|
|
207
|
+
const workflows = startableWorkflows();
|
|
208
|
+
if (!workflows.includes(workflow)) {
|
|
209
|
+
if (!getWorkflowRuntimeRegistry()) throw new WorkflowOperationError(`Unknown Workflow: ${workflow}. The runtime has no Workflow registry.`, 409, "WORKFLOW_REGISTRY_MISSING", "Restart the dev server. In development the Workflow Vite plugin installs the registry with the Nitro plugin .vitehub/nitro/workflow/dev-plugin.mjs.");
|
|
210
|
+
throw new WorkflowOperationError(`Unknown Workflow: ${workflow}.${workflows.length ? ` Available Workflows: ${workflows.join(", ")}.` : " The Workflow registry of the runtime is empty."}`, 404, "WORKFLOW_DEFINITION_NOT_FOUND");
|
|
211
|
+
}
|
|
212
|
+
const started = await runWorkflow(workflow, payload);
|
|
213
|
+
rememberRun(started.id, workflow);
|
|
214
|
+
return { run: await toWorkflowRunView(started, workflow) };
|
|
215
|
+
}
|
|
216
|
+
if (operation === "resume") {
|
|
217
|
+
const { payload, token } = input;
|
|
218
|
+
const signal = await resumeWorkflowSignal(token, payload);
|
|
219
|
+
return { signal: {
|
|
220
|
+
id: signal.id,
|
|
221
|
+
provider: signal.provider
|
|
222
|
+
} };
|
|
223
|
+
}
|
|
224
|
+
const { runId, workflow: named } = input;
|
|
225
|
+
const workflow = named ?? startedRuns.get(runId);
|
|
226
|
+
if (!workflow) throw new WorkflowOperationError(`Run ${runId} was not started by \`workflow.start\` in this runtime.`, 400, "WORKFLOW_RUN_UNKNOWN", "Pass the name of the Workflow with `--workflow <name>`.");
|
|
227
|
+
return { run: await toWorkflowRunView(operation === "get" ? await getWorkflowRun(workflow, runId) : await cancelWorkflow(workflow, runId), workflow) };
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
export { WorkflowOperationError, runWorkflowOperation, toWorkflowRunView };
|