@vite-hub/workflow 0.0.0 → 0.0.2
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/README.md +110 -1
- package/dist/_app-DgwN4ulW.d.ts +11 -0
- package/dist/app-BZpq2cwC.js +10 -0
- package/dist/cloudflare-Cds1VIWa.js +42 -0
- package/dist/cloudflare-env-CIjdFTv6.d.ts +22 -0
- package/dist/cloudflare-env-mFp2Uj6Z.js +53 -0
- package/dist/cloudflare-shared-BVVgDSdq.d.ts +1 -0
- package/dist/cloudflare-vite-B0zel2Do.js +39 -0
- package/dist/config-BDqB6I4E.js +130 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.js +357 -0
- package/dist/provider-operation-CWIV4k42.js +62 -0
- package/dist/runtime/cloudflare-runner.d.ts +27 -0
- package/dist/runtime/cloudflare-runner.js +24 -0
- package/dist/runtime/cloudflare-shared.d.ts +2 -0
- package/dist/runtime/cloudflare-shared.js +2 -0
- package/dist/runtime/cloudflare-vite.d.ts +17 -0
- package/dist/runtime/cloudflare-vite.js +2 -0
- package/dist/runtime/execute.d.ts +11 -0
- package/dist/runtime/execute.js +25 -0
- package/dist/runtime/openworkflow-worker.d.ts +20 -0
- package/dist/runtime/openworkflow-worker.js +112 -0
- package/dist/runtime/openworkflow.d.ts +23 -0
- package/dist/runtime/openworkflow.js +216 -0
- package/dist/runtime/state.d.ts +35 -0
- package/dist/runtime/state.js +179 -0
- package/dist/runtime/vercel-vite.d.ts +69 -0
- package/dist/runtime/vercel-vite.js +49 -0
- package/dist/types-BUlYBZwH.d.ts +163 -0
- package/dist/vercel-D2_J5D_Y.js +217 -0
- package/dist/vite.d.ts +13 -0
- package/dist/vite.js +2037 -0
- package/package.json +90 -5
package/README.md
CHANGED
|
@@ -1,3 +1,112 @@
|
|
|
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">
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
`@vite-hub/workflow` defines long-running work once and starts it through one `runWorkflow()` API.
|
|
11
|
+
|
|
12
|
+
Vercel definitions can register a native entry for durable execution. Definitions without one remain source-compatible, but execute inline and do not survive a function restart.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm add @vite-hub/workflow
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Add the provider dependency for the workflow provider you configure.
|
|
21
|
+
OpenWorkflow worker lifecycle helpers live at `@vite-hub/workflow/runtime/openworkflow-worker`, so importing the provider-agnostic package root does not require OpenWorkflow's types.
|
|
22
|
+
|
|
23
|
+
## Minimal API
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
// server/workflows/welcome.ts
|
|
27
|
+
import { defineWorkflow } from "@vite-hub/workflow"
|
|
28
|
+
|
|
29
|
+
export default defineWorkflow<{ email: string }>(async ({ id, payload, step }) => {
|
|
30
|
+
const email = await step?.do?.("send-email", {}, async () => {
|
|
31
|
+
return { sentTo: payload.email }
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
return { id, email }
|
|
35
|
+
})
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For a durable Vercel run, keep the same context-shaped interface and register a native entry containing the Workflow DevKit directive:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { defineWorkflow, type WorkflowExecutionContext } from "@vite-hub/workflow"
|
|
42
|
+
|
|
43
|
+
interface WelcomePayload {
|
|
44
|
+
email: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function durableWelcome({ payload }: WorkflowExecutionContext<WelcomePayload>) {
|
|
48
|
+
"use workflow"
|
|
49
|
+
|
|
50
|
+
return { sentTo: payload.email }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function inlineWelcome({ payload }: WorkflowExecutionContext<WelcomePayload>) {
|
|
54
|
+
return { sentTo: payload.email }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default defineWorkflow(inlineWelcome, { native: durableWelcome })
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
ViteHub transforms the native entry with Workflow DevKit when it generates Vercel provider output.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
// server/api/welcome.post.ts
|
|
64
|
+
import { getWorkflowRun, runWorkflow } from "@vite-hub/workflow"
|
|
65
|
+
import { defineEventHandler, readBody } from "h3"
|
|
66
|
+
|
|
67
|
+
export default defineEventHandler(async (event) => {
|
|
68
|
+
const run = await runWorkflow("welcome", await readBody<{ email: string }>(event))
|
|
69
|
+
return getWorkflowRun("welcome", run.id)
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`getWorkflowRun()` normalizes provider run and step state, including timestamps, attempts, and failures. `cancelWorkflow()` cancels a durable run, and `resumeWorkflowSignal()` forwards an opaque signal token created inside the native workflow. Providers that cannot perform an operation fail explicitly instead of simulating it.
|
|
74
|
+
|
|
75
|
+
Throw `ViteHubError` when app callers need a stable, inspectable Workflow failure instead of parsing log output or provider-specific messages. ViteHub-owned failures use the package's fixed `WorkflowErrorCode` vocabulary.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { ViteHubError } from "@vite-hub/runtime"
|
|
79
|
+
|
|
80
|
+
async function transcribe(recordingId: string) {
|
|
81
|
+
try {
|
|
82
|
+
return await transcribeRecording(recordingId)
|
|
83
|
+
}
|
|
84
|
+
catch (cause) {
|
|
85
|
+
throw new ViteHubError("TRANSCRIPTION_FAILED", "Transcription failed.", {
|
|
86
|
+
cause,
|
|
87
|
+
details: { recordingId },
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`code` and `message` remain available through `error.toJSON()`. Keep `details` JSON-safe and free of secrets; `toJSON()` omits `cause`, which remains available only on the in-memory error. ViteHub's built-in codes are typed as `WorkflowErrorCode` and use code-derived messages and code-specific details. Workflow Step retry behavior belongs in the Step's retry options rather than the error.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// vite.config.ts
|
|
97
|
+
import { hubWorkflow } from "@vite-hub/workflow/vite"
|
|
98
|
+
import { defineConfig } from "vite"
|
|
99
|
+
|
|
100
|
+
export default defineConfig({
|
|
101
|
+
plugins: [hubWorkflow()],
|
|
102
|
+
workflow: { provider: "openworkflow" },
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Vite Integration
|
|
107
|
+
|
|
108
|
+
Use `hubWorkflow()` in Vite to discover `server/workflows/<name>.ts`, folder workflows such as `server/workflows/welcome/index.ts` with numbered step files, and `src/<name>.workflow.ts`.
|
|
109
|
+
|
|
110
|
+
Providers map to [OpenWorkflow](https://openworkflow.dev/docs/overview), [Cloudflare Workflows](https://developers.cloudflare.com/workflows/), or [Vercel Workflow](https://vercel.com/docs/workflow).
|
|
111
|
+
|
|
112
|
+
Learn more at [vitehub.dev](https://vitehub.dev).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region ../internal/src/runtime/app.d.ts
|
|
2
|
+
type AppHandler = (request: Request, context?: Record<string, unknown>) => Response | Promise<Response>;
|
|
3
|
+
type VitehubApp = AppHandler | {
|
|
4
|
+
fetch?: AppHandler;
|
|
5
|
+
request?: (request: Request, options?: RequestInit, context?: Record<string, unknown>) => Response | Promise<Response>;
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/runtime/_app.d.ts
|
|
9
|
+
type WorkflowApp = VitehubApp;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { WorkflowApp as t };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region ../internal/src/runtime/app.ts
|
|
2
|
+
function resolveAppFetch(label, app) {
|
|
3
|
+
if (!app) return;
|
|
4
|
+
if (typeof app === "function") return app;
|
|
5
|
+
if (typeof app.request === "function") return (request, context) => app.request(request, void 0, context);
|
|
6
|
+
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
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { resolveAppFetch as t };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region ../internal/src/integrations/hex.ts
|
|
2
|
+
const textEncoder = new TextEncoder();
|
|
3
|
+
new TextDecoder();
|
|
4
|
+
function encodeNameHex(name) {
|
|
5
|
+
return [...textEncoder.encode(name)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
6
|
+
}
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/integrations/cloudflare.ts
|
|
9
|
+
const cloudflareWorkflowNameMaxLength = 64;
|
|
10
|
+
function shortHash(value) {
|
|
11
|
+
let hash = 2166136261;
|
|
12
|
+
for (const char of value) {
|
|
13
|
+
hash ^= char.codePointAt(0) || 0;
|
|
14
|
+
hash = Math.imul(hash, 16777619);
|
|
15
|
+
}
|
|
16
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
17
|
+
}
|
|
18
|
+
function getCloudflareWorkflowBindingName(name) {
|
|
19
|
+
return `WORKFLOW_${encodeNameHex(name).toUpperCase()}`;
|
|
20
|
+
}
|
|
21
|
+
function getCloudflareWorkflowName(name) {
|
|
22
|
+
const encoded = encodeNameHex(name);
|
|
23
|
+
const fullName = `workflow--${encoded}`;
|
|
24
|
+
if (fullName.length <= cloudflareWorkflowNameMaxLength) return fullName;
|
|
25
|
+
const hash = shortHash(name);
|
|
26
|
+
const prefixLength = cloudflareWorkflowNameMaxLength - 10 - 1 - hash.length;
|
|
27
|
+
return `workflow--${encoded.slice(0, prefixLength)}-${hash}`;
|
|
28
|
+
}
|
|
29
|
+
function getCloudflareWorkflowClassName(name) {
|
|
30
|
+
return `ViteHub${name.split(/[^a-z0-9]+/i).filter(Boolean).map((segment) => `${segment.charAt(0).toUpperCase()}${segment.slice(1)}`).join("") || "Default"}${shortHash(name)}Workflow`;
|
|
31
|
+
}
|
|
32
|
+
function createCloudflareWorkflowBindings(definitions, options) {
|
|
33
|
+
if (!definitions.length) return;
|
|
34
|
+
if (definitions.length > 1 && options && (options.binding || options.name)) throw new TypeError("Cloudflare workflow binding and name overrides are only supported when one workflow is discovered.");
|
|
35
|
+
return definitions.map((definition) => ({
|
|
36
|
+
binding: definitions.length === 1 && options ? options.binding || getCloudflareWorkflowBindingName(definition.name) : getCloudflareWorkflowBindingName(definition.name),
|
|
37
|
+
class_name: getCloudflareWorkflowClassName(definition.name),
|
|
38
|
+
name: definitions.length === 1 && options ? options.name || getCloudflareWorkflowName(definition.name) : getCloudflareWorkflowName(definition.name)
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { encodeNameHex as a, getCloudflareWorkflowName as i, getCloudflareWorkflowBindingName as n, getCloudflareWorkflowClassName as r, createCloudflareWorkflowBindings as t };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region ../internal/src/runtime/cloudflare-env.d.ts
|
|
2
|
+
type CloudflareWorkerEnv = Record<string, unknown>;
|
|
3
|
+
interface CloudflareWorkerExecutionContext {
|
|
4
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
5
|
+
}
|
|
6
|
+
declare function setActiveCloudflareEnv(env: CloudflareWorkerEnv | undefined): void;
|
|
7
|
+
declare function runWithActiveCloudflareEnv<T>(env: CloudflareWorkerEnv | undefined, callback: () => T): T;
|
|
8
|
+
type WaitUntilFn = (promise: Promise<unknown>) => void;
|
|
9
|
+
interface CloudflareRuntimeEvent {
|
|
10
|
+
context: {
|
|
11
|
+
cloudflare: {
|
|
12
|
+
context: CloudflareWorkerExecutionContext | undefined;
|
|
13
|
+
env: CloudflareWorkerEnv;
|
|
14
|
+
};
|
|
15
|
+
waitUntil?: WaitUntilFn;
|
|
16
|
+
};
|
|
17
|
+
env: CloudflareWorkerEnv;
|
|
18
|
+
waitUntil?: WaitUntilFn;
|
|
19
|
+
}
|
|
20
|
+
declare function createCloudflareRuntimeEvent(env: CloudflareWorkerEnv, context: CloudflareWorkerExecutionContext | undefined): CloudflareRuntimeEvent;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { setActiveCloudflareEnv as a, runWithActiveCloudflareEnv as i, CloudflareWorkerExecutionContext as n, createCloudflareRuntimeEvent as r, CloudflareWorkerEnv as t };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
//#region ../internal/src/runtime/cloudflare-env.ts
|
|
3
|
+
let activeEnv;
|
|
4
|
+
const activeEnvStorage = new AsyncLocalStorage();
|
|
5
|
+
function setActiveCloudflareEnv(env) {
|
|
6
|
+
activeEnv = env;
|
|
7
|
+
globalThis.__env__ = env;
|
|
8
|
+
try {
|
|
9
|
+
activeEnvStorage.enterWith({ env });
|
|
10
|
+
} catch {}
|
|
11
|
+
}
|
|
12
|
+
function runWithActiveCloudflareEnv(env, callback) {
|
|
13
|
+
return activeEnvStorage.run({ env }, callback);
|
|
14
|
+
}
|
|
15
|
+
function getActiveCloudflareEnv() {
|
|
16
|
+
const scoped = activeEnvStorage.getStore();
|
|
17
|
+
return scoped ? scoped.env : activeEnv ?? globalThis.__env__;
|
|
18
|
+
}
|
|
19
|
+
function getCloudflareEnv(event) {
|
|
20
|
+
const target = event;
|
|
21
|
+
return target?.env || target?.context?.cloudflare?.env || target?.context?._platform?.cloudflare?.env || target?.req?.runtime?.cloudflare?.env || getActiveCloudflareEnv();
|
|
22
|
+
}
|
|
23
|
+
function resolveWaitUntil(event) {
|
|
24
|
+
const target = event;
|
|
25
|
+
const owners = [
|
|
26
|
+
target,
|
|
27
|
+
target?.context,
|
|
28
|
+
target?.context?.cloudflare,
|
|
29
|
+
target?.context?.cloudflare?.context,
|
|
30
|
+
target?.context?._platform?.cloudflare,
|
|
31
|
+
target?.context?._platform?.cloudflare?.context,
|
|
32
|
+
target?.req,
|
|
33
|
+
target?.req?.runtime?.cloudflare,
|
|
34
|
+
target?.req?.runtime?.cloudflare?.context
|
|
35
|
+
];
|
|
36
|
+
for (const owner of owners) if (typeof owner?.waitUntil === "function") return owner.waitUntil.bind(owner);
|
|
37
|
+
}
|
|
38
|
+
function createCloudflareRuntimeEvent(env, context) {
|
|
39
|
+
const waitUntil = typeof context?.waitUntil === "function" ? context.waitUntil.bind(context) : void 0;
|
|
40
|
+
return {
|
|
41
|
+
context: {
|
|
42
|
+
cloudflare: {
|
|
43
|
+
env,
|
|
44
|
+
context
|
|
45
|
+
},
|
|
46
|
+
waitUntil
|
|
47
|
+
},
|
|
48
|
+
env,
|
|
49
|
+
waitUntil
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { setActiveCloudflareEnv as a, runWithActiveCloudflareEnv as i, getCloudflareEnv as n, resolveWaitUntil as r, createCloudflareRuntimeEvent as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { t as normalizeWorkflowOptions } from "./config-BDqB6I4E.js";
|
|
2
|
+
import { i as runWithActiveCloudflareEnv, t as createCloudflareRuntimeEvent } from "./cloudflare-env-mFp2Uj6Z.js";
|
|
3
|
+
import { runWithWorkflowRuntimeEvent, setWorkflowRuntimeConfig, setWorkflowRuntimeRegistry } from "./runtime/state.js";
|
|
4
|
+
import { t as resolveAppFetch } from "./app-BZpq2cwC.js";
|
|
5
|
+
import { H3, toWebHandler } from "h3/cloudflare";
|
|
6
|
+
//#region ../internal/src/runtime/cloudflare-hosted.ts
|
|
7
|
+
function createWebRequestHandler(appHandler) {
|
|
8
|
+
const defaultHandler = toWebHandler(new H3());
|
|
9
|
+
return async (request, context) => await Promise.resolve(appHandler ? appHandler(request, context) : defaultHandler(request, context));
|
|
10
|
+
}
|
|
11
|
+
function createCloudflareHostedWorker(adapter) {
|
|
12
|
+
const requestHandler = createWebRequestHandler(resolveAppFetch(adapter.label, adapter.app));
|
|
13
|
+
return { async fetch(request, env, context) {
|
|
14
|
+
const handle = async (handlerContext) => await Promise.resolve(requestHandler(request, handlerContext));
|
|
15
|
+
if (adapter.onRequest) return await adapter.onRequest({
|
|
16
|
+
env,
|
|
17
|
+
executionContext: context,
|
|
18
|
+
handle
|
|
19
|
+
});
|
|
20
|
+
return await handle();
|
|
21
|
+
} };
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/runtime/cloudflare-vite.ts
|
|
25
|
+
function createWorkflowCloudflareWorker(options = {}) {
|
|
26
|
+
const workflowConfig = options.workflow === false ? false : normalizeWorkflowOptions(options.workflow, { hosting: "cloudflare" });
|
|
27
|
+
return createCloudflareHostedWorker({
|
|
28
|
+
app: options.app,
|
|
29
|
+
label: "workflow",
|
|
30
|
+
async onRequest({ env, executionContext, handle }) {
|
|
31
|
+
setWorkflowRuntimeConfig(workflowConfig);
|
|
32
|
+
setWorkflowRuntimeRegistry(options.registry);
|
|
33
|
+
const runtimeEvent = createCloudflareRuntimeEvent(env, executionContext);
|
|
34
|
+
return await runWithActiveCloudflareEnv(env, () => runWithWorkflowRuntimeEvent(runtimeEvent, () => handle(runtimeEvent.context)));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { createWorkflowCloudflareWorker as t };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { defu } from "defu";
|
|
2
|
+
//#region ../internal/src/hosting.ts
|
|
3
|
+
function normalizeHosting(hosting) {
|
|
4
|
+
const normalized = hosting?.trim().toLowerCase().replaceAll("_", "-") || "";
|
|
5
|
+
if (normalized === "cloudflare") return "cloudflare-module";
|
|
6
|
+
return normalized;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region ../internal/src/object.ts
|
|
10
|
+
function isPlainObject(value) {
|
|
11
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/config.ts
|
|
15
|
+
const knownProviders = new Set([
|
|
16
|
+
"cloudflare",
|
|
17
|
+
"openworkflow",
|
|
18
|
+
"vercel"
|
|
19
|
+
]);
|
|
20
|
+
function readString(value, label) {
|
|
21
|
+
if (typeof value === "undefined") return;
|
|
22
|
+
if (typeof value !== "string" || !value.trim()) throw new TypeError(`\`${label}\` must be a non-empty string when provided.`);
|
|
23
|
+
return value.trim();
|
|
24
|
+
}
|
|
25
|
+
function readRuntimeConfigValue(value, label) {
|
|
26
|
+
if (typeof value === "undefined") return;
|
|
27
|
+
if (typeof value === "string") return readString(value, label);
|
|
28
|
+
if (!isRuntimeEnvDeclaration(value)) throw new TypeError(`\`${label}\` must be a string or runtime env declaration.`);
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function readBoolean(value, label) {
|
|
32
|
+
if (typeof value === "undefined") return;
|
|
33
|
+
if (typeof value !== "boolean") throw new TypeError(`\`${label}\` must be a boolean when provided.`);
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
function isRuntimeEnvDeclaration(value) {
|
|
37
|
+
return isPlainObject(value) && value.kind === "env-variable" && (!("source" in value) || isRuntimeEnvSource(value.source));
|
|
38
|
+
}
|
|
39
|
+
function isRuntimeEnvSource(value) {
|
|
40
|
+
return isPlainObject(value) && value.kind === "env" && typeof value.name === "string" && (!("names" in value) || Array.isArray(value.names) && value.names.every((name) => typeof name === "string"));
|
|
41
|
+
}
|
|
42
|
+
function readDatabaseName(value) {
|
|
43
|
+
return readString(value, "workflow.database");
|
|
44
|
+
}
|
|
45
|
+
function readPositiveInteger(value, label) {
|
|
46
|
+
if (typeof value === "undefined") return;
|
|
47
|
+
if (!Number.isInteger(value) || value <= 0) throw new TypeError(`\`${label}\` must be a positive integer when provided.`);
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
function normalizeOpenWorkflowPostgresOptions(value) {
|
|
51
|
+
if (typeof value === "undefined") return;
|
|
52
|
+
if (!isPlainObject(value)) throw new TypeError("`workflow.postgres` must be a plain object.");
|
|
53
|
+
const namespaceId = readString(value.namespaceId, "workflow.postgres.namespaceId");
|
|
54
|
+
const runMigrations = readBoolean(value.runMigrations, "workflow.postgres.runMigrations");
|
|
55
|
+
const schema = readString(value.schema, "workflow.postgres.schema");
|
|
56
|
+
const url = readRuntimeConfigValue(value.url, "workflow.postgres.url");
|
|
57
|
+
return {
|
|
58
|
+
...namespaceId ? { namespaceId } : {},
|
|
59
|
+
...typeof runMigrations === "boolean" ? { runMigrations } : {},
|
|
60
|
+
...schema ? { schema } : {},
|
|
61
|
+
...url ? { url } : {}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function normalizeOpenWorkflowSqliteOptions(value) {
|
|
65
|
+
if (typeof value === "undefined") return;
|
|
66
|
+
if (!isPlainObject(value)) throw new TypeError("`workflow.sqlite` must be a plain object.");
|
|
67
|
+
const namespaceId = readString(value.namespaceId, "workflow.sqlite.namespaceId");
|
|
68
|
+
const path = readRuntimeConfigValue(value.path, "workflow.sqlite.path");
|
|
69
|
+
const runMigrations = readBoolean(value.runMigrations, "workflow.sqlite.runMigrations");
|
|
70
|
+
return {
|
|
71
|
+
...namespaceId ? { namespaceId } : {},
|
|
72
|
+
...path ? { path } : {},
|
|
73
|
+
...typeof runMigrations === "boolean" ? { runMigrations } : {}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function normalizeOpenWorkflowWorkerOptions(value) {
|
|
77
|
+
if (typeof value === "undefined") return;
|
|
78
|
+
if (!isPlainObject(value)) throw new TypeError("`workflow.worker` must be a plain object.");
|
|
79
|
+
const concurrency = readPositiveInteger(value.concurrency, "workflow.worker.concurrency");
|
|
80
|
+
return typeof concurrency === "number" ? { concurrency } : {};
|
|
81
|
+
}
|
|
82
|
+
function hasOpenWorkflowStorageConfig(options) {
|
|
83
|
+
return typeof options.database === "string" && !!options.database.trim() || isPlainObject(options.postgres) && isDefinedRuntimeConfigValue(options.postgres.url) || isPlainObject(options.sqlite) && isDefinedRuntimeConfigValue(options.sqlite.path);
|
|
84
|
+
}
|
|
85
|
+
function isDefinedRuntimeConfigValue(value) {
|
|
86
|
+
return typeof value === "string" && !!value.trim() || isRuntimeEnvDeclaration(value);
|
|
87
|
+
}
|
|
88
|
+
function inferProvider(provider, hosting) {
|
|
89
|
+
if (provider === "cloudflare" || provider === "openworkflow" || provider === "vercel") return provider;
|
|
90
|
+
if (hosting.includes("netlify")) throw new TypeError("`workflow.provider` cannot be inferred for Netlify hosting. Set `workflow.provider` explicitly or disable `workflow`.");
|
|
91
|
+
if (hosting.includes("cloudflare")) return "cloudflare";
|
|
92
|
+
return "vercel";
|
|
93
|
+
}
|
|
94
|
+
function inferProviderFromOptions(options, hosting) {
|
|
95
|
+
if (typeof options.provider === "undefined" && (hosting.includes("node") || hosting.includes("docker")) && hasOpenWorkflowStorageConfig(options)) return "openworkflow";
|
|
96
|
+
return inferProvider(options.provider, hosting);
|
|
97
|
+
}
|
|
98
|
+
function resolveProvider(options, hosting) {
|
|
99
|
+
const shared = {
|
|
100
|
+
...typeof options.binding === "string" ? { binding: options.binding } : {},
|
|
101
|
+
...typeof options.name === "string" ? { name: options.name } : {}
|
|
102
|
+
};
|
|
103
|
+
const provider = options.provider;
|
|
104
|
+
if (typeof provider === "string" && !knownProviders.has(provider)) throw new TypeError(`Unknown \`workflow.provider\`: ${JSON.stringify(provider)}. Expected "cloudflare", "openworkflow", or "vercel".`);
|
|
105
|
+
const resolved = inferProviderFromOptions(options, hosting);
|
|
106
|
+
if (resolved === "cloudflare") return defu(shared, { provider: "cloudflare" });
|
|
107
|
+
if (resolved === "openworkflow") {
|
|
108
|
+
const database = readDatabaseName(options.database);
|
|
109
|
+
const postgres = normalizeOpenWorkflowPostgresOptions(options.postgres);
|
|
110
|
+
const sqlite = normalizeOpenWorkflowSqliteOptions(options.sqlite);
|
|
111
|
+
const worker = normalizeOpenWorkflowWorkerOptions(options.worker);
|
|
112
|
+
if (database && postgres?.url) throw new TypeError("`workflow.database` and `workflow.postgres.url` cannot both configure OpenWorkflow storage.");
|
|
113
|
+
if (database && sqlite?.path) throw new TypeError("`workflow.database` and `workflow.sqlite.path` cannot both configure OpenWorkflow storage.");
|
|
114
|
+
if (postgres?.url && sqlite?.path) throw new TypeError("`workflow.postgres.url` and `workflow.sqlite.path` cannot both configure OpenWorkflow storage.");
|
|
115
|
+
return defu({
|
|
116
|
+
...database ? { database } : {},
|
|
117
|
+
...postgres ? { postgres } : {},
|
|
118
|
+
...sqlite ? { sqlite } : {},
|
|
119
|
+
...worker ? { worker } : {}
|
|
120
|
+
}, shared, { provider: "openworkflow" });
|
|
121
|
+
}
|
|
122
|
+
return defu(shared, { provider: "vercel" });
|
|
123
|
+
}
|
|
124
|
+
function normalizeWorkflowOptions(options, input = {}) {
|
|
125
|
+
if (options === false) return void 0;
|
|
126
|
+
if (typeof options !== "undefined" && !isPlainObject(options)) throw new TypeError("`workflow` must be a plain object.");
|
|
127
|
+
return resolveProvider(options || {}, normalizeHosting(input.hosting));
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { normalizeWorkflowOptions as t };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { A as WorkflowStepFunction, C as WorkflowProviderStep, D as WorkflowSharedOptions, E as WorkflowRunStep, M as WorkflowStepRunner, O as WorkflowSignalResult, S as WorkflowProviderOptions, T as WorkflowRunStatus, _ as WorkflowHandle, a as InferredWorkflowProviderOptions, b as WorkflowModuleProviderOptions, c as OpenWorkflowWorkerOptions, d as WorkflowCreateOptions, f as WorkflowDeferOptions, g as WorkflowExecutionContext, h as WorkflowDefinitionRegistry, i as DiscoveredWorkflowDefinition, j as WorkflowStepOptions, k as WorkflowStartOptions, l as ResolvedWorkflowOptions, m as WorkflowDefinitionOptions, n as CloudflareWorkflowInstance, o as OpenWorkflowPostgresOptions, p as WorkflowDefinition, r as CloudflareWorkflowProviderOptions, s as OpenWorkflowProviderOptions, t as CloudflareWorkflowBinding, u as VercelWorkflowProviderOptions, v as WorkflowHandler, w as WorkflowRun, x as WorkflowProvider, y as WorkflowModuleOptions } from "./types-BUlYBZwH.js";
|
|
2
|
+
import { createWorkflowCloudflareWorker } from "./runtime/cloudflare-vite.js";
|
|
3
|
+
import { ViteHubError } from "@vite-hub/runtime";
|
|
4
|
+
|
|
5
|
+
//#region src/config.d.ts
|
|
6
|
+
interface WorkflowResolutionInput {
|
|
7
|
+
hosting?: string;
|
|
8
|
+
}
|
|
9
|
+
declare function normalizeWorkflowOptions(options: WorkflowModuleOptions | undefined, input?: WorkflowResolutionInput): ResolvedWorkflowOptions | undefined;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/definition.d.ts
|
|
12
|
+
declare function defineWorkflow<TPayload = unknown, TResult = unknown>(handler: WorkflowHandler<TPayload, TResult>, options?: WorkflowDefinitionOptions<TPayload, TResult>): WorkflowDefinition<TPayload, TResult>;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/errors.d.ts
|
|
15
|
+
declare const workflowErrorMessages: {
|
|
16
|
+
readonly OPENWORKFLOW_BACKEND_CLOSE_FAILED: "OpenWorkflow backend cleanup failed.";
|
|
17
|
+
readonly OPENWORKFLOW_RUNTIME_RESET: "OpenWorkflow runtime was reset while it was being acquired.";
|
|
18
|
+
readonly OPENWORKFLOW_WORKER_STOP_FAILED: "OpenWorkflow worker stop failed.";
|
|
19
|
+
readonly VERCEL_WORKFLOW_SDK_LOAD_FAILED: "Vercel Workflow DevKit load failed. Install the optional workflow peer dependency.";
|
|
20
|
+
readonly WORKFLOW_DEFINITION_NOT_FOUND: "Workflow definition was not found.";
|
|
21
|
+
readonly WORKFLOW_DISABLED: "Workflow is disabled.";
|
|
22
|
+
readonly WORKFLOW_NATIVE_ENTRY_INVALID: "Workflow has no transformed native Vercel entry.";
|
|
23
|
+
readonly WORKFLOW_NATIVE_ENTRY_REQUIRED: "Workflow has no native durable entry for Vercel.";
|
|
24
|
+
readonly WORKFLOW_OPERATION_UNSUPPORTED: "Workflow provider operation is unsupported.";
|
|
25
|
+
readonly WORKFLOW_PROVIDER_OPERATION_FAILED: "Workflow provider operation failed.";
|
|
26
|
+
readonly WORKFLOW_RUN_ID_UNSUPPORTED: "Native Vercel workflows assign their own run IDs.";
|
|
27
|
+
};
|
|
28
|
+
declare const workflowProviderNames: readonly ["cloudflare", "openworkflow", "vercel"];
|
|
29
|
+
declare const workflowOperationNames: readonly ["cancel", "cancellation", "connect", "create", "get", "get-run", "import", "list-steps", "resume-signal", "run", "signals", "start", "status"];
|
|
30
|
+
type WorkflowErrorCode = keyof typeof workflowErrorMessages;
|
|
31
|
+
type WorkflowOperationName = typeof workflowOperationNames[number];
|
|
32
|
+
type WorkflowProviderName = typeof workflowProviderNames[number];
|
|
33
|
+
type WorkflowNameDetails = {
|
|
34
|
+
name?: string;
|
|
35
|
+
};
|
|
36
|
+
type VercelWorkflowDetails = {
|
|
37
|
+
name?: string;
|
|
38
|
+
provider: "vercel";
|
|
39
|
+
};
|
|
40
|
+
type WorkflowOperationDetails = {
|
|
41
|
+
operation: WorkflowOperationName;
|
|
42
|
+
provider: WorkflowProviderName;
|
|
43
|
+
};
|
|
44
|
+
type WorkflowProviderOperationDetails = WorkflowOperationDetails & {
|
|
45
|
+
status?: number;
|
|
46
|
+
};
|
|
47
|
+
type WorkflowErrorDetails<TCode extends WorkflowErrorCode = WorkflowErrorCode> = TCode extends "OPENWORKFLOW_BACKEND_CLOSE_FAILED" | "OPENWORKFLOW_RUNTIME_RESET" | "OPENWORKFLOW_WORKER_STOP_FAILED" ? {
|
|
48
|
+
provider: "openworkflow";
|
|
49
|
+
} : TCode extends "VERCEL_WORKFLOW_SDK_LOAD_FAILED" ? {
|
|
50
|
+
provider: "vercel";
|
|
51
|
+
} : TCode extends "WORKFLOW_DEFINITION_NOT_FOUND" ? WorkflowNameDetails : TCode extends "WORKFLOW_DISABLED" ? Record<string, never> : TCode extends "WORKFLOW_NATIVE_ENTRY_INVALID" | "WORKFLOW_NATIVE_ENTRY_REQUIRED" | "WORKFLOW_RUN_ID_UNSUPPORTED" ? VercelWorkflowDetails : TCode extends "WORKFLOW_OPERATION_UNSUPPORTED" ? WorkflowOperationDetails : TCode extends "WORKFLOW_PROVIDER_OPERATION_FAILED" ? WorkflowProviderOperationDetails : never;
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/integrations/cloudflare.d.ts
|
|
54
|
+
declare function getCloudflareWorkflowBindingName(name: string): string;
|
|
55
|
+
declare function getCloudflareWorkflowName(name: string): string;
|
|
56
|
+
declare function getCloudflareWorkflowClassName(name: string): string;
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/integrations/vercel.d.ts
|
|
59
|
+
declare function getVercelWorkflowName(name: string): string;
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/runtime/client.d.ts
|
|
62
|
+
declare function createWorkflow<TPayload = unknown, TResult = unknown>(options: WorkflowCreateOptions<TPayload, TResult> & {
|
|
63
|
+
handler: WorkflowHandler<TPayload, TResult>;
|
|
64
|
+
name: string;
|
|
65
|
+
}): WorkflowHandle<TPayload, TResult>;
|
|
66
|
+
declare function createWorkflow<TPayload = unknown, TResult = unknown>(name: string, options?: WorkflowCreateOptions<TPayload, TResult>): WorkflowHandle<TPayload, TResult>;
|
|
67
|
+
declare function createWorkflow<TPayload = unknown, TResult = unknown>(name: string, handler: WorkflowHandler<TPayload, TResult>, options?: WorkflowCreateOptions<TPayload, TResult>): WorkflowHandle<TPayload, TResult>;
|
|
68
|
+
declare function runWorkflow<TPayload = unknown, TResult = unknown>(name: string, payload?: TPayload, options?: WorkflowDeferOptions): Promise<WorkflowRun<TPayload, TResult>>;
|
|
69
|
+
declare function getWorkflowRun<TPayload = unknown, TResult = unknown>(name: string, id: string): Promise<WorkflowRun<TPayload, TResult>>;
|
|
70
|
+
declare function cancelWorkflow<TPayload = unknown, TResult = unknown>(name: string, id: string): Promise<WorkflowRun<TPayload, TResult>>;
|
|
71
|
+
declare function resumeWorkflowSignal<TPayload = unknown>(token: string, payload: TPayload): Promise<WorkflowSignalResult>;
|
|
72
|
+
declare function deferWorkflow<TPayload = unknown>(name: string, payload?: TPayload, options?: WorkflowStartOptions): Promise<WorkflowRun<TPayload>>;
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/runtime/payload.d.ts
|
|
75
|
+
interface PayloadSchema<T> {
|
|
76
|
+
safeParse: (payload: unknown) => {
|
|
77
|
+
success: boolean;
|
|
78
|
+
data?: T;
|
|
79
|
+
error?: unknown;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
interface ParsePayloadSchema<T> {
|
|
83
|
+
parse: (payload: unknown) => T;
|
|
84
|
+
}
|
|
85
|
+
type PayloadValidator<T> = PayloadSchema<T> | ParsePayloadSchema<T> | ((payload: unknown) => T | Promise<T>);
|
|
86
|
+
declare function readRequestPayload<T = unknown>(request: Request): Promise<T>;
|
|
87
|
+
declare function validatePayload<T>(payload: unknown, schema: PayloadValidator<T>): Promise<T>;
|
|
88
|
+
declare function readValidatedPayload<T>(request: Request, schema: PayloadValidator<T>): Promise<T>;
|
|
89
|
+
//#endregion
|
|
90
|
+
export { type CloudflareWorkflowBinding, type CloudflareWorkflowInstance, type CloudflareWorkflowProviderOptions, type DiscoveredWorkflowDefinition, type InferredWorkflowProviderOptions, type OpenWorkflowPostgresOptions, type OpenWorkflowProviderOptions, type OpenWorkflowWorkerOptions, type ResolvedWorkflowOptions, type VercelWorkflowProviderOptions, type WorkflowCreateOptions, type WorkflowDeferOptions, type WorkflowDefinition, type WorkflowDefinitionOptions, type WorkflowDefinitionRegistry, type WorkflowErrorCode, type WorkflowErrorDetails, type WorkflowExecutionContext, type WorkflowHandle, type WorkflowHandler, type WorkflowModuleOptions, type WorkflowModuleProviderOptions, type WorkflowOperationName, type WorkflowProvider, type WorkflowProviderName, type WorkflowProviderOptions, type WorkflowProviderStep, type WorkflowRun, type WorkflowRunStatus, type WorkflowRunStep, type WorkflowSharedOptions, type WorkflowSignalResult, type WorkflowStartOptions, type WorkflowStepFunction, type WorkflowStepOptions, type WorkflowStepRunner, cancelWorkflow, createWorkflow, createWorkflowCloudflareWorker, deferWorkflow, defineWorkflow, getCloudflareWorkflowBindingName, getCloudflareWorkflowClassName, getCloudflareWorkflowName, getVercelWorkflowName, getWorkflowRun, normalizeWorkflowOptions, readRequestPayload, readValidatedPayload, resumeWorkflowSignal, runWorkflow, validatePayload };
|