@lunora/workflow 1.0.0-alpha.5 → 1.0.0-alpha.50

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 (34) hide show
  1. package/LICENSE.md +6 -0
  2. package/README.md +25 -2
  3. package/dist/do/index.d.mts +16 -16
  4. package/dist/do/index.d.ts +16 -16
  5. package/dist/do/index.mjs +1 -44
  6. package/dist/index.d.mts +187 -124
  7. package/dist/index.d.ts +187 -124
  8. package/dist/index.mjs +1 -9
  9. package/dist/packem_shared/MAX_BRANCHES-j59ZUUSu.mjs +1 -0
  10. package/dist/packem_shared/NonRetryableError-BPu01mU8.mjs +1 -0
  11. package/dist/packem_shared/WorkflowsRestError-Ccq1FUR1.mjs +1 -0
  12. package/dist/packem_shared/branch-marker-CCpWfS5k.mjs +1 -0
  13. package/dist/packem_shared/createRunStep-DnLETYtC.mjs +1 -0
  14. package/dist/packem_shared/createWaitForEvent-D5Ohn2Pz.mjs +1 -0
  15. package/dist/packem_shared/createWorkflowContext-zQR_9N-v.mjs +1 -0
  16. package/dist/packem_shared/createWorkflowRunContext-C03ogm48.mjs +1 -0
  17. package/dist/packem_shared/createWorkflows-LvAyUyKq.mjs +1 -0
  18. package/dist/packem_shared/defineStep-D1-9eOnA.mjs +1 -0
  19. package/dist/packem_shared/defineWorkflow-tKaIifbZ.mjs +1 -0
  20. package/dist/packem_shared/defineWorkflowEvent-suERPEEV.mjs +1 -0
  21. package/dist/packem_shared/isDuplicateInstanceError-CecnqNAa.mjs +1 -0
  22. package/dist/packem_shared/run-step-CFhxLFfp.mjs +1 -0
  23. package/dist/packem_shared/{types.d-C9W6rEWv.d.mts → types.d-C7jti0tm.d.mts} +221 -124
  24. package/dist/packem_shared/{types.d-C9W6rEWv.d.ts → types.d-C7jti0tm.d.ts} +221 -124
  25. package/package.json +3 -2
  26. package/dist/packem_shared/MAX_BRANCHES-D6cQabJ1.mjs +0 -132
  27. package/dist/packem_shared/NonRetryableError-Dn2dTyBS.mjs +0 -27
  28. package/dist/packem_shared/WorkflowsRestError-b06i7K5j.mjs +0 -118
  29. package/dist/packem_shared/createRunStep-8jOXxP2o.mjs +0 -54
  30. package/dist/packem_shared/createWorkflowContext-D6thzmlF.mjs +0 -14
  31. package/dist/packem_shared/createWorkflowRunContext-CYI9SeI9.mjs +0 -110
  32. package/dist/packem_shared/createWorkflows-BoSYVIXg.mjs +0 -23
  33. package/dist/packem_shared/defineStep-DJQtLw7g.mjs +0 -28
  34. package/dist/packem_shared/defineWorkflow-DbUC-oCN.mjs +0 -15
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
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-C9W6rEWv.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-C9W6rEWv.js";
3
- import '@lunora/values';
1
+ import { a as Workflows, L as LunoraWorkflowsOptions, b as WorkflowEventDefinition, S as StepArgsValidator, c as StepConfig, d as StepDefinition, e as WorkflowConfig, W as WorkflowDefinition, f as WorkflowBranch, g as WorkflowInstanceStatus, h as WorkflowEventLike, i as WorkflowStepLike, j as WorkflowRunContext, k as WorkflowLogger, l as WorkflowRunFunction, m as WorkflowRunStepFunction, n as WorkflowWaitForEventFunction } from "./packem_shared/types.d-C7jti0tm.js";
2
+ export type { A as ArgsOf, B as BranchCompensationParams, F as FunctionKind, o as FunctionReference, I as InferStepArgs, R as RunFunctionOptions, p as RunStepOptions, q as StepHandler, r as StepRollbackContext, s as StepRollbackHandler, t as StepRunContext, u as WaitForEventOptions, v as WorkflowBindingLike, w as WorkflowBranchOutputs, x as WorkflowCreateOptions, y as WorkflowHandle, z as WorkflowHandler, C as WorkflowInstanceLike, D as WorkflowParallelFunction, E as WorkflowRollbackContextLike, G as WorkflowRollbackHandlerLike, H as WorkflowSpawnFunction, J as WorkflowSpawnOptions, K as WorkflowStatusResult, M as WorkflowStepConfigLike, N as WorkflowStepContextLike, O as WorkflowStepRollbackOptionsLike } from "./packem_shared/types.d-C7jti0tm.js";
3
+ import { Validator } from '@lunora/values';
4
+ import { LunoraError } from '@lunora/errors';
4
5
  /** Wiring info for one declared workflow, emitted by codegen into the generated shard. */
