@vite-hub/workflow 0.0.4 → 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.
Files changed (45) hide show
  1. package/README.md +34 -14
  2. package/dist/{_app-DgwN4ulW.d.ts → app-BojnC_yf.d.ts} +1 -4
  3. package/dist/{app-BZpq2cwC.js → app-CzPoP84f.js} +2 -1
  4. package/dist/client-DsGM3M4U.js +389 -0
  5. package/dist/cloudflare-env-BNrYo29x.js +113 -0
  6. package/dist/{cloudflare-D0Di-QYL.js → cloudflare-nF4abPd4.js} +20 -4
  7. package/dist/cloudflare-shared-CaxrMD1A.d.ts +1 -0
  8. package/dist/{cloudflare-vite-BkDgB6Sr.js → cloudflare-vite-B3IabBsv.js} +5 -4
  9. package/dist/{config-BHknO_rT.js → config-IJMb0eeZ.js} +38 -30
  10. package/dist/error-diagnostics-A_8Kp-48.js +116 -0
  11. package/dist/error-diagnostics-pwNFvAHP.js +67 -0
  12. package/dist/index.d.ts +20 -21
  13. package/dist/index.js +25 -369
  14. package/dist/inspect-B72iFyj-.js +70 -0
  15. package/dist/operations-B_rYG71W.js +230 -0
  16. package/dist/operations-tNUdj3G4.d.ts +212 -0
  17. package/dist/operations.d.ts +37 -0
  18. package/dist/operations.js +887 -0
  19. package/dist/out-CsaJxg8m.js +21755 -0
  20. package/dist/rolldown-runtime-CMFfr-1z.js +26 -0
  21. package/dist/runtime/cloudflare-runner.d.ts +5 -15
  22. package/dist/runtime/cloudflare-runner.js +40 -5
  23. package/dist/runtime/cloudflare-shared.d.ts +1 -0
  24. package/dist/runtime/cloudflare-shared.js +1 -1
  25. package/dist/runtime/cloudflare-vite.d.ts +8 -10
  26. package/dist/runtime/cloudflare-vite.js +1 -1
  27. package/dist/runtime/execute.d.ts +8 -7
  28. package/dist/runtime/execute.js +28 -5
  29. package/dist/runtime/openworkflow-worker.d.ts +6 -8
  30. package/dist/runtime/openworkflow-worker.js +5 -4
  31. package/dist/runtime/openworkflow.d.ts +8 -10
  32. package/dist/runtime/openworkflow.js +23 -17
  33. package/dist/runtime/state.d.ts +20 -21
  34. package/dist/runtime/state.js +90 -20
  35. package/dist/runtime/vercel-vite.d.ts +10 -12
  36. package/dist/runtime/vercel-vite.js +2 -2
  37. package/dist/runtime-type-DBCvIh5x.js +7 -0
  38. package/dist/{types-B9RdRkKh.d.ts → types-D82WmfZa.d.ts} +23 -12
  39. package/dist/{vercel-B0MuIVgJ.js → vercel-D-ZNtB_u.js} +34 -9
  40. package/dist/vite.d.ts +325 -11
  41. package/dist/vite.js +1267 -691
  42. package/package.json +79 -29
  43. package/dist/cloudflare-env-DjxQNbrx.js +0 -58
  44. package/dist/cloudflare-shared-BVVgDSdq.d.ts +0 -1
  45. package/dist/runtime-type-DGUn8Ple.js +0 -37
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # @vite-hub/workflow
2
2
 
3
3
  <p>
4
- <a href="https://vitehub.dev"><img alt="ViteHub" src="https://img.shields.io/badge/ViteHub-vitehub.dev-646cff?style=flat-square"></a>
5
- <img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-ready-3178c6?style=flat-square">
6
- <img alt="Vite" src="https://img.shields.io/badge/Vite-discovery-646cff?style=flat-square">
7
- <img alt="Workflow" src="https://img.shields.io/badge/Workflow-durable%20steps-7c3aed?style=flat-square">
4
+ <a href="https://vitehub.dev"><img alt="ViteHub" src="https://comark-ui.onmax.me/badge.svg?label=ViteHub&amp;value=vitehub.dev"></a>
5
+ <img alt="TypeScript" src="https://comark-ui.onmax.me/badge.svg?label=TypeScript&amp;value=ready">
6
+ <img alt="Vite" src="https://comark-ui.onmax.me/badge.svg?label=Vite&amp;value=discovery">
7
+ <img alt="Workflow" src="https://comark-ui.onmax.me/badge.svg?label=Workflow&amp;value=durable+steps">
8
8
  </p>
9
9
 
10
10
  `@vite-hub/workflow` discovers named long-running work and exposes one provider-neutral API for starting and inspecting runs.
11
11
 
