@lunora/workflow 1.0.0-alpha.2 → 1.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +6 -0
- package/dist/do/index.d.mts +16 -16
- package/dist/do/index.d.ts +16 -16
- package/dist/do/index.mjs +1 -32
- package/dist/index.d.mts +127 -132
- package/dist/index.d.ts +127 -132
- package/dist/index.mjs +1 -8
- package/dist/packem_shared/MAX_BRANCHES-L0FzeGTH.mjs +1 -0
- package/dist/packem_shared/NonRetryableError-CXD-mZe4.mjs +1 -0
- package/dist/packem_shared/WorkflowsRestError--PIoe9uw.mjs +1 -0
- package/dist/packem_shared/branch-marker-B2lebQA6.mjs +1 -0
- package/dist/packem_shared/createRunStep-CzGKC1eZ.mjs +1 -0
- package/dist/packem_shared/createWorkflowContext-Dw8a1U68.mjs +1 -0
- package/dist/packem_shared/createWorkflowRunContext-BNh1APpj.mjs +1 -0
- package/dist/packem_shared/createWorkflows-3djec-oS.mjs +1 -0
- package/dist/packem_shared/defineStep-rJ0XPdAx.mjs +1 -0
- package/dist/packem_shared/defineWorkflow-fBuCDkGT.mjs +1 -0
- package/dist/packem_shared/{types.d-CQO_koGe.d.mts → types.d-CZ1pcdmw.d.mts} +162 -81
- package/dist/packem_shared/{types.d-CQO_koGe.d.ts → types.d-CZ1pcdmw.d.ts} +162 -81
- package/package.json +4 -3
- package/dist/packem_shared/NonRetryableError-Dn2dTyBS.mjs +0 -27
- package/dist/packem_shared/WorkflowsRestError-b06i7K5j.mjs +0 -118
- package/dist/packem_shared/createRunStep-8jOXxP2o.mjs +0 -54
- package/dist/packem_shared/createWorkflowContext-D6thzmlF.mjs +0 -14
- package/dist/packem_shared/createWorkflowLogger-DR8P4ZoY.mjs +0 -76
- package/dist/packem_shared/createWorkflows-BoSYVIXg.mjs +0 -23
- package/dist/packem_shared/defineStep-DJQtLw7g.mjs +0 -28
- package/dist/packem_shared/defineWorkflow-DbUC-oCN.mjs +0 -15
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { a as Workflows, L as LunoraWorkflowsOptions, S as StepArgsValidator, b as StepConfig, c as StepDefinition, d as WorkflowConfig, W as WorkflowDefinition, e as
|
|
2
|
-
export type { A as ArgsOf, F as FunctionReference, I as InferStepArgs, R as RunFunctionOptions,
|
|
1
|
+
import { a as Workflows, L as LunoraWorkflowsOptions, S as StepArgsValidator, b as StepConfig, c as StepDefinition, d as WorkflowConfig, W as WorkflowDefinition, e as WorkflowBranch, f as WorkflowInstanceStatus, g as WorkflowEventLike, h as WorkflowStepLike, i as WorkflowRunContext, j as WorkflowLogger, k as WorkflowRunFunction, l as WorkflowRunStepFunction } from "./packem_shared/types.d-CZ1pcdmw.js";
|
|
2
|
+
export type { A as ArgsOf, B as BranchCompensationParams, F as FunctionReference, I as InferStepArgs, R as RunFunctionOptions, m as RunStepOptions, n as StepHandler, o as StepRollbackContext, p as StepRollbackHandler, q as StepRunContext, r as WorkflowBindingLike, s as WorkflowBranchOutputs, t as WorkflowCreateOptions, u as WorkflowHandle, v as WorkflowHandler, w as WorkflowInstanceLike, x as WorkflowParallelFunction, y as WorkflowRollbackContextLike, z as WorkflowRollbackHandlerLike, C as WorkflowSpawnFunction, D as WorkflowSpawnOptions, E as WorkflowStatusResult, G as WorkflowStepConfigLike, H as WorkflowStepContextLike, J as WorkflowStepRollbackOptionsLike } from "./packem_shared/types.d-CZ1pcdmw.js";
|
|
3
|
+
import { LunoraError } from '@lunora/errors';
|
|
3
4
|
import '@lunora/values';
|
|
4
5
|
/** Wiring info for one declared workflow, emitted by codegen into the generated shard. */
|
|
5
6
|
interface WorkflowBindingSpec {
|
|
@@ -9,108 +10,108 @@ interface WorkflowBindingSpec {
|
|
|
9
10
|
exportName: string;
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
12
|
-
* Build the `ctx.workflows` handle for a request: resolve every spec's
|
|
13
|
-
* `env[binding]` into the `exportName → Workflow binding` map and wrap it in
|
|
14
|
-
* {@link createWorkflows}. A spec whose binding is absent from `env` is skipped
|
|
15
|
-
* here — the helpful "no workflow named …" error is raised lazily by
|
|
16
|
-
* `workflows.get(name)` when the missing workflow is actually used.
|
|
17
|
-
*/
|
|
13
|
+
* Build the `ctx.workflows` handle for a request: resolve every spec's
|
|
14
|
+
* `env[binding]` into the `exportName → Workflow binding` map and wrap it in
|
|
15
|
+
* {@link createWorkflows}. A spec whose binding is absent from `env` is skipped
|
|
16
|
+
* here — the helpful "no workflow named …" error is raised lazily by
|
|
17
|
+
* `workflows.get(name)` when the missing workflow is actually used.
|
|
18
|
+
*/
|
|
18
19
|
declare const createWorkflowContext: (env: Record<string, unknown>, specs: ReadonlyArray<WorkflowBindingSpec>) => Workflows;
|
|
19
20
|
/**
|
|
20
|
-
* Build the `ctx.workflows` handle from a map of `lunora/workflows.ts` export
|
|
21
|
-
* name → Cloudflare `Workflow` binding. `get(name)` resolves the typed handle;
|
|
22
|
-
* an unknown name throws with the list of declared workflows.
|
|
23
|
-
*/
|
|
21
|
+
* Build the `ctx.workflows` handle from a map of `lunora/workflows.ts` export
|
|
22
|
+
* name → Cloudflare `Workflow` binding. `get(name)` resolves the typed handle;
|
|
23
|
+
* an unknown name throws with the list of declared workflows.
|
|
24
|
+
*/
|
|
24
25
|
declare const createWorkflows: (options: LunoraWorkflowsOptions) => Workflows;
|
|
25
26
|
/**
|
|
26
|
-
* Declare a reusable durable step. Same `args` map shape a Lunora `query` /
|
|
27
|
-
* `mutation` / `action` uses, so a step reads like a function:
|
|
28
|
-
*
|
|
29
|
-
* ```ts
|
|
30
|
-
* // lunora/steps.ts
|
|
31
|
-
* import { defineStep } from "@lunora/workflow";
|
|
32
|
-
* import { v } from "@lunora/values";
|
|
33
|
-
*
|
|
34
|
-
* export const fetchImage = defineStep("fetch image", {
|
|
35
|
-
* args: { imageKey: v.string() },
|
|
36
|
-
* returns: v.object({ data: v.bytes() }),
|
|
37
|
-
* handler: async (ctx, { imageKey }) => {
|
|
38
|
-
* const object = await (ctx.env.BUCKET as R2Bucket).get(imageKey);
|
|
39
|
-
* return { data: new Uint8Array(await object!.arrayBuffer()) };
|
|
40
|
-
* },
|
|
41
|
-
* rollback: async (ctx) => {
|
|
42
|
-
* await (ctx.env.BUCKET as R2Bucket).delete(`tmp/${ctx.args.imageKey}`);
|
|
43
|
-
* },
|
|
44
|
-
* });
|
|
45
|
-
* ```
|
|
46
|
-
*
|
|
47
|
-
* Then, inside a `defineWorkflow` handler:
|
|
48
|
-
*
|
|
49
|
-
* ```ts
|
|
50
|
-
* const { data } = await ctx.runStep(fetchImage, { imageKey: ctx.params.imageKey });
|
|
51
|
-
* ```
|
|
52
|
-
*/
|
|
27
|
+
* Declare a reusable durable step. Same `args` map shape a Lunora `query` /
|
|
28
|
+
* `mutation` / `action` uses, so a step reads like a function:
|
|
29
|
+
*
|
|
30
|
+
* ```ts
|
|
31
|
+
* // lunora/steps.ts
|
|
32
|
+
* import { defineStep } from "@lunora/workflow";
|
|
33
|
+
* import { v } from "@lunora/values";
|
|
34
|
+
*
|
|
35
|
+
* export const fetchImage = defineStep("fetch image", {
|
|
36
|
+
* args: { imageKey: v.string() },
|
|
37
|
+
* returns: v.object({ data: v.bytes() }),
|
|
38
|
+
* handler: async (ctx, { imageKey }) => {
|
|
39
|
+
* const object = await (ctx.env.BUCKET as R2Bucket).get(imageKey);
|
|
40
|
+
* return { data: new Uint8Array(await object!.arrayBuffer()) };
|
|
41
|
+
* },
|
|
42
|
+
* rollback: async (ctx) => {
|
|
43
|
+
* await (ctx.env.BUCKET as R2Bucket).delete(`tmp/${ctx.args.imageKey}`);
|
|
44
|
+
* },
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* Then, inside a `defineWorkflow` handler:
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* const { data } = await ctx.runStep(fetchImage, { imageKey: ctx.params.imageKey });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
53
54
|
declare const defineStep: <A extends StepArgsValidator, Result>(name: string, config: StepConfig<A, Result>) => StepDefinition<A, Result>;
|
|
54
55
|
/** True when a value is a `defineStep` result (the runtime brand check). */
|
|
55
56
|
declare const isStepDefinition: (value: unknown) => value is StepDefinition;
|
|
56
57
|
/**
|
|
57
|
-
* The generated `WorkflowEntrypoint` class name for a `lunora/workflows.ts`
|
|
58
|
-
* export: `orderPipeline` → `OrderPipelineWorkflow`. wrangler's
|
|
59
|
-
* `workflows[].class_name` references it, so codegen and the config layer MUST
|
|
60
|
-
* derive it identically — always via this helper.
|
|
61
|
-
*/
|
|
58
|
+
* The generated `WorkflowEntrypoint` class name for a `lunora/workflows.ts`
|
|
59
|
+
* export: `orderPipeline` → `OrderPipelineWorkflow`. wrangler's
|
|
60
|
+
* `workflows[].class_name` references it, so codegen and the config layer MUST
|
|
61
|
+
* derive it identically — always via this helper.
|
|
62
|
+
*/
|
|
62
63
|
declare const workflowClassName: (exportName: string) => string;
|
|
63
64
|
/**
|
|
64
|
-
* The wrangler binding name for a workflow export: `orderPipeline` →
|
|
65
|
-
* `WORKFLOW_ORDER_PIPELINE`, `etl` → `WORKFLOW_ETL`. The `WORKFLOW_` prefix
|
|
66
|
-
* namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`/`CONTAINER_*` so a
|
|
67
|
-
* workflow export can never collide with the built-in bindings.
|
|
68
|
-
*/
|
|
65
|
+
* The wrangler binding name for a workflow export: `orderPipeline` →
|
|
66
|
+
* `WORKFLOW_ORDER_PIPELINE`, `etl` → `WORKFLOW_ETL`. The `WORKFLOW_` prefix
|
|
67
|
+
* namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`/`CONTAINER_*` so a
|
|
68
|
+
* workflow export can never collide with the built-in bindings.
|
|
69
|
+
*/
|
|
69
70
|
declare const workflowBindingName: (exportName: string) => string;
|
|
70
71
|
/**
|
|
71
|
-
* The stable workflow name wrangler registers (`workflows[].name`):
|
|
72
|
-
* `orderPipeline` → `order-pipeline`. Used as the deployed workflow's
|
|
73
|
-
* identifier when no explicit `name` override is given.
|
|
74
|
-
*/
|
|
72
|
+
* The stable workflow name wrangler registers (`workflows[].name`):
|
|
73
|
+
* `orderPipeline` → `order-pipeline`. Used as the deployed workflow's
|
|
74
|
+
* identifier when no explicit `name` override is given.
|
|
75
|
+
*/
|
|
75
76
|
declare const workflowDefaultName: (exportName: string) => string;
|
|
76
77
|
/**
|
|
77
|
-
* Declare a durable workflow deployed alongside the app. Pure validation +
|
|
78
|
-
* branding: codegen discovers the export, emits the `WorkflowEntrypoint`
|
|
79
|
-
* subclass (`_generated/workflows.ts`), and wires the typed `ctx.workflows`
|
|
80
|
-
* handle; the config layer reconciles the wrangler `workflows[]` entry from the
|
|
81
|
-
* same definition.
|
|
82
|
-
*
|
|
83
|
-
* ```ts
|
|
84
|
-
* // lunora/workflows.ts
|
|
85
|
-
* import { defineWorkflow } from "@lunora/workflow";
|
|
86
|
-
* import { api } from "./_generated/api";
|
|
87
|
-
*
|
|
88
|
-
* export const orderPipeline = defineWorkflow
|
|
89
|
-
* handler: async (ctx) => {
|
|
90
|
-
* const order = await ctx.step.do("load", () => ctx.run(api.orders.get, { id: ctx.params.orderId }));
|
|
91
|
-
* await ctx.step.sleep("cool-off", "1 minute");
|
|
92
|
-
* await ctx.step.do("charge", () => ctx.run(api.payments.charge, { orderId: ctx.params.orderId }));
|
|
93
|
-
* return order;
|
|
94
|
-
* },
|
|
95
|
-
* });
|
|
96
|
-
* ```
|
|
97
|
-
*/
|
|
78
|
+
* Declare a durable workflow deployed alongside the app. Pure validation +
|
|
79
|
+
* branding: codegen discovers the export, emits the `WorkflowEntrypoint`
|
|
80
|
+
* subclass (`_generated/workflows.ts`), and wires the typed `ctx.workflows`
|
|
81
|
+
* handle; the config layer reconciles the wrangler `workflows[]` entry from the
|
|
82
|
+
* same definition.
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* // lunora/workflows.ts
|
|
86
|
+
* import { defineWorkflow } from "@lunora/workflow";
|
|
87
|
+
* import { api } from "./_generated/api";
|
|
88
|
+
*
|
|
89
|
+
* export const orderPipeline = defineWorkflow<{ orderId: string }>({
|
|
90
|
+
* handler: async (ctx) => {
|
|
91
|
+
* const order = await ctx.step.do("load", () => ctx.run(api.orders.get, { id: ctx.params.orderId }));
|
|
92
|
+
* await ctx.step.sleep("cool-off", "1 minute");
|
|
93
|
+
* await ctx.step.do("charge", () => ctx.run(api.payments.charge, { orderId: ctx.params.orderId }));
|
|
94
|
+
* return order;
|
|
95
|
+
* },
|
|
96
|
+
* });
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
98
99
|
declare const defineWorkflow: <Params = Record<string, unknown>, Output = unknown>(config: WorkflowConfig<Params, Output>) => WorkflowDefinition<Params, Output>;
|
|
99
100
|
/** True when a value is a `defineWorkflow` result (the runtime brand check). */
|
|
100
101
|
declare const isWorkflowDefinition: (value: unknown) => value is WorkflowDefinition;
|
|
101
102
|
/**
|
|
102
|
-
* Throw from a workflow step (or handler) to fail the instance immediately
|
|
103
|
-
* **without** retrying — the portable mirror of `cloudflare:workflows`'
|
|
104
|
-
* `NonRetryableError`. Importable from Node, so workflow code stays unit-testable.
|
|
105
|
-
*
|
|
106
|
-
* ```ts
|
|
107
|
-
* import { NonRetryableError } from "@lunora/workflow";
|
|
108
|
-
*
|
|
109
|
-
* if (order.status === "cancelled") {
|
|
110
|
-
* throw new NonRetryableError("order already cancelled — no point retrying");
|
|
111
|
-
* }
|
|
112
|
-
* ```
|
|
113
|
-
*/
|
|
103
|
+
* Throw from a workflow step (or handler) to fail the instance immediately
|
|
104
|
+
* **without** retrying — the portable mirror of `cloudflare:workflows`'
|
|
105
|
+
* `NonRetryableError`. Importable from Node, so workflow code stays unit-testable.
|
|
106
|
+
*
|
|
107
|
+
* ```ts
|
|
108
|
+
* import { NonRetryableError } from "@lunora/workflow";
|
|
109
|
+
*
|
|
110
|
+
* if (order.status === "cancelled") {
|
|
111
|
+
* throw new NonRetryableError("order already cancelled — no point retrying");
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
114
115
|
declare class NonRetryableError extends Error {
|
|
115
116
|
constructor(message: string, name?: string);
|
|
116
117
|
}
|
|
@@ -119,18 +120,31 @@ declare const isNonRetryableError: (value: unknown) => value is NonRetryableErro
|
|
|
119
120
|
/** Constructor shape of `cloudflare:workflows`' native `NonRetryableError`. */
|
|
120
121
|
type NativeNonRetryableErrorConstructor = new (message: string, name?: string) => Error;
|
|
121
122
|
/**
|
|
122
|
-
* Rebuild a portable {@link NonRetryableError} as the native Cloudflare one,
|
|
123
|
-
* preserving its `name`, `message`, `cause`, and `stack`. Used at the `src/do`
|
|
124
|
-
* boundary where the native constructor is available; everywhere else the
|
|
125
|
-
* portable error is thrown unchanged (and still honored by name).
|
|
126
|
-
*/
|
|
123
|
+
* Rebuild a portable {@link NonRetryableError} as the native Cloudflare one,
|
|
124
|
+
* preserving its `name`, `message`, `cause`, and `stack`. Used at the `src/do`
|
|
125
|
+
* boundary where the native constructor is available; everywhere else the
|
|
126
|
+
* portable error is thrown unchanged (and still honored by name).
|
|
127
|
+
*/
|
|
127
128
|
declare const toNativeNonRetryableError: (error: NonRetryableError, NativeNonRetryableError: NativeNonRetryableErrorConstructor) => Error;
|
|
128
129
|
/**
|
|
129
|
-
* If `error` is a portable {@link NonRetryableError} and a native constructor is
|
|
130
|
-
* available, rethrow it as the native error; otherwise rethrow `error` as-is.
|
|
131
|
-
* Always throws — the `never` return lets callers `return convertNonRetryableError(...)`.
|
|
132
|
-
*/
|
|
130
|
+
* If `error` is a portable {@link NonRetryableError} and a native constructor is
|
|
131
|
+
* available, rethrow it as the native error; otherwise rethrow `error` as-is.
|
|
132
|
+
* Always throws — the `never` return lets callers `return convertNonRetryableError(...)`.
|
|
133
|
+
*/
|
|
133
134
|
declare const convertNonRetryableError: (error: unknown, NativeNonRetryableError: NativeNonRetryableErrorConstructor | undefined) => never;
|
|
135
|
+
/** Hard cap on branches per `ctx.parallel` call — auto-scale, never silently spawn unbounded DOs. */
|
|
136
|
+
declare const MAX_BRANCHES = 100;
|
|
137
|
+
/**
|
|
138
|
+
* Build a single fan-out branch: a declared child workflow referenced by its
|
|
139
|
+
* `lunora/workflows.ts` export name, plus the params it is created with. Pass the
|
|
140
|
+
* output type as the generic argument so `ctx.parallel(...)` infers the result
|
|
141
|
+
* tuple — e.g. `branch("imageTag", { key })` typed as `branch` of `{ tags }`.
|
|
142
|
+
*/
|
|
143
|
+
declare const branch: <Output = unknown>(workflow: string, params?: Record<string, unknown>, options?: {
|
|
144
|
+
compensateWith?: string;
|
|
145
|
+
id?: string;
|
|
146
|
+
timeout?: number | string;
|
|
147
|
+
}) => WorkflowBranch<Output>;
|
|
134
148
|
/** The lifecycle mutations the REST API exposes via `PATCH .../instances/{id}`. */
|
|
135
149
|
type WorkflowInstanceAction = "pause" | "resume" | "terminate";
|
|
136
150
|
/** Configuration for a {@link WorkflowsRestClient}. */
|
|
@@ -178,8 +192,7 @@ interface WorkflowInstancePage {
|
|
|
178
192
|
totalCount?: number;
|
|
179
193
|
}
|
|
180
194
|
/** Thrown when the REST API responds non-2xx or `success: false`; carries the status plus body for the caller to surface. */
|
|
181
|
-
declare class WorkflowsRestError extends
|
|
182
|
-
readonly status: number;
|
|
195
|
+
declare class WorkflowsRestError extends LunoraError {
|
|
183
196
|
constructor(status: number, body: string);
|
|
184
197
|
}
|
|
185
198
|
/** The observe client: list instances, read one instance's steps, and (with Edit scope) mutate its status. */
|
|
@@ -203,30 +216,12 @@ interface WorkflowsRestClient {
|
|
|
203
216
|
}>;
|
|
204
217
|
}
|
|
205
218
|
/**
|
|
206
|
-
* Build a {@link WorkflowsRestClient}. Each call hits the account-scoped REST
|
|
207
|
-
* endpoint with the bearer token, unwraps Cloudflare's
|
|
208
|
-
* `{ success, errors, result, result_info }` envelope, and normalizes the
|
|
209
|
-
* snake_case payload into the camelCase shapes the studio renders.
|
|
210
|
-
*/
|
|
219
|
+
* Build a {@link WorkflowsRestClient}. Each call hits the account-scoped REST
|
|
220
|
+
* endpoint with the bearer token, unwraps Cloudflare's
|
|
221
|
+
* `{ success, errors, result, result_info }` envelope, and normalizes the
|
|
222
|
+
* snake_case payload into the camelCase shapes the studio renders.
|
|
223
|
+
*/
|
|
211
224
|
declare const createWorkflowsRestClient: (config: WorkflowsRestConfig) => WorkflowsRestClient;
|
|
212
|
-
interface RunnerOptions {
|
|
213
|
-
/** Worker `env` — read `LUNORA_ORIGIN_URL` + `LUNORA_ADMIN_TOKEN` at call time. */
|
|
214
|
-
env: Record<string, unknown>;
|
|
215
|
-
/** Injectable fetch (tests); defaults to the global. */
|
|
216
|
-
fetchImpl?: typeof fetch;
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Build a {@link WorkflowRunFunction} that invokes a Lunora function by POSTing
|
|
220
|
-
* to the Worker's `/_lunora/scheduler/dispatch` endpoint — the same path the
|
|
221
|
-
* SchedulerDO and the Queues workpool dispatch through — authenticated with the
|
|
222
|
-
* admin bearer. The parsed JSON body (the function's return value) is resolved;
|
|
223
|
-
* an empty/non-JSON body resolves to `undefined`.
|
|
224
|
-
*
|
|
225
|
-
* Wrap calls in `ctx.step.do(...)` to make them durable + memoized + retried.
|
|
226
|
-
*/
|
|
227
|
-
declare const createWorkflowRunner: (options: RunnerOptions) => WorkflowRunFunction;
|
|
228
|
-
/** Console-backed logger, prefixed with the workflow name for log correlation. */
|
|
229
|
-
declare const createWorkflowLogger: (exportName: string) => WorkflowLogger;
|
|
230
225
|
interface RunContextOptions<Params> {
|
|
231
226
|
env: Record<string, unknown>;
|
|
232
227
|
event: WorkflowEventLike<Params>;
|
|
@@ -239,12 +234,12 @@ interface RunContextOptions<Params> {
|
|
|
239
234
|
/** Assemble the {@link WorkflowRunContext} passed to a `defineWorkflow` handler. */
|
|
240
235
|
declare const createWorkflowRunContext: <Params = Record<string, unknown>>(options: RunContextOptions<Params>) => WorkflowRunContext<Params>;
|
|
241
236
|
/**
|
|
242
|
-
* Validate a step's args through its validator map, prefixing any
|
|
243
|
-
* `ValidationError` with `step args
|
|
244
|
-
* offending field. Delegates to `@lunora/values`' shared {@link parseValidatorMap}
|
|
245
|
-
* — the same parser the procedure builder and HTTP routes use — so the
|
|
246
|
-
* optional-skip and error-prefix semantics stay in lockstep across the framework.
|
|
247
|
-
*/
|
|
237
|
+
* Validate a step's args through its validator map, prefixing any
|
|
238
|
+
* `ValidationError` with `step args.<key>` so the failure points at the
|
|
239
|
+
* offending field. Delegates to `@lunora/values`' shared {@link parseValidatorMap}
|
|
240
|
+
* — the same parser the procedure builder and HTTP routes use — so the
|
|
241
|
+
* optional-skip and error-prefix semantics stay in lockstep across the framework.
|
|
242
|
+
*/
|
|
248
243
|
declare const validateStepArgs: (validators: StepArgsValidator, source: Record<string, unknown>) => Record<string, unknown>;
|
|
249
244
|
/** Dependencies needed to run a step: the native step API plus the workflow's env / runner / logger. */
|
|
250
245
|
interface RunStepDeps {
|
|
@@ -260,10 +255,10 @@ interface RunStepDeps {
|
|
|
260
255
|
step: WorkflowStepLike;
|
|
261
256
|
}
|
|
262
257
|
/**
|
|
263
|
-
* Build the `ctx.runStep` function bound to one workflow invocation. Each call
|
|
264
|
-
* runs the step through `step.do(...)`: validate args → run body → validate
|
|
265
|
-
* result (when `returns` is declared), with any portable `NonRetryableError`
|
|
266
|
-
* converted to the native one and any declared rollback forwarded to Cloudflare.
|
|
267
|
-
*/
|
|
258
|
+
* Build the `ctx.runStep` function bound to one workflow invocation. Each call
|
|
259
|
+
* runs the step through `step.do(...)`: validate args → run body → validate
|
|
260
|
+
* result (when `returns` is declared), with any portable `NonRetryableError`
|
|
261
|
+
* converted to the native one and any declared rollback forwarded to Cloudflare.
|
|
262
|
+
*/
|
|
268
263
|
declare const createRunStep: (deps: RunStepDeps) => WorkflowRunStepFunction;
|
|
269
|
-
export { type LunoraWorkflowsOptions, type NativeNonRetryableErrorConstructor, NonRetryableError, type StepArgsValidator, type StepConfig, type StepDefinition, type WorkflowBindingSpec, type WorkflowConfig, type WorkflowDefinition, type WorkflowEventLike, type WorkflowInstanceAction, type WorkflowInstanceDetail, type WorkflowInstancePage, type WorkflowInstanceStatus, type WorkflowInstanceSummary, type WorkflowLogger, type WorkflowRunContext, type WorkflowRunFunction, type WorkflowRunStepFunction, type WorkflowStepDetail, type WorkflowStepLike, type Workflows, type WorkflowsRestClient, type WorkflowsRestConfig, WorkflowsRestError, convertNonRetryableError, createRunStep, createWorkflowContext,
|
|
264
|
+
export { type LunoraWorkflowsOptions, MAX_BRANCHES, type NativeNonRetryableErrorConstructor, NonRetryableError, type StepArgsValidator, type StepConfig, type StepDefinition, type WorkflowBindingSpec, type WorkflowBranch, type WorkflowConfig, type WorkflowDefinition, type WorkflowEventLike, type WorkflowInstanceAction, type WorkflowInstanceDetail, type WorkflowInstancePage, type WorkflowInstanceStatus, type WorkflowInstanceSummary, type WorkflowLogger, type WorkflowRunContext, type WorkflowRunFunction, type WorkflowRunStepFunction, type WorkflowStepDetail, type WorkflowStepLike, type Workflows, type WorkflowsRestClient, type WorkflowsRestConfig, WorkflowsRestError, branch, convertNonRetryableError, createRunStep, createWorkflowContext, createWorkflowRunContext, createWorkflows, createWorkflowsRestClient, defineStep, defineWorkflow, isNonRetryableError, isStepDefinition, isWorkflowDefinition, toNativeNonRetryableError, validateStepArgs, workflowBindingName, workflowClassName, workflowDefaultName };
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { default as createWorkflows } from './packem_shared/createWorkflows-BoSYVIXg.mjs';
|
|
3
|
-
export { defineStep, isStepDefinition } from './packem_shared/defineStep-DJQtLw7g.mjs';
|
|
4
|
-
export { defineWorkflow, isWorkflowDefinition, workflowBindingName, workflowClassName, workflowDefaultName } from './packem_shared/defineWorkflow-DbUC-oCN.mjs';
|
|
5
|
-
export { NonRetryableError, convertNonRetryableError, isNonRetryableError, toNativeNonRetryableError } from './packem_shared/NonRetryableError-Dn2dTyBS.mjs';
|
|
6
|
-
export { WorkflowsRestError, createWorkflowsRestClient } from './packem_shared/WorkflowsRestError-b06i7K5j.mjs';
|
|
7
|
-
export { createWorkflowLogger, createWorkflowRunContext, createWorkflowRunner } from './packem_shared/createWorkflowLogger-DR8P4ZoY.mjs';
|
|
8
|
-
export { createRunStep, validateStepArgs } from './packem_shared/createRunStep-8jOXxP2o.mjs';
|
|
1
|
+
import{createWorkflowContext as e}from"./packem_shared/createWorkflowContext-Dw8a1U68.mjs";import{default as f}from"./packem_shared/createWorkflows-3djec-oS.mjs";import{defineStep as l,isStepDefinition as n}from"./packem_shared/defineStep-rJ0XPdAx.mjs";import{defineWorkflow as p,isWorkflowDefinition as w,workflowBindingName as m,workflowClassName as s,workflowDefaultName as x}from"./packem_shared/defineWorkflow-fBuCDkGT.mjs";import{NonRetryableError as N,convertNonRetryableError as R,isNonRetryableError as c,toNativeNonRetryableError as W}from"./packem_shared/NonRetryableError-CXD-mZe4.mjs";import{MAX_BRANCHES as b,branch as d}from"./packem_shared/MAX_BRANCHES-L0FzeGTH.mjs";import{WorkflowsRestError as S,createWorkflowsRestClient as u}from"./packem_shared/WorkflowsRestError--PIoe9uw.mjs";import{createWorkflowRunContext as v}from"./packem_shared/createWorkflowRunContext-BNh1APpj.mjs";import{createRunStep as D,validateStepArgs as g}from"./packem_shared/createRunStep-CzGKC1eZ.mjs";export{b as MAX_BRANCHES,N as NonRetryableError,S as WorkflowsRestError,d as branch,R as convertNonRetryableError,D as createRunStep,e as createWorkflowContext,v as createWorkflowRunContext,f as createWorkflows,u as createWorkflowsRestClient,l as defineStep,p as defineWorkflow,c as isNonRetryableError,n as isStepDefinition,w as isWorkflowDefinition,W as toNativeNonRetryableError,g as validateStepArgs,m as workflowBindingName,s as workflowClassName,x as workflowDefaultName};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as g}from"@lunora/errors";import{a as p,h as y,B as $}from"./branch-marker-B2lebQA6.mjs";import{NonRetryableError as c}from"./NonRetryableError-CXD-mZe4.mjs";const h=100,f="lunora:spawn:",v="lunora:await:",x="lunora:signal:",B="lunora:compensate:",w="lunora:branch:",T=(e,t,a)=>({compensateWith:a?.compensateWith,id:a?.id,params:t,timeout:a?.timeout,workflow:e}),m=e=>e instanceof Error?{message:e.message,name:e.name}:{message:String(e),name:"Error"},R=e=>({status:"ok",value:e}),S=e=>({error:m(e),status:"error"}),u=async(e,t,a)=>{for(let i=t.length-1;i>=0;i-=1){const n=t[i],l=n?.plan.item.compensateWith;if(!(n===void 0||l===void 0))try{const r=e.resolveBinding(l);await e.step.do(`${B}${n.plan.childId}`,async()=>{const o=`${n.plan.childId}:compensate`,s={branch:n.plan.item.workflow,error:a,index:n.plan.index,output:n.output};return await r.create({id:o,params:s}),o})}catch(r){e.log?.error(`ctx.parallel: group-saga compensation "${l}" for branch "${n.plan.item.workflow}" (#${String(n.plan.index)}) failed`,r)}}},W=e=>async t=>{if(t.length===0)return[];if(t.length>h)throw new c(`ctx.parallel: ${String(t.length)} branches exceeds the cap of ${String(h)} — split the fan-out or raise the work into fewer child workflows`);const a=t.map((r,o)=>{const s=e.nextChildId(r.id);return{childId:s,eventType:`${w}${s}`,index:o,item:r}}),i=new Set;for(const r of a){if(i.has(r.childId))throw new c(`ctx.parallel: duplicate branch id "${r.childId}" — each branch in a group must resolve to a unique child instance id (check explicit \`id\` options)`);i.add(r.childId)}await Promise.all(a.map(r=>e.step.do(`${f}${r.childId}`,async()=>{const o=e.resolveBinding(r.item.workflow),s={eventType:r.eventType,index:r.index,parentBinding:e.parentBinding,parentId:e.instanceId};return await o.create({id:r.childId,params:{...r.item.params,[p]:s}}),r.childId})));const n=[],l=[];for(const r of a){let o;try{o=(await e.step.waitForEvent(`${v}${r.childId}`,{timeout:r.item.timeout,type:r.eventType})).payload}catch(s){const d=m(s);throw await u(e,l,d),new c(`ctx.parallel: branch "${r.item.workflow}" (#${String(r.index)}) join failed: ${d.message}`)}if(o.status==="error")throw await u(e,l,o.error),new c(`ctx.parallel: branch "${r.item.workflow}" (#${String(r.index)}) failed: ${o.error.message}`);l.push({output:o.value,plan:r}),n.push(o.value)}return n},A=e=>async(t,a,i)=>{if(y(a))throw new g("BAD_REQUEST",`@lunora/workflow: params ${$}`);const n=e.nextChildId(i?.id);return await e.step.do(`${f}${n}`,async()=>(await e.resolveBinding(t).create({id:n,params:a}),n)),e.resolveBinding(t).get(n)},_=e=>{if(typeof e!="object"||e===null)return;const t=e[p];if(typeof t!="object"||t===null)return;const a=t;if(!(typeof a.eventType!="string"||typeof a.parentBinding!="string"||typeof a.parentId!="string"||typeof a.index!="number")&&!(!a.parentBinding.startsWith("WORKFLOW_")||!a.eventType.startsWith(w)))return{eventType:a.eventType,index:a.index,parentBinding:a.parentBinding,parentId:a.parentId}},C=e=>{if(typeof e!="object"||e===null)return e;const t={...e};return Reflect.deleteProperty(t,p),t},I=async(e,t,a)=>{const i=e.env[t.parentBinding];if(!i||typeof i.get!="function")return;const n=i.get.bind(i);await e.step.do(`${x}${String(t.index)}`,async()=>(await(await n(t.parentId)).sendEvent({payload:a,type:t.eventType}),t.eventType))},M=async(e,t,a)=>{try{await I(e,t,a)}catch(i){e.log?.error(`@lunora/workflow: failed to signal branch parent "${t.parentId}" (event "${t.eventType}")`,i)}};export{h as MAX_BRANCHES,T as branch,W as createParallel,A as createSpawn,S as errorOutcome,_ as extractBranchMarker,R as okOutcome,I as signalBranchParent,M as signalBranchParentSafe,C as stripBranchMarker};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t="__lunoraNonRetryable";class c extends Error{constructor(e,o="NonRetryableError"){super(e),this.name=o,this[t]=!0}}const s=r=>r instanceof Error&&r[t]===!0,a=(r,e)=>{const o=new e(r.message,r.name);return r.stack!==void 0&&(o.stack=r.stack),r.cause!==void 0&&o.cause===void 0&&(o.cause=r.cause),o},n=(r,e)=>{throw e!==void 0&&s(r)?a(r,e):r};export{c as NonRetryableError,n as convertNonRetryableError,s as isNonRetryableError,a as toNativeNonRetryableError};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as h}from"@lunora/errors";const $="https://api.cloudflare.com/client/v4/accounts",I={complete:!0,errored:!0,paused:!0,queued:!0,running:!0,terminated:!0,unknown:!0,waiting:!0,waitingForPause:!0},w=e=>typeof e=="string"&&Object.hasOwn(I,e)?e:"unknown",c=e=>typeof e=="string"&&e!==""?e:void 0,f=(e,a)=>typeof e=="string"?e:a,S=e=>typeof e=="boolean"?e:void 0,k=e=>Array.isArray(e)?e.length:typeof e=="number"?e:void 0,m=e=>({createdOn:c(e.created_on),endedOn:c(e.ended_on),id:f(e.id,""),startedOn:c(e.started_on),status:w(e.status)}),b=e=>({attempts:k(e.attempts),end:c(e.end),error:e.error,name:f(e.name,""),output:e.output,start:c(e.start),success:S(e.success),type:c(e.type)});class g extends h{constructor(a,i){super("WORKFLOWS_REST_ERROR",`Cloudflare Workflows REST API returned ${String(a)}: ${i}`,{name:"WorkflowsRestError",status:a})}}const C=e=>{const a=e.fetch??globalThis.fetch.bind(globalThis),i=`${$}/${e.accountId}/workflows`,p=async(r,s)=>{const t=await a(`${i}${r}`,{...s,headers:{Authorization:`Bearer ${e.apiToken}`,"Content-Type":"application/json"}}),o=await t.text();let n;try{n=JSON.parse(o)}catch{throw new g(t.status,o)}if(!t.ok||n.success===!1)throw new g(t.status,o);return n};return{getInstance:async({instanceId:r,workflowName:s})=>{const t=(await p(`/${encodeURIComponent(s)}/instances/${encodeURIComponent(r)}`)).result??{},o=Array.isArray(t.steps)?t.steps:[];return{...m(t),error:t.error,output:t.output,params:t.params,steps:o.map(n=>b(n))}},listInstances:async({page:r,perPage:s,status:t,workflowName:o})=>{const n=new URLSearchParams;t!==void 0&&n.set("status",t),r!==void 0&&n.set("page",String(r)),s!==void 0&&n.set("per_page",String(s));const y=n.toString()===""?"":`?${n.toString()}`,d=await p(`/${encodeURIComponent(o)}/instances${y}`),l=Array.isArray(d.result)?d.result:[],u=d.result_info??{};return{instances:l.map(R=>m(R)),page:typeof u.page=="number"?u.page:r??1,perPage:typeof u.per_page=="number"?u.per_page:s??l.length,totalCount:typeof u.total_count=="number"?u.total_count:void 0}},setInstanceStatus:async({action:r,instanceId:s,workflowName:t})=>{const o=(await p(`/${encodeURIComponent(t)}/instances/${encodeURIComponent(s)}`,{body:JSON.stringify({status:r}),method:"PATCH"})).result??{};return{status:w(o.status)}}}};export{g as WorkflowsRestError,C as createWorkflowsRestClient};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r="__lunoraBranch",e=a=>typeof a=="object"&&a!==null&&Object.hasOwn(a,r),o=`may not contain the reserved workflow branch-marker key ("${r}")`;export{o as B,r as a,e as h};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{parseValidatorMap as v}from"@lunora/values";import{convertNonRetryableError as p,NonRetryableError as y}from"./NonRetryableError-CXD-mZe4.mjs";const b=(r,t)=>v(r,t,"step args"),E=r=>async(t,g,f)=>{const o=f?.config??t.config,s=b(t.args,g),l=async n=>{const m={attempt:n.attempt,config:n.config,env:r.env,log:r.log,run:r.run,step:n.step};let e;try{e=await t.handler(m,s)}catch(a){return p(a,r.nonRetryableErrorClass)}if(!t.returns)return e;try{return t.returns.parse(e)}catch(a){const d=a instanceof Error?a.message:String(a),u=new y(`step "${t.name}" returns validation failed: ${d}`);return a!==void 0&&(u.cause=a),p(u,r.nonRetryableErrorClass)}},c=t.rollback,i=c?{rollback:async n=>{await c({args:s,env:r.env,error:n.error,log:r.log,output:n.output,run:r.run})},rollbackConfig:t.rollbackConfig}:void 0;return o===void 0?r.step.do(t.name,l,i):r.step.do(t.name,o,l,i)};export{E as createRunStep,b as validateStepArgs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import r from"./createWorkflows-3djec-oS.mjs";const i=(e,c)=>{const o={};for(const n of c){const t=e[n.binding];t&&typeof t.create=="function"&&typeof t.createBatch=="function"&&typeof t.get=="function"&&(o[n.exportName]=t)}return r({bindings:o})};export{i as createWorkflowContext};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as f}from"@lunora/errors";import{workflowBindingName as h}from"./defineWorkflow-fBuCDkGT.mjs";import{createSpawn as N,createParallel as m}from"./MAX_BRANCHES-L0FzeGTH.mjs";import{createRunStep as v}from"./createRunStep-CzGKC1eZ.mjs";const I=e=>({debug:(t,...n)=>{console.debug(e,t,...n)},error:(t,...n)=>{console.error(e,t,...n)},info:(t,...n)=>{console.info(e,t,...n)},warn:(t,...n)=>{console.warn(e,t,...n)}}),b=e=>{let t="";for(let n=0;n<e.length;n+=32768)t+=String.fromCharCode(...e.subarray(n,n+32768));return btoa(t)},g=e=>b(e).replaceAll("+","-").replaceAll("/","_").replace(/=+$/,"");new TextDecoder;const y=new TextEncoder,w="=",R=e=>{for(let t=0;t<e.length;t+=1)if(e.charCodeAt(t)>255)return!1;return!0},A=e=>g(y.encode(JSON.stringify(e))),O=e=>!e.startsWith(w)&&R(e)?e:`${w}${g(y.encode(e))}`,S="/_lunora/scheduler/dispatch",L=e=>{let t=e.length;for(;t>0&&e[t-1]==="/";)t-=1;return e.slice(0,t)},T=(e,t,n)=>{try{const r=JSON.parse(n)?.error;if(typeof r=="object"&&r!==null&&typeof r.code=="string"){const{code:i,data:u,message:s}=r;return new f(i,typeof s=="string"?s:void 0,{data:u,status:t})}}catch{}return new f("INTERNAL",`${e}: function dispatch failed (${String(t)}): ${n}`,{status:t})},k=e=>{const{label:t}=e,n=globalThis.fetch,r=e.fetchImpl??(typeof n=="function"?n.bind(globalThis):void 0);return async(i,u,s={})=>{if(typeof r!="function")throw new TypeError(`${t}: no fetch implementation available — pass fetchImpl or run on a platform with global fetch`);const o=e.env.LUNORA_ORIGIN_URL;if(typeof o!="string"||o.length===0)throw new f("INTERNAL",`${t}: \`LUNORA_ORIGIN_URL\` must be set on the Worker env so a handler can call back into Lunora functions`);const a=e.env.LUNORA_ADMIN_TOKEN;if(typeof a!="string"||a.length===0)throw new f("INTERNAL",`${t}: \`LUNORA_ADMIN_TOKEN\` must be set on the Worker env to authenticate function dispatch`);const l=`${L(o)}${S}`,d={authorization:`Bearer ${a}`,"content-type":"application/json"};e.identity?.userId!==void 0&&(d["x-lunora-userid"]=O(e.identity.userId)),e.identity?.claims!==void 0&&(d["x-lunora-identity"]=A(e.identity.claims));const c=await r(l,{body:JSON.stringify({args:u??{},functionPath:i.__lunoraRef,shardKey:s.shardKey}),headers:d,method:"POST"});if(!c.ok)throw T(t,c.status,await c.text());const p=await c.text();if(p.length!==0)try{return JSON.parse(p)}catch{throw new f("INTERNAL",`${t}: function dispatch returned a non-JSON body (${String(c.status)}): ${p}`,{status:c.status})}}},C=e=>{const t=I(`[workflow:${e.exportName}]`),n=k({env:e.env,fetchImpl:e.fetchImpl,label:"@lunora/workflow"}),r=o=>{const a=h(o),l=e.env[a];if(!l||typeof l.create!="function"||typeof l.get!="function")throw new f("INTERNAL",`@lunora/workflow: cannot spawn child workflow "${o}" — no Workflow binding "${a}" on env (is it declared in lunora/workflows.ts?)`);return l};let i=0;const u=o=>{if(o!==void 0)return o;const a=`${e.event.instanceId}-c${String(i)}`;return i+=1,a},s={env:e.env,instanceId:e.event.instanceId,log:t,nextChildId:u,parentBinding:h(e.exportName),resolveBinding:r,step:e.step};return{env:e.env,event:e.event,log:t,parallel:m(s),params:e.event.payload,run:n,runStep:v({env:e.env,log:t,nonRetryableErrorClass:e.nonRetryableErrorClass,run:n,step:e.step}),spawn:N(s),step:e.step}};export{C as createWorkflowRunContext};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as s}from"@lunora/errors";import{h as w,B as f}from"./branch-marker-B2lebQA6.mjs";const t=o=>{if(w(o?.params))throw new s("BAD_REQUEST",`@lunora/workflow: params ${f}`)},l=o=>({create:async r=>(t(r),o.create(r)),createBatch:async r=>{for(const a of r)t(a);return o.createBatch(r)},get:async r=>o.get(r)}),k=o=>{const r=o.bindings??{};return{get:a=>{const n=r[a];if(n===void 0){const e=Object.keys(r),c=e.length===0?"no workflows are declared":`known workflows: ${e.join(", ")}`;throw new s("INTERNAL",`@lunora/workflow: no workflow named "${a}" (${c})`)}return l(n)}}};export{k as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const o=(r,e)=>{if(typeof r!="string"||r.length===0)throw new TypeError("defineStep: `name` must be a non-empty string (the durable step label)");const n=e.args;if(typeof n!="object"||n===null)throw new TypeError("defineStep: `args` must be a validator map (e.g. `{ id: v.string() }`)");if(typeof e.handler!="function")throw new TypeError("defineStep: `handler` must be a function (the step body)");if(e.rollback!==void 0&&typeof e.rollback!="function")throw new TypeError("defineStep: `rollback` must be a function when provided");return{args:e.args,config:e.config,handler:e.handler,isLunoraStep:!0,name:r,returns:e.returns,rollback:e.rollback,rollbackConfig:e.rollbackConfig}},t=r=>typeof r=="object"&&r!==null&&r.isLunoraStep===!0;export{o as defineStep,t as isStepDefinition};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const o=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}Workflow`,r=e=>`WORKFLOW_${e.replaceAll(/(?<=[a-z0-9])(?=[A-Z])/g,"_").toUpperCase()}`,n=e=>e.replaceAll(/(?<=[a-z0-9])(?=[A-Z])/g,"-").toLowerCase(),a=e=>{if(typeof e.handler!="function")throw new TypeError("defineWorkflow: `handler` must be a function (the workflow body)");if(e.name!==void 0&&(typeof e.name!="string"||e.name.length===0))throw new TypeError("defineWorkflow: `name` must be a non-empty string when provided");return{...e,isLunoraWorkflow:!0}},t=e=>typeof e=="object"&&e!==null&&e.isLunoraWorkflow===!0;export{a as defineWorkflow,t as isWorkflowDefinition,r as workflowBindingName,o as workflowClassName,n as workflowDefaultName};
|