5
6
  interface WorkflowBindingSpec {
6
7
  /** The Cloudflare `Workflow` binding name, e.g. `WORKFLOW_ORDER_PIPELINE`. */
@@ -9,108 +10,148 @@ 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 an external event, its wire type, and its payload shape.
28
+ *
29
+ * ```ts
30
+ * // lunora/events.ts
31
+ * import { defineWorkflowEvent } from "@lunora/workflow";
32
+ * import { v } from "@lunora/values";
33
+ *
34
+ * export const orderApproved = defineWorkflowEvent("order-approved", v.object({ approvedBy: v.string() }));
35
+ * ```
36
+ *
37
+ * Wait on it inside a workflow body — the payload is typed and validated:
38
+ *
39
+ * ```ts
40
+ * const { approvedBy } = await ctx.waitForEvent(orderApproved, { name: "await approval", timeout: "7 days" });
41
+ * ```
42
+ *
43
+ * …and send it from a mutation/action with the same definition:
44
+ *
45
+ * ```ts
46
+ * await ctx.workflows.get("orderPipeline").sendEvent(instanceId, orderApproved, { approvedBy });
47
+ * ```
48
+ */
49
+ declare const defineWorkflowEvent: <Payload>(type: string, payload: Validator<Payload>) => WorkflowEventDefinition<Payload>;
50
+ /**
51
+ * True when a value is usable as an event definition. The brand alone is not the
52
+ * test: the predicate hands the caller a `type` and a `payload.parse`, so it checks
53
+ * for both (and for the reserved namespace) rather than trusting a public boolean.
54
+ */
55
+ declare const isWorkflowEventDefinition: (value: unknown) => value is WorkflowEventDefinition;
56
+ /**
57
+ * Declare a reusable durable step. Same `args` map shape a Lunora `query` /
58
+ * `mutation` / `action` uses, so a step reads like a function:
59
+ *
60
+ * ```ts
61
+ * // lunora/steps.ts
62
+ * import { defineStep } from "@lunora/workflow";
63
+ * import { v } from "@lunora/values";
64
+ *
65
+ * export const fetchImage = defineStep("fetch image", {
66
+ * args: { imageKey: v.string() },
67
+ * returns: v.object({ data: v.bytes() }),
68
+ * handler: async (ctx, { imageKey }) => {
69
+ * const object = await (ctx.env.BUCKET as R2Bucket).get(imageKey);
70
+ * return { data: new Uint8Array(await object!.arrayBuffer()) };
71
+ * },
72
+ * rollback: async (ctx) => {
73
+ * await (ctx.env.BUCKET as R2Bucket).delete(`tmp/${ctx.args.imageKey}`);
74
+ * },
75
+ * });
76
+ * ```
77
+ *
78
+ * Then, inside a `defineWorkflow` handler:
79
+ *
80
+ * ```ts
81
+ * const { data } = await ctx.runStep(fetchImage, { imageKey: ctx.params.imageKey });
82
+ * ```
83
+ */
53
84
  declare const defineStep: <A extends StepArgsValidator, Result>(name: string, config: StepConfig<A, Result>) => StepDefinition<A, Result>;
54
85
  /** True when a value is a `defineStep` result (the runtime brand check). */
55
86
  declare const isStepDefinition: (value: unknown) => value is StepDefinition;
56
87
  /**
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
- */
88
+ * The generated `WorkflowEntrypoint` class name for a `lunora/workflows.ts`
89
+ * export: `orderPipeline` → `OrderPipelineWorkflow`. wrangler's
90
+ * `workflows[].class_name` references it, so codegen and the config layer MUST
91
+ * derive it identically — always via this helper.
92
+ */
62
93
  declare const workflowClassName: (exportName: string) => string;
63
94
  /**
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
- */
95
+ * The wrangler binding name for a workflow export: `orderPipeline` →
96
+ * `WORKFLOW_ORDER_PIPELINE`, `etl` → `WORKFLOW_ETL`. The `WORKFLOW_` prefix
97
+ * namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`/`CONTAINER_*` so a
98
+ * workflow export can never collide with the built-in bindings.
99
+ */
69
100
  declare const workflowBindingName: (exportName: string) => string;
70
101
  /**
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
- */
102
+ * The stable workflow name wrangler registers (`workflows[].name`):
103
+ * `orderPipeline` → `order-pipeline`. Used as the deployed workflow's
104
+ * identifier when no explicit `name` override is given.
105
+ */
75
106
  declare const workflowDefaultName: (exportName: string) => string;
76
107
  /**
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&lt;{ orderId: string }>({
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
- */
108
+ * Declare a durable workflow deployed alongside the app. Pure validation +
109
+ * branding: codegen discovers the export, emits the `WorkflowEntrypoint`
110
+ * subclass (`_generated/workflows.ts`), and wires the typed `ctx.workflows`
111
+ * handle; the config layer reconciles the wrangler `workflows[]` entry from the
112
+ * same definition.
113
+ *
114
+ * ```ts
115
+ * // lunora/workflows.ts
116
+ * import { defineWorkflow } from "@lunora/workflow";
117
+ * import { api } from "./_generated/api";
118
+ *
119
+ * export const orderPipeline = defineWorkflow<{ orderId: string }>({
120
+ * handler: async (ctx) => {
121
+ * const order = await ctx.step.do("load", () => ctx.run(api.orders.get, { id: ctx.params.orderId }));
122
+ * await ctx.step.sleep("cool-off", "1 minute");
123
+ * await ctx.step.do("charge", () => ctx.run(api.payments.charge, { orderId: ctx.params.orderId }));
124
+ * return order;
125
+ * },
126
+ * });
127
+ * ```
128
+ */
98
129
  declare const defineWorkflow: <Params = Record<string, unknown>, Output = unknown>(config: WorkflowConfig<Params, Output>) => WorkflowDefinition<Params, Output>;
99
130
  /** True when a value is a `defineWorkflow` result (the runtime brand check). */
100
131
  declare const isWorkflowDefinition: (value: unknown) => value is WorkflowDefinition;
101
132
  /**
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
- */
133
+ * Whether a `WorkflowBinding.create()` rejection is a duplicate-instance-id
134
+ * error — the idempotency signal that a *previous* attempt's create already
135
+ * applied — as opposed to a transient or config failure (Workflows service
136
+ * error, instance-creation quota, bad params).
137
+ *
138
+ * Every other failure MUST surface, so the caller retries or fails visibly
139
+ * rather than silently reporting success for work that never started.
140
+ */
141
+ declare const isDuplicateInstanceError: (error: unknown) => boolean;
142
+ /**
143
+ * Throw from a workflow step (or handler) to fail the instance immediately
144
+ * **without** retrying — the portable mirror of `cloudflare:workflows`'
145
+ * `NonRetryableError`. Importable from Node, so workflow code stays unit-testable.
146
+ *
147
+ * ```ts
148
+ * import { NonRetryableError } from "@lunora/workflow";
149
+ *
150
+ * if (order.status === "cancelled") {
151
+ * throw new NonRetryableError("order already cancelled — no point retrying");
152
+ * }
153
+ * ```
154
+ */
114
155
  declare class NonRetryableError extends Error {
115
156
  constructor(message: string, name?: string);
116
157
  }
@@ -119,34 +160,31 @@ declare const isNonRetryableError: (value: unknown) => value is NonRetryableErro
119
160
  /** Constructor shape of `cloudflare:workflows`' native `NonRetryableError`. */
120
161
  type NativeNonRetryableErrorConstructor = new (message: string, name?: string) => Error;
121
162
  /**
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
- */
163
+ * Rebuild a portable {@link NonRetryableError} as the native Cloudflare one,
164
+ * preserving its `name`, `message`, `cause`, and `stack`. Used at the `src/do`
165
+ * boundary where the native constructor is available; everywhere else the
166
+ * portable error is thrown unchanged (and still honored by name).
167
+ */
127
168
  declare const toNativeNonRetryableError: (error: NonRetryableError, NativeNonRetryableError: NativeNonRetryableErrorConstructor) => Error;
128
169
  /**
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
- */
170
+ * If `error` is a portable {@link NonRetryableError} and a native constructor is
171
+ * available, rethrow it as the native error; otherwise rethrow `error` as-is.
172
+ * Always throws — the `never` return lets callers `return convertNonRetryableError(...)`.
173
+ */
133
174
  declare const convertNonRetryableError: (error: unknown, NativeNonRetryableError: NativeNonRetryableErrorConstructor | undefined) => never;
134
175
  /** Hard cap on branches per `ctx.parallel` call — auto-scale, never silently spawn unbounded DOs. */
135
176
  declare const MAX_BRANCHES = 100;
136
- /** The params key the parent injects so a child knows to signal completion back. Stripped before the user handler sees `ctx.params`. */
137
-
138
177
  /**
139
- * Build a single fan-out branch: a declared child workflow referenced by its
140
- * `lunora/workflows.ts` export name, plus the params it is created with. Pass the
141
- * output type as the generic argument so `ctx.parallel(...)` infers the result
142
- * tuple — e.g. `branch("imageTag", { key })` typed as `branch` of `{ tags }`.
143
- */
178
+ * Build a single fan-out branch: a declared child workflow referenced by its
179
+ * `lunora/workflows.ts` export name, plus the params it is created with. Pass the
180
+ * output type as the generic argument so `ctx.parallel(...)` infers the result
181
+ * tuple — e.g. `branch("imageTag", { key })` typed as `branch` of `{ tags }`.
182
+ */
144
183
  declare const branch: <Output = unknown>(workflow: string, params?: Record<string, unknown>, options?: {
145
184
  compensateWith?: string;
146
185
  id?: string;
147
186
  timeout?: number | string;
148
187
  }) => WorkflowBranch<Output>;
149
- /** Build the success outcome a completed branch reports to its parent. */
150
188
  /** The lifecycle mutations the REST API exposes via `PATCH .../instances/{id}`. */
151
189
  type WorkflowInstanceAction = "pause" | "resume" | "terminate";
152
190
  /** Configuration for a {@link WorkflowsRestClient}. */
@@ -193,9 +231,15 @@ interface WorkflowInstancePage {
193
231
  perPage: number;
194
232
  totalCount?: number;
195
233
  }
196
- /** Thrown when the REST API responds non-2xx or `success: false`; carries the status plus body for the caller to surface. */
197
- declare class WorkflowsRestError extends Error {
198
- readonly status: number;
234
+ /**
235
+ * Thrown when the REST API responds non-2xx or `success: false`; carries the
236
+ * status plus a capped body preview, with the full body on `cause`.
237
+ *
238
+ * The preview is capped because the Cloudflare API's auth/authorization error
239
+ * text (and its HTML gateway pages) would otherwise ride out verbatim —
240
+ * `WORKFLOWS_REST_ERROR` is non-internal, so the message reaches the browser.
241
+ */
242
+ declare class WorkflowsRestError extends LunoraError {
199
243
  constructor(status: number, body: string);
200
244
  }
201
245
  /** The observe client: list instances, read one instance's steps, and (with Edit scope) mutate its status. */
@@ -219,11 +263,11 @@ interface WorkflowsRestClient {
219
263
  }>;
220
264
  }
221
265
  /**
222
- * Build a {@link WorkflowsRestClient}. Each call hits the account-scoped REST
223
- * endpoint with the bearer token, unwraps Cloudflare's
224
- * `{ success, errors, result, result_info }` envelope, and normalizes the
225
- * snake_case payload into the camelCase shapes the studio renders.
226
- */
266
+ * Build a {@link WorkflowsRestClient}. Each call hits the account-scoped REST
267
+ * endpoint with the bearer token, unwraps Cloudflare's
268
+ * `{ success, errors, result, result_info }` envelope, and normalizes the
269
+ * snake_case payload into the camelCase shapes the studio renders.
270
+ */
227
271
  declare const createWorkflowsRestClient: (config: WorkflowsRestConfig) => WorkflowsRestClient;
228
272
  interface RunContextOptions<Params> {
229
273
  env: Record<string, unknown>;
@@ -237,12 +281,12 @@ interface RunContextOptions<Params> {
237
281
  /** Assemble the {@link WorkflowRunContext} passed to a `defineWorkflow` handler. */
238
282
  declare const createWorkflowRunContext: <Params = Record<string, unknown>>(options: RunContextOptions<Params>) => WorkflowRunContext<Params>;
239
283
  /**
240
- * Validate a step's args through its validator map, prefixing any
241
- * `ValidationError` with `step args.&lt;key>` so the failure points at the
242
- * offending field. Delegates to `@lunora/values`' shared {@link parseValidatorMap}
243
- * — the same parser the procedure builder and HTTP routes use — so the
244
- * optional-skip and error-prefix semantics stay in lockstep across the framework.
245
- */
284
+ * Validate a step's args through its validator map, prefixing any
285
+ * `ValidationError` with `step args.<key>` so the failure points at the
286
+ * offending field. Delegates to `@lunora/values`' shared {@link parseValidatorMap}
287
+ * — the same parser the procedure builder and HTTP routes use — so the
288
+ * optional-skip and error-prefix semantics stay in lockstep across the framework.
289
+ */
246
290
  declare const validateStepArgs: (validators: StepArgsValidator, source: Record<string, unknown>) => Record<string, unknown>;
247
291
  /** Dependencies needed to run a step: the native step API plus the workflow's env / runner / logger. */
248
292
  interface RunStepDeps {
@@ -258,10 +302,29 @@ interface RunStepDeps {
258
302
  step: WorkflowStepLike;
259
303
  }
260
304
  /**
261
- * Build the `ctx.runStep` function bound to one workflow invocation. Each call
262
- * runs the step through `step.do(...)`: validate args → run body → validate
263
- * result (when `returns` is declared), with any portable `NonRetryableError`
264
- * converted to the native one and any declared rollback forwarded to Cloudflare.
265
- */
305
+ * Build the `ctx.runStep` function bound to one workflow invocation. Each call
306
+ * runs the step through `step.do(...)`: validate args → run body → validate
307
+ * result (when `returns` is declared), with any portable `NonRetryableError`
308
+ * converted to the native one and any declared rollback forwarded to Cloudflare.
309
+ */
266
310
  declare const createRunStep: (deps: RunStepDeps) => WorkflowRunStepFunction;
267
- 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 };
311
+ /** Dependencies one workflow invocation's `ctx.waitForEvent` closes over. */
312
+ interface WaitForEventDeps {
313
+ /** Native `cloudflare:workflows` `NonRetryableError` constructor — injected by `src/do`; absent in Node tests. */
314
+ nonRetryableErrorClass?: NativeNonRetryableErrorConstructor;
315
+ /** The native Cloudflare durable-step API. */
316
+ step: WorkflowStepLike;
317
+ }
318
+ /**
319
+ * Build `ctx.waitForEvent` for one workflow invocation.
320
+ *
321
+ * Every failure here is **non-retryable**. A malformed definition or a reserved
322
+ * step name is a deterministic programmer error, and a payload that fails the
323
+ * validator has already consumed the event — replaying the wait cannot produce a
324
+ * different value, it can only hibernate the instance until its timeout. Each is
325
+ * raised through the shared {@link raiseNonRetryable}, the same classification path
326
+ * `ctx.runStep` uses, so the native error reaches Cloudflare and the instance fails
327
+ * fast.
328
+ */
329
+ declare const createWaitForEvent: (deps: WaitForEventDeps) => WorkflowWaitForEventFunction;
330
+ export { type LunoraWorkflowsOptions, MAX_BRANCHES, type NativeNonRetryableErrorConstructor, NonRetryableError, type StepArgsValidator, type StepConfig, type StepDefinition, type WorkflowBindingSpec, type WorkflowBranch, type WorkflowConfig, type WorkflowDefinition, type WorkflowEventDefinition, 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 WorkflowWaitForEventFunction, type Workflows, type WorkflowsRestClient, type WorkflowsRestConfig, WorkflowsRestError, branch, convertNonRetryableError, createRunStep, createWaitForEvent, createWorkflowContext, createWorkflowRunContext, createWorkflows, createWorkflowsRestClient, defineStep, defineWorkflow, defineWorkflowEvent, isDuplicateInstanceError, isNonRetryableError, isStepDefinition, isWorkflowDefinition, isWorkflowEventDefinition, toNativeNonRetryableError, validateStepArgs, workflowBindingName, workflowClassName, workflowDefaultName };
package/dist/index.mjs CHANGED
@@ -1,9 +1 @@
1
- export { createWorkflowContext } from './packem_shared/createWorkflowContext-D6thzmlF.mjs';
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 { MAX_BRANCHES, branch } from './packem_shared/MAX_BRANCHES-D6cQabJ1.mjs';
7
- export { WorkflowsRestError, createWorkflowsRestClient } from './packem_shared/WorkflowsRestError-b06i7K5j.mjs';
8
- export { createWorkflowRunContext } from './packem_shared/createWorkflowRunContext-CYI9SeI9.mjs';
9
- export { createRunStep, validateStepArgs } from './packem_shared/createRunStep-8jOXxP2o.mjs';
1
+ import{createWorkflowContext as e}from"./packem_shared/createWorkflowContext-zQR_9N-v.mjs";import{default as f}from"./packem_shared/createWorkflows-LvAyUyKq.mjs";import{defineWorkflowEvent as a,isWorkflowEventDefinition as i}from"./packem_shared/defineWorkflowEvent-suERPEEV.mjs";import{defineStep as p,isStepDefinition as s}from"./packem_shared/defineStep-D1-9eOnA.mjs";import{defineWorkflow as w,isWorkflowDefinition as x,workflowBindingName as k,workflowClassName as c,workflowDefaultName as E}from"./packem_shared/defineWorkflow-tKaIifbZ.mjs";import{NonRetryableError as N,convertNonRetryableError as R,isNonRetryableError as v,toNativeNonRetryableError as d}from"./packem_shared/NonRetryableError-BPu01mU8.mjs";import{MAX_BRANCHES as u,branch as C}from"./packem_shared/MAX_BRANCHES-j59ZUUSu.mjs";import{WorkflowsRestError as S,createWorkflowsRestClient as y}from"./packem_shared/WorkflowsRestError-Ccq1FUR1.mjs";import{createWorkflowRunContext as g}from"./packem_shared/createWorkflowRunContext-C03ogm48.mjs";import{c as h,v as F}from"./packem_shared/run-step-CFhxLFfp.mjs";import{createWaitForEvent as I}from"./packem_shared/createWaitForEvent-D5Ohn2Pz.mjs";import{isDuplicateInstanceError as X}from"./packem_shared/isDuplicateInstanceError-CecnqNAa.mjs";export{u as MAX_BRANCHES,N as NonRetryableError,S as WorkflowsRestError,C as branch,R as convertNonRetryableError,h as createRunStep,I as createWaitForEvent,e as createWorkflowContext,g as createWorkflowRunContext,f as createWorkflows,y as createWorkflowsRestClient,p as defineStep,w as defineWorkflow,a as defineWorkflowEvent,X as isDuplicateInstanceError,v as isNonRetryableError,s as isStepDefinition,x as isWorkflowDefinition,i as isWorkflowEventDefinition,d as toNativeNonRetryableError,F as validateStepArgs,k as workflowBindingName,c as workflowClassName,E as workflowDefaultName};
@@ -0,0 +1 @@
1
+ import{LunoraError as R}from"@lunora/errors";import{h as x,B as A,a as g}from"./branch-marker-CCpWfS5k.mjs";import{RESERVED_EVENT_TYPE_PREFIX as P}from"./defineWorkflowEvent-suERPEEV.mjs";import{NonRetryableError as f}from"./NonRetryableError-BPu01mU8.mjs";import{isDuplicateInstanceError as N}from"./isDuplicateInstanceError-CecnqNAa.mjs";const p=t=>t.toString(16).padStart(4,"0"),O=t=>{let e=8997,n=33826,a=40164,o=52210;for(let c=0;c<t.length;c+=1){e^=t.charCodeAt(c);const l=e*435,d=n*435,h=a*435+e*256,r=o*435+n*256,s=d+(l>>>16),i=h+(s>>>16),u=r+(i>>>16);e=l&65535,n=s&65535,a=i&65535,o=u&65535}return p(o)+p(a)+p(n)+p(e)},v=100,I=100,b="lunora:spawn:",k="lunora:await:",C="lunora:signal:",W="lunora:compensate:",F=17,w=(t,e="")=>t.length+e.length<=I?`${t}${e}`:`${t.slice(0,I-e.length-F)}-${O(t)}${e}`,S=`${P}branch:`,G=(t,e,n)=>({compensateWith:n?.compensateWith,id:n?.id,params:e,timeout:n?.timeout,workflow:t}),T=t=>t instanceof Error?{message:t.message,name:t.name}:{message:String(t),name:"Error"},M=t=>({status:"ok",value:t}),U=t=>({error:T(t),status:"error"}),$=1048576,X=t=>{try{return String(t instanceof Error?t.message:t).slice(0,200)}catch{return"the failure itself could not be described"}},B=t=>{let e;try{e=new TextEncoder().encode(JSON.stringify(t)).length}catch(n){return{error:{message:`branch outcome cannot be serialised to JSON (${X(n)}) — the parent can never receive it. Return a plain JSON value (no cycles, no BigInt, no class instance that fails to serialise) or a reference the parent can dereference (an R2 key, a row id)`,name:"BranchOutputUnserializable"},status:"error"}}return e<=$?t:{error:{message:`branch outcome serialises to ${String(e)} bytes, over Cloudflare's ${String($)}-byte event payload limit — the parent can never receive it. Return a reference the parent can dereference (an R2 key, a row id) instead of the payload itself`,name:"BranchOutputTooLarge"},status:"error"}},E=async(t,e)=>{try{return{attached:!1,instance:await t.create(e)}}catch(n){if(!N(n))throw n;return{attached:!0,instance:await t.get(e.id)}}},j=async t=>{const{error:e,output:n,status:a}=await t.status();if(a==="complete")return M(n);if(a==="errored"||a==="terminated")return{error:e??{message:`branch instance ${a}`,name:"Error"},status:"error"}},D=t=>{if(typeof t!="object"||t===null)return;const{status:e}=t;return e==="error"||e==="ok"?t:void 0};class m extends Error{branchError;kind;plan;constructor(e,n,a){super(n.message),this.branchError=n,this.kind=a,this.plan=e}}const H=async(t,e,n)=>{for(let a=e.length-1;a>=0;a-=1){const o=e[a],c=o?.plan.item.compensateWith;if(!(o===void 0||c===void 0))try{const l=t.resolveBinding(c);await t.step.do(`${W}${o.plan.childId}`,async()=>{const d=w(o.plan.childId,"-compensate"),h={branch:o.plan.item.workflow,error:n,index:o.plan.index,output:o.output};return await E(l,{id:d,params:h}),d})}catch(l){t.log?.error(`ctx.parallel: group-saga compensation "${c}" for branch "${o.plan.item.workflow}" (#${String(o.plan.index)}) failed`,l)}}},q=t=>async n=>{if(n.length===0)return[];if(n.length>v)throw new f(`ctx.parallel: ${String(n.length)} branches exceeds the cap of ${String(v)} — split the fan-out or raise the work into fewer child workflows`);const a=n.map((r,s)=>{const i=w(t.nextChildId(r.id));return{childId:i,eventType:`${S}${i}`,index:s,item:r}}),o=new Set;for(const r of a){if(o.has(r.childId))throw new f(`ctx.parallel: duplicate branch id "${r.childId}" — each branch in a group must resolve to a unique child instance id (check explicit \`id\` options)`);o.add(r.childId)}const c=await Promise.all(a.map(r=>t.step.do(`${b}${r.childId}`,async()=>{const s=t.resolveBinding(r.item.workflow),i={eventType:r.eventType,index:r.index,parentBinding:t.parentBinding,parentId:t.instanceId},{attached:u,instance:_}=await E(s,{id:r.childId,params:{...r.item.params,[g]:i}});if(!u)return;const y=await j(_);return y===void 0?void 0:B(y)}))),l=Array.from({length:a.length}),d=Array.from({length:a.length}),h=async(r,s)=>{let i;if(s===void 0)try{i=(await t.step.waitForEvent(`${k}${r.childId}`,{timeout:r.item.timeout,type:r.eventType})).payload}catch(u){throw new m(r,T(u),"join failed")}else i=s;if(i.status==="error")throw new m(r,i.error,"failed");d[r.index]={output:i.value,plan:r},l[r.index]=i.value};try{await Promise.all(a.map((r,s)=>h(r,D(c[s]))))}catch(r){throw r instanceof m?(await H(t,d.filter(s=>s!==void 0),r.branchError),new f(`ctx.parallel: branch "${r.plan.item.workflow}" (#${String(r.plan.index)}) ${r.kind}: ${r.branchError.message}`)):r}return l},Q=t=>async(e,n,a)=>{if(x(n))throw new R("BAD_REQUEST",`@lunora/workflow: params ${A}`);const o=w(t.nextChildId(a?.id));return await t.step.do(`${b}${o}`,async()=>{const c=t.resolveBinding(e);return await E(c,{id:o,params:n}),o}),t.resolveBinding(e).get(o)},Z=t=>{if(typeof t!="object"||t===null)return;const e=t[g];if(typeof e!="object"||e===null)return;const n=e;if(!(typeof n.eventType!="string"||typeof n.parentBinding!="string"||typeof n.parentId!="string"||typeof n.index!="number")&&!(!n.parentBinding.startsWith("WORKFLOW_")||!n.eventType.startsWith(S)))return{eventType:n.eventType,index:n.index,parentBinding:n.parentBinding,parentId:n.parentId}},tt=t=>{if(typeof t!="object"||t===null)return t;const e={...t};return Reflect.deleteProperty(e,g),e},L=async(t,e,n)=>{const a=t.env[e.parentBinding];if(!a||typeof a.get!="function")return;const o=a.get.bind(a),c=B(n);await t.step.do(`${C}${String(e.index)}`,async()=>(await(await o(e.parentId)).sendEvent({payload:c,type:e.eventType}),e.eventType))},et=async(t,e,n)=>{try{await L(t,e,n)}catch(a){t.log?.error(`@lunora/workflow: failed to signal branch parent "${e.parentId}" (event "${e.eventType}")`,a)}};export{v as MAX_BRANCHES,G as branch,q as createParallel,Q as createSpawn,U as errorOutcome,Z as extractBranchMarker,M as okOutcome,L as signalBranchParent,et as signalBranchParentSafe,tt as stripBranchMarker};
@@ -0,0 +1 @@
1
+ import{isDuplicateInstanceError as E}from"./isDuplicateInstanceError-CecnqNAa.mjs";const s="__lunoraNonRetryable";class r extends Error{constructor(e,n="NonRetryableError"){super(e),this.name=n,this[s]=!0}}const a=t=>t instanceof Error&&t[s]===!0,c=(t,e)=>{const n=new e(t.message,t.name);return t.stack!==void 0&&(n.stack=t.stack),t.cause!==void 0&&n.cause===void 0&&(n.cause=t.cause),n},i=(t,e)=>{throw e!==void 0&&a(t)?c(t,e):t},u=(t,e,n)=>{const o=new r(t);return e!==void 0&&(o.cause=e),i(o,n)};export{r as NonRetryableError,i as convertNonRetryableError,E as isDuplicateInstanceError,a as isNonRetryableError,u as raiseNonRetryable,c as toNativeNonRetryableError};
@@ -0,0 +1 @@
1
+ import{LunoraError as _}from"@lunora/errors";const m=256,A=t=>t.length>m?`${t.slice(0,m)}… (truncated)`:t,$="https://api.cloudflare.com/client/v4/accounts",b={complete:!0,errored:!0,paused:!0,queued:!0,running:!0,terminated:!0,unknown:!0,waiting:!0,waitingForPause:!0},S=t=>typeof t=="string"&&Object.hasOwn(b,t)?t:"unknown",u=t=>typeof t=="string"&&t!==""?t:void 0,R=(t,a)=>typeof t=="string"?t:a,O=t=>typeof t=="boolean"?t:void 0,I=t=>Array.isArray(t)?t.length:typeof t=="number"?t:void 0,f=t=>({createdOn:u(t.created_on),endedOn:u(t.ended_on),id:R(t.id,""),startedOn:u(t.started_on),status:S(t.status)}),C=t=>({attempts:I(t.attempts),end:u(t.end),error:t.error,name:R(t.name,""),output:t.output,start:u(t.start),success:O(t.success),type:u(t.type)});class g extends _{constructor(a,i){super("WORKFLOWS_REST_ERROR",`Cloudflare Workflows REST API returned ${String(a)}: ${A(i)}`,{cause:i,name:"WorkflowsRestError",status:a})}}const E=t=>{const a=t.fetch??globalThis.fetch.bind(globalThis),i=`${$}/${t.accountId}/workflows`,p=async(o,r)=>{const n=await a(`${i}${o}`,{...r,headers:{Authorization:`Bearer ${t.apiToken}`,"Content-Type":"application/json"}}),e=await n.text();let s;try{s=JSON.parse(e)}catch{throw new g(n.status,e)}if(!n.ok||s.success===!1)throw new g(n.status,e);return s};return{getInstance:async({instanceId:o,workflowName:r})=>{const e=(await p(`/${encodeURIComponent(r)}/instances/${encodeURIComponent(o)}`)).result??{},s=Array.isArray(e.steps)?e.steps:[];return{...f(e),error:e.error,output:e.output,params:e.params,steps:s.map(d=>C(d))}},listInstances:async({page:o,perPage:r,status:n,workflowName:e})=>{const s=new URLSearchParams;n!==void 0&&s.set("status",n),o!==void 0&&s.set("page",String(o)),r!==void 0&&s.set("per_page",String(r));const d=s.toString()===""?"":`?${s.toString()}`,l=await p(`/${encodeURIComponent(e)}/instances${d}`),y=Array.isArray(l.result)?l.result:[],c=l.result_info??{};return{instances:y.map(h=>f(h)),page:typeof c.page=="number"?c.page:o??1,perPage:typeof c.per_page=="number"?c.per_page:r??y.length,totalCount:typeof c.total_count=="number"?c.total_count:void 0}},setInstanceStatus:async({action:o,instanceId:r,workflowName:n})=>{const s=(await p(`/${encodeURIComponent(n)}/instances/${encodeURIComponent(r)}/status`,{body:JSON.stringify({status:o}),method:"PATCH"})).result??{};return{status:S(s.status)}}}};export{g as WorkflowsRestError,E as createWorkflowsRestClient};
@@ -0,0 +1 @@
1
+ const e="__lunoraBranch",o=n=>typeof n=="object"&&n!==null&&Object.hasOwn(n,e),a=`may not contain the reserved workflow branch-marker key ("${e}")`;export{a as B,e as a,o as h};
@@ -0,0 +1 @@
1
+ import{c as p,v as o}from"./run-step-CFhxLFfp.mjs";import"./defineWorkflowEvent-suERPEEV.mjs";import"./NonRetryableError-BPu01mU8.mjs";export{p as createRunStep,o as validateStepArgs};
@@ -0,0 +1 @@
1
+ import{eventDefinitionProblem as m,RESERVED_EVENT_TYPE_PREFIX as i}from"./defineWorkflowEvent-suERPEEV.mjs";import{raiseNonRetryable as a}from"./NonRetryableError-BPu01mU8.mjs";const c=e=>async(r,t)=>{const n=m(r);if(n!==void 0)return a(`@lunora/workflow: ctx.waitForEvent ${n}`,void 0,e.nonRetryableErrorClass);if(t?.name?.startsWith(i)===!0)return a(`@lunora/workflow: ctx.waitForEvent step name "${t.name}" is reserved — the "${i}" prefix is used by the framework's own steps`,void 0,e.nonRetryableErrorClass);const s=await e.step.waitForEvent(t?.name??`event:${r.type}`,{timeout:t?.timeout,type:r.type});try{return r.payload.parse(s.payload)}catch(o){const l=o instanceof Error?o.message:String(o);return a(`@lunora/workflow: event "${r.type}" payload validation failed: ${l}`,o,e.nonRetryableErrorClass)}};export{c as createWaitForEvent};
@@ -0,0 +1 @@
1
+ import r from"./createWorkflows-LvAyUyKq.mjs";const i=(n,c)=>{const o={};for(const e of c){const t=n[e.binding];t&&typeof t.create=="function"&&typeof t.createBatch=="function"&&typeof t.get=="function"&&(o[e.exportName]=t)}return r({bindings:o})};export{i as createWorkflowContext};
@@ -0,0 +1 @@
1
+ import{t as E,c as A}from"./run-step-CFhxLFfp.mjs";import{LunoraError as h}from"@lunora/errors";import{workflowBindingName as m}from"./defineWorkflow-tKaIifbZ.mjs";import{raiseNonRetryable as R}from"./NonRetryableError-BPu01mU8.mjs";import{createSpawn as I,createParallel as k}from"./MAX_BRANCHES-j59ZUUSu.mjs";import{createWaitForEvent as T}from"./createWaitForEvent-D5Ohn2Pz.mjs";const C=r=>({debug:(n,...o)=>{console.debug(r,n,...o)},error:(n,...o)=>{console.error(r,n,...o)},info:(n,...o)=>{console.info(r,n,...o)},warn:(n,...o)=>{console.warn(r,n,...o)}}),N=r=>{let n="";for(let c=0;c<r.length;c+=32768)n+=String.fromCharCode(...r.subarray(c,c+32768));return btoa(n)},O=r=>{const n=atob(r),o=new Uint8Array(n.length);for(let c=0;c<n.length;c+=1)o[c]=n.codePointAt(c)??0;return o},S="$lunora.wire$",w=64,y=1024,b="__proto__",g={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},p={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},i=(r,n=0)=>{if(n>w)throw new RangeError(`wire-codec: value nesting exceeds the ${w}-level limit`);if(r===null||typeof r!="object")return r;if(Array.isArray(r)){if(r[0]===S)switch(r[1]){case"-inf":return-1/0;case"arr":return r[2].map(e=>i(e,n+1));case"bigint":{const e=r[2];if(typeof e!="string"||e.length>y||!/^-?\d+$/.test(e))throw new RangeError(`wire-codec: invalid or over-long bigint (max ${y} digits)`);return BigInt(e)}case"date":{const e=i(r[2],n+1);if(typeof e!="number")throw new TypeError("wire-codec: malformed date — epoch must be a number");return new Date(e)}case"map":{const e=r[2];return new Map(e.map(t=>{if(!Array.isArray(t)||t.length!==2)throw new TypeError("wire-codec: malformed map entry — expected a [key, value] pair");return[i(t[0],n+1),i(t[1],n+1)]}))}case"set":return new Set(r[2].map(e=>i(e,n+1)));case"url":{const e=r[2];if(typeof e!="string")throw new TypeError("wire-codec: malformed url — href must be a string");return new URL(e)}case"error":{const e=r[2],t=r[3];if(typeof e!="string"||typeof t!="string")throw new TypeError("wire-codec: malformed error — name and message must be strings");const u=(Object.hasOwn(p,e)?p[e]:void 0)??Error,s=new u(t);s.name!==e&&Object.defineProperty(s,"name",{configurable:!0,value:e,writable:!0});const a=i(r[4],n+1);if(a===null||typeof a!="object"||Array.isArray(a))throw new TypeError("wire-codec: malformed error — props must be an object");for(const l of Object.keys(a))l===b?Object.defineProperty(s,l,{configurable:!0,enumerable:!0,value:a[l],writable:!0}):s[l]=a[l];return r.length>5&&Object.defineProperty(s,"cause",{configurable:!0,value:i(r[5],n+1),writable:!0}),s}case"bytes":{const e=r[2];if(typeof e!="string")throw new TypeError("wire-codec: malformed bytes — payload must be a base64 string");const t=O(e);if(N(t)!==e)throw new TypeError("wire-codec: malformed bytes — payload must be canonical padded base64");const u=r[3]??"Uint8Array";if(u==="ArrayBuffer")return t.buffer.byteLength===t.byteLength?t.buffer:t.slice().buffer;const s=Object.hasOwn(g,u)?g[u]:void 0;return s?new s(t.slice().buffer):t}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return r.map(e=>i(e,n+1))}return r.map(f=>i(f,n+1))}const o=r,c={};for(const f of Object.keys(o)){const e=i(o[f],n+1);f===b?Object.defineProperty(c,f,{configurable:!0,enumerable:!0,value:e,writable:!0}):c[f]=e}return c},P=r=>{const n=C(`[workflow:${r.exportName}]`),o=E({env:r.env,fetchImpl:r.fetchImpl,label:"@lunora/workflow"}),c=a=>{const l=m(a),d=r.env[l];if(!d||typeof d.create!="function"||typeof d.get!="function")throw new h("INTERNAL",`@lunora/workflow: cannot spawn child workflow "${a}" — no Workflow binding "${l}" on env (is it declared in lunora/workflows.ts?)`);return d};let f=0;const e=a=>{if(a!==void 0)return a;const l=`${r.event.instanceId}-c${String(f)}`;return f+=1,l},t={env:r.env,instanceId:r.event.instanceId,log:n,nextChildId:e,parentBinding:m(r.exportName),resolveBinding:c,step:r.step},s=(()=>{try{return i(r.event.payload)}catch(a){return R(`@lunora/workflow: workflow "${r.exportName}" params could not be decoded — ${a instanceof Error?a.message:String(a)}`,a,r.nonRetryableErrorClass)}})();return{env:r.env,event:r.event,log:n,parallel:k(t),params:s,run:o,runStep:A({env:r.env,log:n,nonRetryableErrorClass:r.nonRetryableErrorClass,run:o,step:r.step}),spawn:I(t),step:r.step,waitForEvent:T({nonRetryableErrorClass:r.nonRetryableErrorClass,step:r.step})}};export{P as createWorkflowRunContext};
@@ -0,0 +1 @@
1
+ import{LunoraError as a}from"@lunora/errors";import{h as w,B as f}from"./branch-marker-CCpWfS5k.mjs";import{eventDefinitionProblem as l}from"./defineWorkflowEvent-suERPEEV.mjs";const c=r=>{if(w(r?.params))throw new a("BAD_REQUEST",`@lunora/workflow: params ${f}`)},i=r=>({create:async o=>(c(o),r.create(o)),createBatch:async o=>{for(const e of o)c(e);return r.createBatch(o)},get:async o=>r.get(o),sendEvent:async(o,e,t)=>{const n=l(e);if(n!==void 0)throw new a("BAD_REQUEST",`@lunora/workflow: sendEvent ${n}`);await(await r.get(o)).sendEvent({payload:e.payload.parse(t),type:e.type})}}),k=r=>{const o=r.bindings??{};return{get:e=>{const t=o[e];if(t===void 0){const n=Object.keys(o),s=n.length===0?"no workflows are declared":`known workflows: ${n.join(", ")}`;throw new a("INTERNAL",`@lunora/workflow: no workflow named "${e}" (${s})`)}return i(t)}}};export{k as default};
@@ -0,0 +1 @@
1
+ const n=(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 t=e.args;if(typeof t!="object"||t===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}},o=r=>typeof r=="object"&&r!==null&&r.isLunoraStep===!0;export{n as defineStep,o as isStepDefinition};
@@ -0,0 +1 @@
1
+ const e=o=>`${o.charAt(0).toUpperCase()}${o.slice(1)}Workflow`,r=o=>`WORKFLOW_${o.replaceAll(/(?<=[a-z0-9])(?=[A-Z])/g,"_").toUpperCase()}`,n=o=>o.replaceAll(/(?<=[a-z0-9])(?=[A-Z])/g,"-").toLowerCase(),t=o=>{if(typeof o.handler!="function")throw new TypeError("defineWorkflow: `handler` must be a function (the workflow body)");if(o.name!==void 0&&(typeof o.name!="string"||o.name.length===0))throw new TypeError("defineWorkflow: `name` must be a non-empty string when provided");return{...o,isLunoraWorkflow:!0}},l=o=>typeof o=="object"&&o!==null&&o.isLunoraWorkflow===!0;export{t as defineWorkflow,l as isWorkflowDefinition,r as workflowBindingName,e as workflowClassName,n as workflowDefaultName};
@@ -0,0 +1 @@
1
+ const o="lunora:",r=e=>{if(typeof e!="object"||e===null||e.isLunoraWorkflowEvent!==!0)return"expects a `defineWorkflowEvent` definition";const t=e;if(typeof t.type!="string"||t.type.length===0)return"event `type` must be a non-empty string";if(t.type.startsWith(o))return`event type "${t.type}" is reserved — the "${o}" prefix is used by the framework's own events`;if(typeof t.payload?.parse!="function")return"event `payload` must be a validator (e.g. `v.object({ ok: v.boolean() })`)"},i=(e,t)=>{if(typeof e!="string"||e.length===0)throw new TypeError("defineWorkflowEvent: `type` must be a non-empty string (the wire event type)");const n=r({isLunoraWorkflowEvent:!0,payload:t,type:e});if(n!==void 0)throw new TypeError(`defineWorkflowEvent: ${n}`);return{isLunoraWorkflowEvent:!0,payload:t,type:e}},f=e=>r(e)===void 0;export{o as RESERVED_EVENT_TYPE_PREFIX,i as defineWorkflowEvent,r as eventDefinitionProblem,f as isWorkflowEventDefinition};
@@ -0,0 +1 @@
1
+ const t=/already[\s_-]?exists/iu,e=s=>t.test(s instanceof Error?s.message:String(s));export{e as isDuplicateInstanceError};
@@ -0,0 +1 @@
1
+ import{LunoraError as w,isLunoraError as C}from"@lunora/errors";import{parseValidatorMap as B}from"@lunora/values";import{RESERVED_EVENT_TYPE_PREFIX as S}from"./defineWorkflowEvent-suERPEEV.mjs";import{raiseNonRetryable as $,convertNonRetryableError as M}from"./NonRetryableError-BPu01mU8.mjs";const J=(e,t,o)=>{if(t===void 0)return{dispose:()=>{},signal:e};const a=new AbortController,n=setTimeout(()=>{a.abort(o())},t);return{dispose:()=>{clearTimeout(n)},signal:a.signal}},O=e=>{let t="";for(let o=0;o<e.length;o+=32768)t+=String.fromCharCode(...e.subarray(o,o+32768));return btoa(t)},F=e=>{const t=atob(e),o=new Uint8Array(t.length);for(let a=0;a<t.length;a+=1)o[a]=t.codePointAt(a)??0;return o},p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",U=e=>{let t="",o=0;const a=e.length-2;for(;o<a;o+=3){const r=e[o]<<16|e[o+1]<<8|e[o+2];t+=p.charAt(r>>18&63)+p.charAt(r>>12&63)+p.charAt(r>>6&63)+p.charAt(r&63)}const n=e.length-o;if(n===1){const r=e[o]<<16;t+=p.charAt(r>>18&63)+p.charAt(r>>12&63)}else if(n===2){const r=e[o]<<16|e[o+1]<<8;t+=p.charAt(r>>18&63)+p.charAt(r>>12&63)+p.charAt(r>>6&63)}return t};new TextDecoder;const k=new TextEncoder,I="=",K=e=>{for(let t=0;t<e.length;t+=1)if(e.charCodeAt(t)>255)return!1;return!0},V=e=>U(k.encode(JSON.stringify(e))),W=e=>!e.startsWith(I)&&K(e)?e:`${I}${U(k.encode(e))}`,u="$lunora.wire$",R=64,j=1024,T="__proto__",_={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},L={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},H=e=>{if(e===null||typeof e!="object")return!1;const t=Object.getPrototypeOf(e);return t===null||t===Object.prototype},h=(e,t=0)=>{if(t>R)throw new RangeError(`wire-codec: value nesting exceeds the ${R}-level limit`);if(e===void 0)return[u,"undefined"];if(e===null)return null;const o=typeof e;if(o==="bigint")return[u,"bigint",e.toString()];if(o==="number"){const r=e;return Number.isNaN(r)?[u,"nan"]:r===1/0?[u,"inf"]:r===-1/0?[u,"-inf"]:r}if(o!=="object")return e;if(e instanceof Date)return[u,"date",h(e.getTime(),t+1)];if(e instanceof Error){const r=e,s={};for(const c of Object.keys(r)){if(r[c]===void 0)continue;const l=h(r[c],t+1);c===T?Object.defineProperty(s,c,{configurable:!0,enumerable:!0,value:l,writable:!0}):s[c]=l}const i=[u,"error",String(r.name),String(r.message),s];return r.cause!==void 0&&i.push(h(r.cause,t+1)),i}if(e instanceof URL)return[u,"url",e.href];if(e instanceof Map)return[u,"map",[...e.entries()].map(([r,s])=>[h(r,t+1),h(s,t+1)])];if(e instanceof Set)return[u,"set",[...e].map(r=>h(r,t+1))];if(e instanceof ArrayBuffer)return[u,"bytes",O(new Uint8Array(e)),"ArrayBuffer"];if(ArrayBuffer.isView(e)){const r=e,s=r.constructor.name,i=new Uint8Array(r.buffer,r.byteOffset,r.byteLength);return s==="Uint8Array"?[u,"bytes",O(i)]:[u,"bytes",O(i),s]}if(Array.isArray(e)){const r=e.map(s=>h(s,t+1));return r.length>0&&r[0]===u?[u,"arr",r]:r}if(!H(e)){const r=e.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${r} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const a=e,n={};for(const r of Object.keys(a)){const s=a[r];if(s===void 0)continue;const i=h(s,t+1);r===T?Object.defineProperty(n,r,{configurable:!0,enumerable:!0,value:i,writable:!0}):n[r]=i}return n},y=(e,t=0)=>{if(t>R)throw new RangeError(`wire-codec: value nesting exceeds the ${R}-level limit`);if(e===null||typeof e!="object")return e;if(Array.isArray(e)){if(e[0]===u)switch(e[1]){case"-inf":return-1/0;case"arr":return e[2].map(n=>y(n,t+1));case"bigint":{const n=e[2];if(typeof n!="string"||n.length>j||!/^-?\d+$/.test(n))throw new RangeError(`wire-codec: invalid or over-long bigint (max ${j} digits)`);return BigInt(n)}case"date":{const n=y(e[2],t+1);if(typeof n!="number")throw new TypeError("wire-codec: malformed date — epoch must be a number");return new Date(n)}case"map":{const n=e[2];return new Map(n.map(r=>{if(!Array.isArray(r)||r.length!==2)throw new TypeError("wire-codec: malformed map entry — expected a [key, value] pair");return[y(r[0],t+1),y(r[1],t+1)]}))}case"set":return new Set(e[2].map(n=>y(n,t+1)));case"url":{const n=e[2];if(typeof n!="string")throw new TypeError("wire-codec: malformed url — href must be a string");return new URL(n)}case"error":{const n=e[2],r=e[3];if(typeof n!="string"||typeof r!="string")throw new TypeError("wire-codec: malformed error — name and message must be strings");const s=(Object.hasOwn(L,n)?L[n]:void 0)??Error,i=new s(r);i.name!==n&&Object.defineProperty(i,"name",{configurable:!0,value:n,writable:!0});const c=y(e[4],t+1);if(c===null||typeof c!="object"||Array.isArray(c))throw new TypeError("wire-codec: malformed error — props must be an object");for(const l of Object.keys(c))l===T?Object.defineProperty(i,l,{configurable:!0,enumerable:!0,value:c[l],writable:!0}):i[l]=c[l];return e.length>5&&Object.defineProperty(i,"cause",{configurable:!0,value:y(e[5],t+1),writable:!0}),i}case"bytes":{const n=e[2];if(typeof n!="string")throw new TypeError("wire-codec: malformed bytes — payload must be a base64 string");const r=F(n);if(O(r)!==n)throw new TypeError("wire-codec: malformed bytes — payload must be canonical padded base64");const s=e[3]??"Uint8Array";if(s==="ArrayBuffer")return r.buffer.byteLength===r.byteLength?r.buffer:r.slice().buffer;const i=Object.hasOwn(_,s)?_[s]:void 0;return i?new i(r.slice().buffer):r}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return e.map(n=>y(n,t+1))}return e.map(n=>y(n,t+1))}const o=e,a={};for(const n of Object.keys(o)){const r=y(o[n],t+1);n===T?Object.defineProperty(a,n,{configurable:!0,enumerable:!0,value:r,writable:!0}):a[n]=r}return a},G=(e,t,o)=>{try{return h(o)}catch(a){throw new TypeError(`${e}: cannot encode args for '${t}' — ${a instanceof Error?a.message:String(a)}`,a instanceof Error?{cause:a}:void 0)}},z="/_lunora/scheduler/dispatch",X=3e4,Y=e=>{let t=e.length;for(;t>0&&e[t-1]==="/";)t-=1;return e.slice(0,t)},P=Symbol("lunoraDispatchFailure"),q=Symbol("lunoraDispatchMessageId"),x=(e,t)=>(t!==void 0&&Object.defineProperty(e,q,{value:t}),e),Q=(e,t)=>(Object.defineProperty(e,P,{value:!0}),x(e,t)),Z=(e,t,o,a)=>{try{const n=JSON.parse(o)?.error;if(typeof n=="object"&&n!==null&&typeof n.code=="string"){const{code:r,data:s,message:i}=n;return Q(new w(r,typeof i=="string"?i:void 0,{data:s,status:t}),a)}}catch{}return x(new w("INTERNAL",`${e}: function dispatch failed (${String(t)}): ${o}`,{status:t}),a)},ee=new Set([400,403,404,422]),te=new Set(["DISPATCH_UNAUTHENTICATED"]),re=e=>C(e)&&e[P]===!0&&ee.has(e.status)&&!te.has(e.code),ne=(e,t,o)=>new w("INTERNAL",`${e}: function dispatch to "${t}" timed out after ${String(o)}ms`,{status:503}),ue=e=>{const{label:t}=e,o=globalThis.fetch,a=e.fetchImpl??(typeof o=="function"?o.bind(globalThis):void 0);return async(n,r,s={})=>{if(typeof a!="function")throw new TypeError(`${t}: no fetch implementation available — pass fetchImpl or run on a platform with global fetch`);const i=()=>e.argsAlreadyEncoded===!0?r??{}:G(t,n.__lunoraRef,r??{}),c=e.env.LUNORA_ORIGIN_URL;if(typeof c!="string"||c.length===0)throw new w("INTERNAL",`${t}: \`LUNORA_ORIGIN_URL\` must be set on the Worker env so a handler can call back into Lunora functions`);const l=e.env.LUNORA_ADMIN_TOKEN;if(typeof l!="string"||l.length===0)throw new w("INTERNAL",`${t}: \`LUNORA_ADMIN_TOKEN\` must be set on the Worker env to authenticate function dispatch`);const g=`${Y(c)}${z}`,A={authorization:`Bearer ${l}`,"content-type":"application/json"};e.traceparent!==void 0&&e.traceparent.length>0&&(A.traceparent=e.traceparent),e.identity?.userId!==void 0&&(A["x-lunora-userid"]=W(e.identity.userId)),e.identity?.claims!==void 0&&(A["x-lunora-identity"]=V(e.identity.claims));const m=s.timeoutMs??X,f=J(void 0,m,()=>new DOMException(`dispatch timed out after ${String(m)}ms`,"TimeoutError")),N=b=>{throw b instanceof Error&&b.name==="TimeoutError"?ne(t,n.__lunoraRef,m):b};let d;try{try{d=await a(g,{body:JSON.stringify({args:i(),functionPath:n.__lunoraRef,id:s.dedupId,shardKey:s.shardKey}),headers:A,method:"POST",signal:f.signal})}catch(v){return N(v)}if(!d.ok){let v;try{v=await d.text()}catch(D){return N(D)}throw Z(t,d.status,v,s.messageId)}let b;try{b=await d.text()}catch(v){return N(v)}if(b.length===0)return;let E;try{E=JSON.parse(b)}catch{throw new w("INTERNAL",`${t}: function dispatch returned a non-JSON body (${String(d.status)}): ${b}`,{status:d.status})}if(E===null||typeof E!="object"||Array.isArray(E)||!("result"in E))throw new w("INTERNAL",`${t}: function dispatch returned a JSON body that is not a { result } envelope (${String(d.status)}): ${b}`,{status:d.status});return y(E.result)}finally{f.dispose()}}},oe=(e,t)=>B(e,t,"step args"),le=e=>async(t,o,a)=>{const n=a?.config??t.config,r=a?.name??t.name;if(r.startsWith(S))return $(`@lunora/workflow: ctx.runStep step name "${r}" is reserved — the "${S}" prefix is used by the framework's own steps`,void 0,e.nonRetryableErrorClass);const s=oe(t.args,o),i=async g=>{const A={attempt:g.attempt,config:g.config,env:e.env,log:e.log,run:e.run,step:g.step};let m;try{m=await t.handler(A,s)}catch(f){return re(f)?$(f.message,f,e.nonRetryableErrorClass):M(f,e.nonRetryableErrorClass)}if(!t.returns)return m;try{return t.returns.parse(m)}catch(f){const N=f instanceof Error?f.message:String(f);return $(`step "${t.name}" returns validation failed: ${N}`,f,e.nonRetryableErrorClass)}},c=t.rollback,l=c?{rollback:async g=>{await c({args:s,env:e.env,error:g.error,log:e.log,output:g.output,run:e.run})},rollbackConfig:t.rollbackConfig}:void 0;return n===void 0?e.step.do(r,i,l):e.step.do(r,n,i,l)};export{le as c,ue as t,oe as v};