12
- Use a Workflow when the application needs a run id, durable state, retries, cancellation, or resumable work. Use [Queue](https://vitehub.dev/docs/server-primitives/queue) when background delivery is enough and the application does not need to inspect a run.
12
+ Use a Workflow when the application needs a run id, durable state, retries, cancellation, or resumable work. Use [Queue](https://vitehub.dev/docs/queue) when background delivery is enough and the application does not need to inspect a run.
13
13
 
14
14
  ## Install
15
15
 
@@ -23,10 +23,14 @@ Then install the dependency required by the selected provider:
23
23
  | -------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------ |
24
24
  | Cloudflare Workflows | None in application code | Cloudflare owns run state. |
25
25
  | Vercel Workflow | `workflow` and `@workflow/builders` for native durable definitions | Native definitions survive function restarts; plain handlers run inline. |
26
- | OpenWorkflow | `openworkflow`; add `postgres` when using Postgres | Requires explicit SQLite or Postgres storage. |
26
+ | OpenWorkflow | `openworkflow`; add `postgres` when using Postgres | Uses local SQLite by default. Configure durable storage for deployment. |
27
27
 
28
28
  Importing the provider-neutral package root does not load OpenWorkflow types. Worker lifecycle helpers live at `@vite-hub/workflow/runtime/openworkflow-worker`.
29
29
 
30
+ Set `workflow.sqlite.path` or `workflow.postgres.url` to select OpenWorkflow storage. An explicit value takes precedence over ambient storage environment variables. A configured environment declaration must resolve to a value; missing configuration rejects before opening a backend. With no storage configuration or storage environment variables, OpenWorkflow uses `.vitehub/data/openworkflow.sqlite.db`.
31
+
32
+ The former `workflow.database` option was never connected to Named Databases. It now rejects instead of silently selecting fallback storage. Replace it with `workflow.sqlite.path` or `workflow.postgres.url`.
33
+
30
34
  ## Define a workflow
31
35
 
32
36
  ```ts
@@ -52,7 +56,6 @@ ViteHub discovers `server/workflows/<name>.ts`, folder workflows such as `server
52
56
  // server/workflow-runtime.ts
53
57
  import type {
54
58
  ResolvedWorkflowOptions,
55
- WorkflowDefinition,
56
59
  WorkflowDefinitionRegistry,
57
60
  } from "@vite-hub/workflow";
58
61
  import {
@@ -70,9 +73,7 @@ export const workflowConfig = {
70
73
  } satisfies ResolvedWorkflowOptions;
71
74
 
72
75
  const workflowRegistry = {
73
- "onboard-user": async () => ({
74
- default: (await import("./workflows/onboard-user.ts")).default as WorkflowDefinition,
75
- }),
76
+ "onboard-user": () => import("./workflows/onboard-user.ts"),
76
77
  } satisfies WorkflowDefinitionRegistry;
77
78
 
78
79
  export function installWorkflowRuntime() {
@@ -147,6 +148,10 @@ The other runtime helpers are:
147
148
 
148
149
  Unsupported provider operations fail with `WORKFLOW_OPERATION_UNSUPPORTED`; ViteHub does not pretend that an inline run was cancelled or resumed.
149
150
 
151
+ Inline runs buffer the response body before completion. Each inspection and duplicate start returns a fresh response, so reading or cancelling one result does not consume later results. A body read failure marks the run as failed.
152
+
153
+ Inline run inspection uses weak references while execution is active. A reachable execution remains inspectable; an abandoned execution with no remaining owner can be garbage-collected and then reports `unknown`. Completed inline runs are retained for five minutes, up to 1,024 entries per runtime. Completed history does not evict active executions. If either `WeakRef` or `FinalizationRegistry` is unavailable, active inspection uses strong references capped at 1,024 entries. Starting another run evicts the oldest active inspection entry, even if execution is still reachable; it reports `unknown` and completion does not restore it. Use a durable provider when inspection must survive abandonment or a process restart.
154
+
150
155
  ## Make a Vercel workflow durable
151
156
 
152
157
  A plain Vercel definition executes inline and does not survive a function restart. For durable execution, keep the same context-shaped handler and register a native Workflow DevKit entry:
@@ -156,7 +161,7 @@ pnpm add workflow @workflow/builders
156
161
  ```
157
162
 
158
163
  ```ts
159
- import { defineWorkflow, type WorkflowExecutionContext } from "@vite-hub/workflow";
164
+ import { defineWorkflow, type WorkflowExecutionContext, type WorkflowProviderContext } from "@vite-hub/workflow";
160
165
 
161
166
  interface OnboardPayload {
162
167
  email: string;
@@ -168,7 +173,7 @@ async function upsertUserStep(email: string) {
168
173
  return await upsertUser(email, { idempotencyKey: `onboard-user:${email}` });
169
174
  }
170
175
 
171
- async function durableOnboard({ payload }: WorkflowExecutionContext<OnboardPayload>) {
176
+ async function durableOnboard({ payload }: WorkflowProviderContext<OnboardPayload>) {
172
177
  "use workflow";
173
178
 
174
179
  const user = await upsertUserStep(payload.email);
@@ -183,7 +188,7 @@ async function inlineOnboard({ payload }: WorkflowExecutionContext<OnboardPayloa
183
188
  export default defineWorkflow(inlineOnboard, { native: durableOnboard });
184
189
  ```
185
190
 
186
- ViteHub transforms `native` when it generates Vercel output. Other providers keep using the normal handler. Put external side effects in idempotent `use step` functions because a durable step may be retried.
191
+ ViteHub transforms `native` when it generates Vercel output. Other providers keep using the normal handler. Vercel calls `native` with a `WorkflowProviderContext`, which has no `steps` runner. Put external side effects in idempotent `use step` functions because a durable step may be retried.
187
192
 
188
193
  ## Handle stable failures
189
194
 
@@ -216,8 +221,23 @@ async function transcribe(recordingId: string) {
216
221
 
217
222
  ## Documentation
218
223
 
219
- - [Workflow guide](https://vitehub.dev/docs/server-primitives/workflows)
224
+ - [Workflow guide](https://vitehub.dev/docs/workflows)
220
225
  - [Runtime and host support](https://vitehub.dev/docs/frameworks-hosts/support-matrix)
221
226
  - [OpenWorkflow](https://openworkflow.dev/docs/overview)
222
227
  - [Cloudflare Workflows](https://developers.cloudflare.com/workflows/)
223
228
  - [Vercel Workflow](https://vercel.com/docs/workflow)
229
+
230
+
231
+ ## Required handle input
232
+
233
+ A typed Workflow handle requires a payload when its handler requires one. Both `run()` and `defer()` use this rule. A handle with an optional payload or `void` input can still run with no argument. To set start options for a no-input Workflow, pass `undefined` first.
234
+
235
+ ```ts
236
+ const welcome = createWorkflow<{ email: string }>("welcome", async ({ payload }) => {
237
+ await sendWelcomeEmail(payload.email)
238
+ })
239
+ await welcome.run({ email: "ada@example.com" })
240
+ // welcome.run() is a type error.
241
+ ```
242
+
243
+ This is a breaking type correction. Supply the required payload at each handle call. Named operational functions do not infer a handler from a string, and persisted run results remain unknown.
@@ -5,7 +5,4 @@ type VitehubApp = AppHandler | {
5
5
  request?: (request: Request, options?: RequestInit, context?: Record<string, unknown>) => Response | Promise<Response>;
6
6
  };
7
7
  //#endregion
8
- //#region src/runtime/_app.d.ts
9
- type WorkflowApp = VitehubApp;
10
- //#endregion
11
- export { WorkflowApp as t };
8
+ export { VitehubApp as t };
@@ -1,10 +1,11 @@
1
+ import { t as internalErrorDiagnostics } from "./error-diagnostics-A_8Kp-48.js";
1
2
  //#region ../internal/src/runtime/app.ts
2
3
  function resolveAppFetch(label, app) {
3
4
  if (!app) return;
4
5
  if (typeof app === "function") return app;
5
6
  if (typeof app.request === "function") return (request, context) => app.request(request, void 0, context);
6
7
  if (typeof app.fetch === "function") return app.fetch.bind(app);
7
- throw new TypeError(`Invalid ${label} app. Expected an h3 app or a fetch-compatible handler.`);
8
+ throw internalErrorDiagnostics.INTERNAL_R0011({ message: `Invalid ${label} app. Expected an h3 app or a fetch-compatible handler.` });
8
9
  }
9
10
  //#endregion
10
11
  export { resolveAppFetch as t };
@@ -0,0 +1,389 @@
1
+ import { t as normalizeWorkflowOptions } from "./config-IJMb0eeZ.js";
2
+ import { t as workflowErrorDiagnostics } from "./error-diagnostics-pwNFvAHP.js";
3
+ import { n as getCloudflareWorkflowBindingName, o as encodeNameHex } from "./cloudflare-nF4abPd4.js";
4
+ import { t as hasRuntimeType } from "./runtime-type-DBCvIh5x.js";
5
+ import { a as createWorkflowError, i as safeWorkflowName, r as runWorkflowProviderOperation } from "./provider-operation-CW2cYfVy.js";
6
+ import { n as getCloudflareEnv, r as resolveWaitUntil } from "./cloudflare-env-BNrYo29x.js";
7
+ import { runSerializedWorkflowHandler } from "./runtime/execute.js";
8
+ import { getOpenWorkflowRun, runOpenWorkflow } from "./runtime/openworkflow.js";
9
+ import { getWorkflowRunState, getWorkflowRuntimeConfig, getWorkflowRuntimeEvent, getWorkflowRuntimeRegistry, loadWorkflowDefinition, registerInlineWorkflowDefinition, runWithWorkflowRuntimeEvent, setWorkflowRun } from "./runtime/state.js";
10
+ import { a as startVercelWorkflow, n as inspectVercelWorkflowRun, r as resumeVercelWorkflowSignal, t as cancelVercelWorkflow } from "./vercel-D-ZNtB_u.js";
11
+ import { deserializeResponse, isSerializedResponse } from "@vite-hub/runtime";
12
+ //#region src/integrations/vercel.ts
13
+ function getVercelWorkflowName(name) {
14
+ return `workflow--${encodeNameHex(name)}`;
15
+ }
16
+ //#endregion
17
+ //#region ../internal/src/runtime/random.ts
18
+ function randomId(prefix) {
19
+ const body = `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
20
+ return prefix ? `${prefix}_${body}` : body;
21
+ }
22
+ //#endregion
23
+ //#region src/runtime/adapters.ts
24
+ function resolveDispatchObserver(event) {
25
+ if (!event || !hasRuntimeType(event, "object")) return void 0;
26
+ const candidate = Object.hasOwn(event, "onDispatch") ? Reflect.get(event, "onDispatch") : void 0;
27
+ return hasRuntimeType(candidate, "function") ? candidate : void 0;
28
+ }
29
+ function resolveSettlementObserver(event) {
30
+ if (!event || !hasRuntimeType(event, "object")) return void 0;
31
+ const candidate = Object.hasOwn(event, "settled") ? Reflect.get(event, "settled") : void 0;
32
+ if (hasRuntimeType(candidate, "function")) return candidate;
33
+ const context = Object.hasOwn(event, "context") ? Reflect.get(event, "context") : void 0;
34
+ if (!context || !hasRuntimeType(context, "object")) return void 0;
35
+ const contextObject = Object(context);
36
+ const nested = Object.hasOwn(contextObject, "settled") ? Reflect.get(contextObject, "settled") : void 0;
37
+ return hasRuntimeType(nested, "function") ? nested : void 0;
38
+ }
39
+ function unsupportedOperation(provider, operation) {
40
+ throw createWorkflowError({
41
+ code: "WORKFLOW_OPERATION_UNSUPPORTED",
42
+ details: {
43
+ operation,
44
+ provider
45
+ }
46
+ });
47
+ }
48
+ function resolveCloudflareBinding(event, binding, name, definition) {
49
+ const env = getCloudflareEnv(event);
50
+ const bindingName = definition?.internalAgentInvocationRecovery ? getCloudflareWorkflowBindingName(name) : binding || getCloudflareWorkflowBindingName(name);
51
+ return env && Object.hasOwn(env, bindingName) ? env[bindingName] : void 0;
52
+ }
53
+ function resolveCloudflareInspectionBinding(event, binding, name) {
54
+ return resolveCloudflareBinding(event, binding, name, getWorkflowRuntimeRegistry()?.[name]);
55
+ }
56
+ function normalizeCloudflareWorkflowStart(value, expectedId) {
57
+ if (!hasRuntimeType(value, "object") || value === null) throw new Error("Cloudflare Workflow provider returned an invalid workflow instance.");
58
+ let id;
59
+ try {
60
+ id = Reflect.get(value, "id");
61
+ } catch (cause) {
62
+ throw new Error("Cloudflare Workflow provider returned an invalid workflow instance.", { cause });
63
+ }
64
+ if (!hasRuntimeType(id, "string") || !id || id !== expectedId) throw new Error("Cloudflare Workflow provider returned an invalid workflow instance.");
65
+ return { id };
66
+ }
67
+ const cloudflareStatusMap = {
68
+ cancelled: "cancelled",
69
+ complete: "completed",
70
+ completed: "completed",
71
+ errored: "failed",
72
+ failed: "failed",
73
+ queued: "queued",
74
+ running: "running",
75
+ success: "completed",
76
+ terminated: "failed"
77
+ };
78
+ function normalizeCloudflareStatus(status) {
79
+ const value = hasRuntimeType(status, "object") && status && Object.hasOwn(status, "status") ? status.status : status;
80
+ const normalized = String(value || "").toLowerCase();
81
+ return Object.hasOwn(cloudflareStatusMap, normalized) ? cloudflareStatusMap[normalized] : "unknown";
82
+ }
83
+ function ownSerializedOutput(metadata) {
84
+ if (!hasRuntimeType(metadata, "object") || metadata === null || !Object.hasOwn(metadata, "output")) return void 0;
85
+ const output = metadata.output;
86
+ return isSerializedResponse(output) ? output : void 0;
87
+ }
88
+ function hasUnknownWorkflowAcknowledgement(error) {
89
+ if (!error || !hasRuntimeType(error, "object")) return false;
90
+ const details = error.details;
91
+ return Boolean(details && hasRuntimeType(details, "object") && details.acknowledgement === "unknown");
92
+ }
93
+ function createCloudflareAdapter(config) {
94
+ return {
95
+ cancel: () => unsupportedOperation("cloudflare", "cancellation"),
96
+ async get({ event, id, name }) {
97
+ const binding = resolveCloudflareInspectionBinding(event, config.binding, name);
98
+ if (binding) {
99
+ const instance = await runWorkflowProviderOperation("cloudflare", "get", () => binding.get(id));
100
+ const metadata = await runWorkflowProviderOperation("cloudflare", "status", () => instance.status());
101
+ const output = ownSerializedOutput(metadata);
102
+ return {
103
+ id,
104
+ metadata,
105
+ provider: "cloudflare",
106
+ result: output ? deserializeResponse(output) : void 0,
107
+ status: normalizeCloudflareStatus(metadata)
108
+ };
109
+ }
110
+ return await inlineAdapter(config).get({
111
+ event,
112
+ id,
113
+ name
114
+ });
115
+ },
116
+ async run({ definition, event, id, name, options, payload }) {
117
+ const binding = resolveCloudflareBinding(event, config.binding, name, definition);
118
+ if (binding) {
119
+ resolveDispatchObserver(event)?.();
120
+ const start = () => runWorkflowProviderOperation("cloudflare", "create", async () => normalizeCloudflareWorkflowStart((await binding.createBatch([{
121
+ id,
122
+ params: payload
123
+ }]))[0] || await binding.get(id), id), { acknowledgementUnknown: (_error, status) => status === void 0 });
124
+ const creation = start().catch(async (firstError) => {
125
+ try {
126
+ return await start();
127
+ } catch (retryError) {
128
+ if (hasUnknownWorkflowAcknowledgement(firstError)) throw firstError;
129
+ throw retryError;
130
+ }
131
+ });
132
+ const waitUntil = options.deferred ? resolveWaitUntil(event) : void 0;
133
+ if (waitUntil) waitUntil(creation);
134
+ return {
135
+ id: (await creation).id,
136
+ payload,
137
+ provider: "cloudflare",
138
+ status: "queued"
139
+ };
140
+ }
141
+ return await inlineAdapter(config).run({
142
+ definition,
143
+ event,
144
+ id,
145
+ name,
146
+ options,
147
+ payload
148
+ });
149
+ },
150
+ resume: () => unsupportedOperation("cloudflare", "signals")
151
+ };
152
+ }
153
+ function createOpenWorkflowAdapter(config) {
154
+ return {
155
+ cancel: () => unsupportedOperation("openworkflow", "cancellation"),
156
+ get: async ({ id, name }) => await getOpenWorkflowRun(config, name, id),
157
+ resume: () => unsupportedOperation("openworkflow", "signals"),
158
+ run: async ({ definition, event, name, options, payload }) => await runOpenWorkflow(config, name, payload, definition, options, resolveDispatchObserver(event))
159
+ };
160
+ }
161
+ function inlineAdapter(config) {
162
+ return {
163
+ cancel: () => unsupportedOperation(config.provider, "cancellation"),
164
+ async get({ id, name }) {
165
+ const run = getWorkflowRunState(name, id);
166
+ if (run) {
167
+ if (run.status === "running") return {
168
+ id,
169
+ provider: config.provider,
170
+ status: "running"
171
+ };
172
+ return {
173
+ id,
174
+ metadata: run.error,
175
+ provider: config.provider,
176
+ result: isSerializedResponse(run.result) ? deserializeResponse(run.result) : run.result,
177
+ status: run.status
178
+ };
179
+ }
180
+ return {
181
+ id,
182
+ provider: config.provider,
183
+ status: "unknown"
184
+ };
185
+ },
186
+ async run({ definition, event, id, name, payload }) {
187
+ const existing = getWorkflowRunState(name, id);
188
+ if (existing) return {
189
+ id,
190
+ metadata: existing.error ?? (config.provider === "vercel" ? {
191
+ mode: "inline",
192
+ workflow: getVercelWorkflowName(name)
193
+ } : void 0),
194
+ payload: existing.payload,
195
+ provider: config.provider,
196
+ result: isSerializedResponse(existing.result) ? deserializeResponse(existing.result) : existing.result,
197
+ status: existing.status === "running" ? "queued" : existing.status
198
+ };
199
+ resolveDispatchObserver(event)?.();
200
+ const run = Promise.resolve().then(() => runSerializedWorkflowHandler({
201
+ id,
202
+ name,
203
+ payload,
204
+ provider: config.provider
205
+ }, definition)).then((result) => ({
206
+ result,
207
+ status: "completed"
208
+ })).catch((error) => ({
209
+ error,
210
+ status: "failed"
211
+ }));
212
+ const runState = setWorkflowRun(name, id, run, payload, true);
213
+ const waitUntil = resolveWaitUntil(event);
214
+ if (waitUntil) waitUntil(runState.promise);
215
+ return {
216
+ id,
217
+ metadata: config.provider === "vercel" ? {
218
+ mode: "inline",
219
+ workflow: getVercelWorkflowName(name)
220
+ } : void 0,
221
+ payload,
222
+ provider: config.provider,
223
+ status: "queued"
224
+ };
225
+ },
226
+ resume: () => unsupportedOperation(config.provider, "signals")
227
+ };
228
+ }
229
+ function createVercelAdapter(config) {
230
+ const fallback = inlineAdapter(config);
231
+ return {
232
+ async cancel({ id, name }) {
233
+ const definition = await loadWorkflowDefinition(name);
234
+ if (!definition?.options?.native) return await fallback.cancel({
235
+ event: void 0,
236
+ id,
237
+ name
238
+ });
239
+ return await cancelVercelWorkflow(name, definition, id);
240
+ },
241
+ async get({ id, name }) {
242
+ const definition = await loadWorkflowDefinition(name);
243
+ return definition?.options?.native ? await inspectVercelWorkflowRun(name, definition, id) : await fallback.get({
244
+ event: void 0,
245
+ id,
246
+ name
247
+ });
248
+ },
249
+ resume: async (token, payload) => await resumeVercelWorkflowSignal(token, payload),
250
+ async run({ definition, event, id, name, options, payload }) {
251
+ if (!definition.options?.native) return await fallback.run({
252
+ definition,
253
+ event,
254
+ id,
255
+ name,
256
+ options,
257
+ payload
258
+ });
259
+ if (options.id) throw createWorkflowError({
260
+ code: "WORKFLOW_RUN_ID_UNSUPPORTED",
261
+ details: {
262
+ ...safeWorkflowName(name) ? { name } : {},
263
+ provider: "vercel"
264
+ }
265
+ });
266
+ return await startVercelWorkflow(name, definition, payload, resolveSettlementObserver(event), resolveDispatchObserver(event));
267
+ }
268
+ };
269
+ }
270
+ function getWorkflowRuntimeAdapter(config) {
271
+ if (config.provider === "cloudflare") return createCloudflareAdapter(config);
272
+ if (config.provider === "openworkflow") return createOpenWorkflowAdapter(config);
273
+ return createVercelAdapter(config);
274
+ }
275
+ //#endregion
276
+ //#region src/runtime/client.ts
277
+ function getActiveWorkflowConfig() {
278
+ const config = getWorkflowRuntimeConfig();
279
+ if (config === false) throw createWorkflowError({ code: "WORKFLOW_DISABLED" });
280
+ return config || normalizeWorkflowOptions(void 0, { hosting: "vercel" });
281
+ }
282
+ async function loadRequiredWorkflowDefinition(name) {
283
+ const definition = await loadWorkflowDefinition(name);
284
+ if (!definition) throw createWorkflowError({
285
+ code: "WORKFLOW_DEFINITION_NOT_FOUND",
286
+ details: safeWorkflowName(name) ? { name } : void 0
287
+ });
288
+ return definition;
289
+ }
290
+ function normalizeWorkflowRunIdValue(value) {
291
+ if (value === void 0) return null;
292
+ if (value === null || !hasRuntimeType(value, "object")) return value;
293
+ if (Array.isArray(value)) return value.map((item) => normalizeWorkflowRunIdValue(item));
294
+ const record = value;
295
+ return Object.fromEntries(Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => [key, normalizeWorkflowRunIdValue(record[key])]));
296
+ }
297
+ async function sha256Hex(value) {
298
+ const data = new TextEncoder().encode(value);
299
+ const digest = await crypto.subtle.digest("SHA-256", data);
300
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
301
+ }
302
+ async function createStableWorkflowRunId(name, value) {
303
+ const hash = await sha256Hex(`${name}:${JSON.stringify(normalizeWorkflowRunIdValue(value))}`);
304
+ return `${name.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "workflow"}-${hash.slice(0, 32)}`;
305
+ }
306
+ async function resolveWorkflowStartOptions(name, payload, createOptions, startOptions) {
307
+ if (startOptions.id || !createOptions?.id) return startOptions;
308
+ return {
309
+ ...startOptions,
310
+ id: await createStableWorkflowRunId(name, await createOptions.id({
311
+ name,
312
+ payload
313
+ }))
314
+ };
315
+ }
316
+ function createWorkflow(nameOrOptions, handlerOrOptions, options) {
317
+ let name;
318
+ let handler;
319
+ let createOptions;
320
+ if (hasRuntimeType(nameOrOptions, "object") && nameOrOptions !== null) {
321
+ createOptions = nameOrOptions;
322
+ if (!createOptions.name || !hasRuntimeType(createOptions.name, "string")) throw workflowErrorDiagnostics.WORKFLOW_R0009({ message: "`createWorkflow()` requires a workflow name." });
323
+ if (!hasRuntimeType(createOptions.handler, "function")) throw workflowErrorDiagnostics.WORKFLOW_R0010({ message: "`createWorkflow()` requires a workflow handler." });
324
+ name = createOptions.name;
325
+ handler = createOptions.handler;
326
+ } else {
327
+ name = nameOrOptions;
328
+ if (!name || !hasRuntimeType(name, "string")) throw workflowErrorDiagnostics.WORKFLOW_R0011({ message: "`createWorkflow()` requires a workflow name." });
329
+ handler = hasRuntimeType(handlerOrOptions, "function") ? handlerOrOptions : hasRuntimeType(handlerOrOptions, "object") && handlerOrOptions !== null ? handlerOrOptions.handler : void 0;
330
+ createOptions = hasRuntimeType(handlerOrOptions, "function") ? options : handlerOrOptions;
331
+ if (handler !== void 0 && !hasRuntimeType(handler, "function")) throw workflowErrorDiagnostics.WORKFLOW_R0012({ message: "`createWorkflow()` requires a workflow handler." });
332
+ if (handler === void 0 && handlerOrOptions !== void 0 && (!hasRuntimeType(handlerOrOptions, "object") || handlerOrOptions === null)) throw workflowErrorDiagnostics.WORKFLOW_R0013({ message: "`createWorkflow()` options must be an object." });
333
+ }
334
+ if (handler !== void 0) registerInlineWorkflowDefinition(name, {
335
+ handler,
336
+ ...createOptions?.rootStep === void 0 ? {} : { options: { rootStep: createOptions.rootStep } }
337
+ });
338
+ return {
339
+ cancel: (id) => cancelWorkflow(name, id),
340
+ name,
341
+ defer: async (payload, options = {}) => deferWorkflow(name, payload, await resolveWorkflowStartOptions(name, payload, createOptions, options)),
342
+ getRun: (id) => getWorkflowRun(name, id),
343
+ async run(payload, options = {}) {
344
+ return await runWorkflow(name, payload, await resolveWorkflowStartOptions(name, payload, createOptions, options));
345
+ }
346
+ };
347
+ }
348
+ async function runWorkflow(name, payload, options = {}) {
349
+ const config = getActiveWorkflowConfig();
350
+ const id = options.id || randomId("wrun");
351
+ const definition = await loadRequiredWorkflowDefinition(name);
352
+ return await getWorkflowRuntimeAdapter(config).run({
353
+ definition,
354
+ event: getWorkflowRuntimeEvent(),
355
+ id,
356
+ name,
357
+ options,
358
+ payload
359
+ });
360
+ }
361
+ async function getWorkflowRun(name, id) {
362
+ return await getWorkflowRuntimeAdapter(getActiveWorkflowConfig()).get({
363
+ event: getWorkflowRuntimeEvent(),
364
+ id,
365
+ name
366
+ });
367
+ }
368
+ async function cancelWorkflow(name, id) {
369
+ const config = getActiveWorkflowConfig();
370
+ await loadRequiredWorkflowDefinition(name);
371
+ return await getWorkflowRuntimeAdapter(config).cancel({
372
+ event: getWorkflowRuntimeEvent(),
373
+ id,
374
+ name
375
+ });
376
+ }
377
+ async function resumeWorkflowSignal(token, payload) {
378
+ if (!token || !hasRuntimeType(token, "string")) throw workflowErrorDiagnostics.WORKFLOW_R0014({ message: "`resumeWorkflowSignal()` requires an opaque signal token." });
379
+ return await getWorkflowRuntimeAdapter(getActiveWorkflowConfig()).resume(token, payload);
380
+ }
381
+ function deferWorkflow(name, payload, options = {}) {
382
+ const request = getWorkflowRuntimeEvent();
383
+ return runWithWorkflowRuntimeEvent(request, () => runWorkflow(name, payload, {
384
+ ...options,
385
+ deferred: true
386
+ }));
387
+ }
388
+ //#endregion
389
+ export { resumeWorkflowSignal as a, getWorkflowRun as i, createWorkflow as n, runWorkflow as o, deferWorkflow as r, getVercelWorkflowName as s, cancelWorkflow as t };
@@ -0,0 +1,113 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ //#region ../internal/src/runtime/cloudflare-env.ts
3
+ let activeEnv;
4
+ const activeEnvStorageKey = Symbol.for("vitehub.cloudflare.env-storage");
5
+ const runtimeGlobal = globalThis;
6
+ const activeEnvStorage = runtimeGlobal[activeEnvStorageKey] ||= new AsyncLocalStorage();
7
+ const globalEnvProbe = Symbol("vitehub.cloudflare.global-env-probe");
8
+ /**
9
+ * Under the Nitro Cloudflare dev server, `cloudflare:workers` exports unenv's env proxy, which reads every key from
10
+ * `globalThis.__env__`. Storing that proxy in `__env__` makes it read itself, and the next read overflows the stack.
11
+ */
12
+ function readsGlobalEnv(env) {
13
+ const scope = globalThis;
14
+ const owned = Object.hasOwn(scope, "__env__");
15
+ const previous = scope.__env__;
16
+ scope.__env__ = { [globalEnvProbe]: true };
17
+ try {
18
+ return Reflect.get(env, globalEnvProbe) === true;
19
+ } catch {
20
+ return false;
21
+ } finally {
22
+ if (owned) scope.__env__ = previous;
23
+ else delete scope.__env__;
24
+ }
25
+ }
26
+ function setActiveCloudflareEnv(env) {
27
+ activeEnv = env;
28
+ if (!env || !readsGlobalEnv(env)) globalThis.__env__ = env;
29
+ try {
30
+ activeEnvStorage.enterWith({ env });
31
+ } catch {}
32
+ }
33
+ function runWithActiveCloudflareEnv(env, callback) {
34
+ return activeEnvStorage.run({ env }, callback);
35
+ }
36
+ function getActiveCloudflareEnv() {
37
+ const scoped = activeEnvStorage.getStore();
38
+ if (scoped) return scoped.env;
39
+ const host = globalThis;
40
+ return Object.hasOwn(host, "__env__") && host.__env__ !== void 0 ? host.__env__ : activeEnv;
41
+ }
42
+ function ownValue(value, key) {
43
+ if (value === null || typeof value !== "object" && typeof value !== "function" || !Object.hasOwn(value, key)) return void 0;
44
+ return Reflect.get(value, key);
45
+ }
46
+ function ownObject(value) {
47
+ return value !== null && (typeof value === "object" || typeof value === "function") ? value : void 0;
48
+ }
49
+ /** The objects where H3 1, H3 2, and Nitro hosts put Cloudflare `env` and `context`, in lookup order. */
50
+ function cloudflareOwners(target) {
51
+ const context = ownObject(ownValue(target, "context"));
52
+ const platform = ownObject(ownValue(context, "_platform"));
53
+ const req = ownObject(ownValue(target, "req"));
54
+ const nodeReq = ownObject(ownValue(ownObject(ownValue(target, "node")), "req"));
55
+ return [
56
+ ownObject(ownValue(context, "cloudflare")),
57
+ ownObject(ownValue(platform, "cloudflare")),
58
+ ownObject(ownValue(ownObject(ownValue(req, "runtime")), "cloudflare")),
59
+ ownObject(ownValue(ownObject(ownValue(nodeReq, "runtime")), "cloudflare"))
60
+ ];
61
+ }
62
+ function getCloudflareEnv(event, options = {}) {
63
+ const target = ownObject(event);
64
+ for (const candidate of [ownValue(target, "env"), ...cloudflareOwners(target).map((owner) => ownValue(owner, "env"))]) if (candidate) return candidate;
65
+ return options.fallback === false ? void 0 : getActiveCloudflareEnv();
66
+ }
67
+ function resolveWaitUntil(event, options = {}) {
68
+ const target = ownObject(event);
69
+ const context = ownObject(ownValue(target, "context"));
70
+ const cloudflare = ownObject(ownValue(context, "cloudflare"));
71
+ const cloudflareContext = ownObject(ownValue(cloudflare, "context"));
72
+ const platformCloudflare = ownObject(ownValue(ownObject(ownValue(context, "_platform")), "cloudflare"));
73
+ const platformCloudflareContext = ownObject(ownValue(platformCloudflare, "context"));
74
+ const req = ownObject(ownValue(target, "req"));
75
+ const reqCloudflare = ownObject(ownValue(ownObject(ownValue(req, "runtime")), "cloudflare"));
76
+ const reqCloudflareContext = ownObject(ownValue(reqCloudflare, "context"));
77
+ const nodeReq = ownObject(ownValue(ownObject(ownValue(target, "node")), "req"));
78
+ const nodeCloudflare = ownObject(ownValue(ownObject(ownValue(nodeReq, "runtime")), "cloudflare"));
79
+ const hosts = [
80
+ context,
81
+ cloudflare,
82
+ cloudflareContext,
83
+ platformCloudflare,
84
+ platformCloudflareContext,
85
+ req,
86
+ reqCloudflare,
87
+ reqCloudflareContext,
88
+ nodeReq,
89
+ nodeCloudflare,
90
+ ownObject(ownValue(nodeCloudflare, "context"))
91
+ ];
92
+ const owners = options.preferHost ? [...hosts, target] : [target, ...hosts];
93
+ for (const owner of owners) {
94
+ const waitUntil = ownValue(owner, "waitUntil");
95
+ if (typeof waitUntil === "function") return waitUntil.bind(owner);
96
+ }
97
+ }
98
+ function createCloudflareRuntimeEvent(env, context) {
99
+ const waitUntil = typeof context?.waitUntil === "function" ? context.waitUntil.bind(context) : void 0;
100
+ return {
101
+ context: {
102
+ cloudflare: {
103
+ env,
104
+ context
105
+ },
106
+ waitUntil
107
+ },
108
+ env,
109
+ waitUntil
110
+ };
111
+ }
112
+ //#endregion
113
+ export { setActiveCloudflareEnv as a, runWithActiveCloudflareEnv as i, getCloudflareEnv as n, resolveWaitUntil as r, createCloudflareRuntimeEvent as t };