@lunora/workflow 1.0.0-alpha.8 → 1.0.0-alpha.81

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 (36) hide show
  1. package/README.md +32 -7
  2. package/dist/do/index.d.mts +16 -16
  3. package/dist/do/index.d.ts +16 -16
  4. package/dist/do/index.mjs +1 -44
  5. package/dist/index.d.mts +203 -128
  6. package/dist/index.d.ts +203 -128
  7. package/dist/index.mjs +1 -9
  8. package/dist/packem_shared/MAX_BRANCHES-Bcw3dss9.mjs +1 -0
  9. package/dist/packem_shared/NonRetryableError-BPu01mU8.mjs +1 -0
  10. package/dist/packem_shared/WorkflowsRestError-CxLJskBZ.mjs +1 -0
  11. package/dist/packem_shared/branch-marker-CCpWfS5k.mjs +1 -0
  12. package/dist/packem_shared/createRunStep-KjGNW5rm.mjs +1 -0
  13. package/dist/packem_shared/createWaitForEvent-D5Ohn2Pz.mjs +1 -0
  14. package/dist/packem_shared/createWorkflowContext-DBUnSy0_.mjs +1 -0
  15. package/dist/packem_shared/createWorkflowRunContext-CRTTub21.mjs +1 -0
  16. package/dist/packem_shared/createWorkflows-BwR_J9mp.mjs +1 -0
  17. package/dist/packem_shared/defineStep-D1-9eOnA.mjs +1 -0
  18. package/dist/packem_shared/defineWorkflow-DynRL6LP.mjs +1 -0
  19. package/dist/packem_shared/defineWorkflowEvent-suERPEEV.mjs +1 -0
  20. package/dist/packem_shared/isDuplicateInstanceError-CecnqNAa.mjs +1 -0
  21. package/dist/packem_shared/run-step-DJ107J5H.mjs +1 -0
  22. package/dist/packem_shared/types.d-DUrUHeVi.d.mts +686 -0
  23. package/dist/packem_shared/types.d-DUrUHeVi.d.ts +686 -0
  24. package/dist/packem_shared/workflow-binding-Dm9hBzx_.mjs +1 -0
  25. package/package.json +4 -3
  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-zmjOxTR1.mjs +0 -117
  29. package/dist/packem_shared/createRunStep-8jOXxP2o.mjs +0 -54
  30. package/dist/packem_shared/createWorkflowContext-DfSgBq5I.mjs +0 -14
  31. package/dist/packem_shared/createWorkflowRunContext-C09mSbgk.mjs +0 -112
  32. package/dist/packem_shared/createWorkflows-BC1Mwjwe.mjs +0 -25
  33. package/dist/packem_shared/defineStep-DJQtLw7g.mjs +0 -28
  34. package/dist/packem_shared/defineWorkflow-DbUC-oCN.mjs +0 -15
  35. package/dist/packem_shared/types.d-C9W6rEWv.d.mts +0 -425
  36. package/dist/packem_shared/types.d-C9W6rEWv.d.ts +0 -425
package/README.md CHANGED
@@ -85,10 +85,33 @@ The handler context bundles:
85
85
  - `ctx.step` — the native Cloudflare durable-step API (`do` / `sleep` / `sleepUntil` / `waitForEvent`).
86
86
  - `ctx.run(ref, args, opts?)` — call a Lunora query / mutation / action; wrap in `ctx.step.do(...)` for durability.
87
87
  - `ctx.runStep(step, args, opts?)` — run a reusable, schema-validated `defineStep` as a durable step (see below).
88
+ - `ctx.waitForEvent(event, opts?)` — hibernate until a declared `defineWorkflowEvent` arrives; resolves with its validated payload (see below).
88
89
  - `ctx.event` / `ctx.params` — the triggering event and its payload.
89
90
  - `ctx.env` — the Worker bindings.
90
91
  - `ctx.log` — a workflow-prefixed logger surfaced in `wrangler tail` / Studio.
91
92
 
93
+ ### Declared events (`defineWorkflowEvent`)
94
+
95
+ `step.waitForEvent(name, { type })` and `instance.sendEvent({ type })` match on a bare string, and the payload crosses as `unknown` — so a typo hibernates the instance until its timeout with no error anywhere, and a changed payload shape resumes the workflow on garbage. Declare the event once and both ends import the same value:
96
+
97
+ ```ts
98
+ // lunora/events.ts
99
+ import { defineWorkflowEvent } from "@lunora/workflow";
100
+ import { v } from "@lunora/values";
101
+
102
+ export const orderApproved = defineWorkflowEvent("order-approved", v.object({ approvedBy: v.string() }));
103
+ ```
104
+
105
+ ```ts
106
+ // inside the workflow body — typed and parsed
107
+ const { approvedBy } = await ctx.waitForEvent(orderApproved, { name: "await approval", timeout: "7 days" });
108
+
109
+ // from a mutation/action — validated before the send
110
+ await ctx.workflows.get("orderPipeline").sendEvent(instanceId, orderApproved, { approvedBy });
111
+ ```
112
+
113
+ Pass a stable `{ name }` on any wait that can outlive a deploy: the step is otherwise labelled `event:<type>`, so renaming the event type also renames the durable step and an instance that already recorded the wait replays into a fresh one nothing will satisfy.
114
+
92
115
  ### Reusable steps (`defineStep`)
93
116
 
94
117
  A step authored inline with `ctx.step.do("name", () => …)` is fine for one-offs, but `defineStep` lets you define a step **once** — schema-validated and reusable across workflows. Args are validated (with `@lunora/values`) **before** the body runs, and the return value is validated **after** (when you declare `returns`), so a bad payload fails fast instead of corrupting later steps. Scaffold one with `vis generate lunora-step --name=chargeOrder` (appends to `lunora/steps.ts`), or write it by hand:
@@ -169,7 +192,9 @@ export const checkout = mutation.input({ orderId: v.string() }).mutation(async (
169
192
  });
170
193
  ```
171
194
 
172
- A handle exposes `create({ id?, params?, retention? })`, `createBatch([...])`, and `get(id)`. An instance handle exposes its lifecycle: `status()`, `pause()`, `resume()`, `terminate()`, and `sendEvent({ type, payload })` (to satisfy a `waitForEvent`).
195
+ A handle exposes `create({ id?, params?, retention? })`, `createBatch({ instances })` or `createBatch({ count, params?, retention? })` (returns `{ created, errors }`, one error per instance it did not create), `deleteBatch([...ids])` (up to 100), `get(id)`, and `sendEvent(instanceId, event, payload)` — the typed delivery of a declared event (see above). `create`/`get` return the native Cloudflare instance, which exposes its own lifecycle: `status()`, `pause()`, `resume()`, `restart()`, `terminate()`, `delete()`, `subscribe({ cursor?, filter? })` (streams the instance's event history, then new events), and the untyped `sendEvent({ type, payload })`.
196
+
197
+ `defineWorkflow` also takes deploy settings, which codegen writes into the workflow's `wrangler.jsonc` entry: `schedules` (cron expressions that each start an instance), `limits: { steps }`, and `defaultRetention: { successRetention, errorRetention }`. Scheduled workflows are refused on the Node and celld targets.
173
198
 
174
199
  ### Runtime requirements
175
200
 
@@ -181,7 +206,7 @@ A handle exposes `create({ id?, params?, retention? })`, `createBatch([...])`, a
181
206
  ### Manual wiring (without codegen)
182
207
 
183
208
  1. Author `lunora/workflows.ts` as above.
184
- 2. Re-export the generated class from your worker entry — wrangler requires every `workflows[].class_name` to be exported:
209
+ 2. Re-export the generated class from your worker entry — wrangler requires every workflow export to be a class the worker exports:
185
210
 
186
211
  ```ts
187
212
  import LunoraWorkflow from "@lunora/workflow/do";
@@ -194,17 +219,17 @@ A handle exposes `create({ id?, params?, retention? })`, `createBatch([...])`, a
194
219
  }
195
220
  ```
196
221
 
197
- 3. Add the binding to `wrangler.jsonc`:
222
+ 3. Declare it in `wrangler.jsonc`'s `exports` (Wrangler 4.142.0+):
198
223
 
199
224
  ```jsonc
200
225
  {
201
- "workflows": [{ "name": "order-pipeline", "binding": "WORKFLOW_ORDER_PIPELINE", "class_name": "OrderPipelineWorkflow" }],
226
+ "exports": { "OrderPipelineWorkflow": { "type": "workflow", "name": "order-pipeline" } },
202
227
  }
203
228
  ```
204
229
 
205
- 4. Build `ctx.workflows` from the binding: `createWorkflows({ bindings: { orderPipeline: env.WORKFLOW_ORDER_PIPELINE } })`.
230
+ 4. Build `ctx.workflows` from the invoking context's exports: `createWorkflows({ bindings: { orderPipeline: ctx.exports.OrderPipelineWorkflow } })`.
206
231
 
207
- The `workflowClassName` / `workflowBindingName` / `workflowDefaultName` helpers produce exactly these names so codegen and config never disagree.
232
+ The `workflowClassName` / `workflowDefaultName` helpers produce exactly these names so codegen and config never disagree.
208
233
 
209
234
  ### Observing instances (REST client)
210
235
 
@@ -223,7 +248,7 @@ const detail = await client.getInstance({ workflowName: "order-pipeline", instan
223
248
  await client.setInstanceStatus({ workflowName: "order-pipeline", instanceId: detail.id, action: "terminate" });
224
249
  ```
225
250
 
226
- > This README covers the basics. For the full API, options, and guides, see the **[documentation](https://lunora.sh/docs/addons/workflows)**.
251
+ > This README covers the basics. For the full API, options, and guides, see the **[documentation](https://lunora.sh/docs/packages/workflow)**.
227
252
 
228
253
  ## Related
229
254
 
@@ -1,22 +1,22 @@
1
1
  import { WorkflowEntrypoint, WorkflowEvent, WorkflowStep } from 'cloudflare:workers';
2
- import { W as WorkflowDefinition } from "../packem_shared/types.d-C9W6rEWv.mjs";
2
+ import { W as WorkflowDefinition } from "../packem_shared/types.d-DUrUHeVi.mjs";
3
3
  import '@lunora/values';
4
4
  /**
5
- * Base class for the generated `WorkflowEntrypoint` classes. Applies a
6
- * `defineWorkflow` definition onto Cloudflare's `WorkflowEntrypoint`: `run`
7
- * assembles the Lunora context (native `step`/`event` + the `ctx.run` function
8
- * dispatcher + a logger) and invokes the user's handler.
9
- *
10
- * Generated subclasses stay one line of behavior:
11
- *
12
- * ```ts
13
- * export class OrderPipelineWorkflow extends LunoraWorkflow {
14
- * constructor(ctx: ExecutionContext, env: Record&lt;string, unknown>) {
15
- * super(ctx, env, orderPipeline, "orderPipeline");
16
- * }
17
- * }
18
- * ```
19
- */
5
+ * Base class for the generated `WorkflowEntrypoint` classes. Applies a
6
+ * `defineWorkflow` definition onto Cloudflare's `WorkflowEntrypoint`: `run`
7
+ * assembles the Lunora context (native `step`/`event` + the `ctx.run` function
8
+ * dispatcher + a logger) and invokes the user's handler.
9
+ *
10
+ * Generated subclasses stay one line of behavior:
11
+ *
12
+ * ```ts
13
+ * export class OrderPipelineWorkflow extends LunoraWorkflow {
14
+ * constructor(ctx: ExecutionContext, env: Record<string, unknown>) {
15
+ * super(ctx, env, orderPipeline, "orderPipeline");
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
20
  declare class LunoraWorkflow<Params = Record<string, unknown>, Output = unknown> extends WorkflowEntrypoint<Record<string, unknown>, Params> {
21
21
  #private;
22
22
  constructor(context: ConstructorParameters<typeof WorkflowEntrypoint>[0], env: Record<string, unknown>, definition: WorkflowDefinition<Params, Output>, exportName?: string);
@@ -1,22 +1,22 @@
1
1
  import { WorkflowEntrypoint, WorkflowEvent, WorkflowStep } from 'cloudflare:workers';
2
- import { W as WorkflowDefinition } from "../packem_shared/types.d-C9W6rEWv.js";
2
+ import { W as WorkflowDefinition } from "../packem_shared/types.d-DUrUHeVi.js";
3
3
  import '@lunora/values';
4
4
  /**
5
- * Base class for the generated `WorkflowEntrypoint` classes. Applies a
6
- * `defineWorkflow` definition onto Cloudflare's `WorkflowEntrypoint`: `run`
7
- * assembles the Lunora context (native `step`/`event` + the `ctx.run` function
8
- * dispatcher + a logger) and invokes the user's handler.
9
- *
10
- * Generated subclasses stay one line of behavior:
11
- *
12
- * ```ts
13
- * export class OrderPipelineWorkflow extends LunoraWorkflow {
14
- * constructor(ctx: ExecutionContext, env: Record&lt;string, unknown>) {
15
- * super(ctx, env, orderPipeline, "orderPipeline");
16
- * }
17
- * }
18
- * ```
19
- */
5
+ * Base class for the generated `WorkflowEntrypoint` classes. Applies a
6
+ * `defineWorkflow` definition onto Cloudflare's `WorkflowEntrypoint`: `run`
7
+ * assembles the Lunora context (native `step`/`event` + the `ctx.run` function
8
+ * dispatcher + a logger) and invokes the user's handler.
9
+ *
10
+ * Generated subclasses stay one line of behavior:
11
+ *
12
+ * ```ts
13
+ * export class OrderPipelineWorkflow extends LunoraWorkflow {
14
+ * constructor(ctx: ExecutionContext, env: Record<string, unknown>) {
15
+ * super(ctx, env, orderPipeline, "orderPipeline");
16
+ * }
17
+ * }
18
+ * ```
19
+ */
20
20
  declare class LunoraWorkflow<Params = Record<string, unknown>, Output = unknown> extends WorkflowEntrypoint<Record<string, unknown>, Params> {
21
21
  #private;
22
22
  constructor(context: ConstructorParameters<typeof WorkflowEntrypoint>[0], env: Record<string, unknown>, definition: WorkflowDefinition<Params, Output>, exportName?: string);
package/dist/do/index.mjs CHANGED
@@ -1,44 +1 @@
1
- import { WorkflowEntrypoint } from 'cloudflare:workers';
2
- import { NonRetryableError } from 'cloudflare:workflows';
3
- import { convertNonRetryableError } from '../packem_shared/NonRetryableError-Dn2dTyBS.mjs';
4
- import { stripBranchMarker, signalBranchParent, extractBranchMarker, errorOutcome, okOutcome } from '../packem_shared/MAX_BRANCHES-D6cQabJ1.mjs';
5
- import { createWorkflowRunContext } from '../packem_shared/createWorkflowRunContext-C09mSbgk.mjs';
6
-
7
- class LunoraWorkflow extends WorkflowEntrypoint {
8
- /** The `lunora/workflows.ts` export name, for log correlation. */
9
- #lunoraName;
10
- /** The `defineWorkflow` result this entrypoint runs. */
11
- #definition;
12
- constructor(context, env, definition, exportName) {
13
- super(context, env);
14
- this.#definition = definition;
15
- this.#lunoraName = exportName ?? "workflow";
16
- }
17
- async run(event, step) {
18
- const nativeStep = step;
19
- const marker = extractBranchMarker(event.payload);
20
- const handlerEvent = marker ? { ...event, payload: stripBranchMarker(event.payload) } : event;
21
- const context = createWorkflowRunContext({
22
- env: this.env,
23
- event: handlerEvent,
24
- exportName: this.#lunoraName,
25
- nonRetryableErrorClass: NonRetryableError,
26
- step: nativeStep
27
- });
28
- let output;
29
- try {
30
- output = await this.#definition.handler(context);
31
- } catch (error) {
32
- if (marker) {
33
- await signalBranchParent({ env: this.env, step: nativeStep }, marker, errorOutcome(error));
34
- }
35
- return convertNonRetryableError(error, NonRetryableError);
36
- }
37
- if (marker) {
38
- await signalBranchParent({ env: this.env, step: nativeStep }, marker, okOutcome(output));
39
- }
40
- return output;
41
- }
42
- }
43
-
44
- export { LunoraWorkflow as default };
1
+ import{WorkflowEntrypoint as l}from"cloudflare:workers";import{NonRetryableError as i}from"cloudflare:workflows";import{convertNonRetryableError as h}from"../packem_shared/NonRetryableError-BPu01mU8.mjs";import{extractBranchMarker as m,stripBranchMarker as x,signalBranchParentSafe as p,errorOutcome as f,okOutcome as u}from"../packem_shared/MAX_BRANCHES-Bcw3dss9.mjs";import{createWorkflowRunContext as y}from"../packem_shared/createWorkflowRunContext-CRTTub21.mjs";class N extends l{#r;#t;constructor(t,e,o,r){super(t,e),this.#t=o,this.#r=r??"workflow"}async run(t,e){const o=e,r=m(t.payload),c=r?{...t,payload:x(t.payload)}:t,a=y({env:this.env,exports:this.ctx.exports,event:c,exportName:this.#r,nonRetryableErrorClass:i,step:o});let n;try{n=await this.#t.handler(a)}catch(s){return r&&await p({env:this.env,exports:this.ctx.exports,log:a.log,step:o},r,f(s)),h(s,i)}return r&&await p({env:this.env,exports:this.ctx.exports,log:a.log,step:o},r,u(n)),n}}export{N as default};
package/dist/index.d.mts CHANGED
@@ -1,117 +1,161 @@
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.mjs";
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.mjs";
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-DUrUHeVi.mjs";
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 WorkflowBatchDeleteResult, w as WorkflowBindingLike, x as WorkflowBranchOutputs, y as WorkflowCreateBatchError, z as WorkflowCreateBatchOptions, C as WorkflowCreateBatchResult, D as WorkflowCreateOptions, E as WorkflowDelayFunctionLike, G as WorkflowHandle, H as WorkflowHandler, J as WorkflowInstanceEventLike, K as WorkflowInstanceLike, M as WorkflowInstanceSubscribeOptionsLike, N as WorkflowInstanceSubscriptionLike, O as WorkflowParallelFunction, P as WorkflowRetention, Q as WorkflowRollbackContextLike, T as WorkflowRollbackHandlerLike, U as WorkflowSpawnFunction, V as WorkflowSpawnOptions, X as WorkflowStatusResult, Y as WorkflowStepConfigLike, Z as WorkflowStepContextLike, _ as WorkflowStepRollbackOptionsLike } from "./packem_shared/types.d-DUrUHeVi.mjs";
3
+ import { Validator } from '@lunora/values';
3
4
  import { LunoraError } from '@lunora/errors';
4
- import '@lunora/values';
5
5
  /** Wiring info for one declared workflow, emitted by codegen into the generated shard. */
6
6
  interface WorkflowBindingSpec {
7
- /** The Cloudflare `Workflow` binding name, e.g. `WORKFLOW_ORDER_PIPELINE`. */
8
- binding: string;
7
+ /** The workflow's export key — its generated class name, e.g. `OrderPipelineWorkflow`. */
8
+ className: string;
9
9
  /** The `lunora/workflows.ts` export name, e.g. `orderPipeline`. */
10
10
  exportName: string;
11
11
  }
12
12
  /**
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
- */
19
- declare const createWorkflowContext: (env: Record<string, unknown>, specs: ReadonlyArray<WorkflowBindingSpec>) => Workflows;
13
+ * Build the `ctx.workflows` handle for a request: resolve every spec's key off
14
+ * the invoking context's `exports` (Cloudflare) or `env` (other hosts) into the
15
+ * `exportName → Workflow binding` map and wrap it in {@link createWorkflows}. A
16
+ * spec whose workflow is absent from both is skipped
17
+ * here — the helpful "no workflow named …" error is raised lazily by
18
+ * `workflows.get(name)` when the missing workflow is actually used.
19
+ */
20
+ declare const createWorkflowContext: (env: Record<string, unknown>, specs: ReadonlyArray<WorkflowBindingSpec>, exports?: unknown) => Workflows;
20
21
  /**
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
- */
22
+ * Build the `ctx.workflows` handle from a map of `lunora/workflows.ts` export
23
+ * name → Cloudflare `Workflow` binding. `get(name)` resolves the typed handle;
24
+ * an unknown name throws with the list of declared workflows.
25
+ */
25
26
  declare const createWorkflows: (options: LunoraWorkflowsOptions) => Workflows;
26
27
  /**
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
- */
28
+ * Declare an external event, its wire type, and its payload shape.
29
+ *
30
+ * ```ts
31
+ * // lunora/events.ts
32
+ * import { defineWorkflowEvent } from "@lunora/workflow";
33
+ * import { v } from "@lunora/values";
34
+ *
35
+ * export const orderApproved = defineWorkflowEvent("order-approved", v.object({ approvedBy: v.string() }));
36
+ * ```
37
+ *
38
+ * Wait on it inside a workflow body — the payload is typed and validated:
39
+ *
40
+ * ```ts
41
+ * const { approvedBy } = await ctx.waitForEvent(orderApproved, { name: "await approval", timeout: "7 days" });
42
+ * ```
43
+ *
44
+ * …and send it from a mutation/action with the same definition:
45
+ *
46
+ * ```ts
47
+ * await ctx.workflows.get("orderPipeline").sendEvent(instanceId, orderApproved, { approvedBy });
48
+ * ```
49
+ */
50
+ declare const defineWorkflowEvent: <Payload>(type: string, payload: Validator<Payload>) => WorkflowEventDefinition<Payload>;
51
+ /**
52
+ * True when a value is usable as an event definition. The brand alone is not the
53
+ * test: the predicate hands the caller a `type` and a `payload.parse`, so it checks
54
+ * for both (and for the reserved namespace) rather than trusting a public boolean.
55
+ */
56
+ declare const isWorkflowEventDefinition: (value: unknown) => value is WorkflowEventDefinition;
57
+ /**
58
+ * Declare a reusable durable step. Same `args` map shape a Lunora `query` /
59
+ * `mutation` / `action` uses, so a step reads like a function:
60
+ *
61
+ * ```ts
62
+ * // lunora/steps.ts
63
+ * import { defineStep } from "@lunora/workflow";
64
+ * import { v } from "@lunora/values";
65
+ *
66
+ * export const fetchImage = defineStep("fetch image", {
67
+ * args: { imageKey: v.string() },
68
+ * returns: v.object({ data: v.bytes() }),
69
+ * handler: async (ctx, { imageKey }) => {
70
+ * const object = await (ctx.env.BUCKET as R2Bucket).get(imageKey);
71
+ * return { data: new Uint8Array(await object!.arrayBuffer()) };
72
+ * },
73
+ * rollback: async (ctx) => {
74
+ * await (ctx.env.BUCKET as R2Bucket).delete(`tmp/${ctx.args.imageKey}`);
75
+ * },
76
+ * });
77
+ * ```
78
+ *
79
+ * Then, inside a `defineWorkflow` handler:
80
+ *
81
+ * ```ts
82
+ * const { data } = await ctx.runStep(fetchImage, { imageKey: ctx.params.imageKey });
83
+ * ```
84
+ */
54
85
  declare const defineStep: <A extends StepArgsValidator, Result>(name: string, config: StepConfig<A, Result>) => StepDefinition<A, Result>;
55
86
  /** True when a value is a `defineStep` result (the runtime brand check). */
56
87
  declare const isStepDefinition: (value: unknown) => value is StepDefinition;
57
88
  /**
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
- */
89
+ * The generated `WorkflowEntrypoint` class name for a `lunora/workflows.ts`
90
+ * export: `orderPipeline` → `OrderPipelineWorkflow`. It is also the workflow's
91
+ * key everywhere at runtime: wrangler's `exports.<Class>` declares it, the
92
+ * invoking context's `ctx.exports.<Class>` reaches it (see
93
+ * `shared/workflow-binding.ts`), so codegen, config and the runtime MUST derive
94
+ * it identically — always via this helper.
95
+ */
63
96
  declare const workflowClassName: (exportName: string) => string;
64
97
  /**
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
- */
70
- declare const workflowBindingName: (exportName: string) => string;
71
- /**
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
- */
98
+ * The stable workflow name wrangler registers (`exports.<Class>.name`):
99
+ * `orderPipeline` → `order-pipeline`. Used as the deployed workflow's
100
+ * identifier when no explicit `name` override is given.
101
+ */
76
102
  declare const workflowDefaultName: (exportName: string) => string;
77
103
  /**
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&lt;{ 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
- */
104
+ * Declare a durable workflow deployed alongside the app. Pure validation +
105
+ * branding: codegen discovers the export, emits the `WorkflowEntrypoint`
106
+ * subclass (`_generated/workflows.ts`), and wires the typed `ctx.workflows`
107
+ * handle; the config layer reconciles the wrangler `exports.<Class>` entry from
108
+ * the same definition.
109
+ *
110
+ * ```ts
111
+ * // lunora/workflows.ts
112
+ * import { defineWorkflow } from "@lunora/workflow";
113
+ * import { api } from "./_generated/api";
114
+ *
115
+ * export const orderPipeline = defineWorkflow<{ orderId: string }>({
116
+ * handler: async (ctx) => {
117
+ * const order = await ctx.step.do("load", () => ctx.run(api.orders.get, { id: ctx.params.orderId }));
118
+ * await ctx.step.sleep("cool-off", "1 minute");
119
+ * // A raw `step.do` retries its callback in place, so a WRITE made through
120
+ * // it needs an explicit dedup id (or use `ctx.runStep`, which pins one).
121
+ * await ctx.step.do("charge", () =>
122
+ * ctx.run(api.payments.charge, { orderId: ctx.params.orderId }, { dedupId: `charge:${ctx.params.orderId}` }),
123
+ * );
124
+ * return order;
125
+ * },
126
+ * // Deploy settings, written into the wrangler `exports.<Class>` entry.
127
+ * schedules: ["0 * * * *"],
128
+ * limits: { steps: 25_000 },
129
+ * defaultRetention: { successRetention: "3 days", errorRetention: "30 days" },
130
+ * });
131
+ * ```
132
+ */
99
133
  declare const defineWorkflow: <Params = Record<string, unknown>, Output = unknown>(config: WorkflowConfig<Params, Output>) => WorkflowDefinition<Params, Output>;
100
134
  /** True when a value is a `defineWorkflow` result (the runtime brand check). */
101
135
  declare const isWorkflowDefinition: (value: unknown) => value is WorkflowDefinition;
102
136
  /**
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
- */
137
+ * Whether a `WorkflowBinding.create()` rejection is a duplicate-instance-id
138
+ * error — the idempotency signal that a *previous* attempt's create already
139
+ * applied — as opposed to a transient or config failure (Workflows service
140
+ * error, instance-creation quota, bad params).
141
+ *
142
+ * Every other failure MUST surface, so the caller retries or fails visibly
143
+ * rather than silently reporting success for work that never started.
144
+ */
145
+ declare const isDuplicateInstanceError: (error: unknown) => boolean;
146
+ /**
147
+ * Throw from a workflow step (or handler) to fail the instance immediately
148
+ * **without** retrying — the portable mirror of `cloudflare:workflows`'
149
+ * `NonRetryableError`. Importable from Node, so workflow code stays unit-testable.
150
+ *
151
+ * ```ts
152
+ * import { NonRetryableError } from "@lunora/workflow";
153
+ *
154
+ * if (order.status === "cancelled") {
155
+ * throw new NonRetryableError("order already cancelled — no point retrying");
156
+ * }
157
+ * ```
158
+ */
115
159
  declare class NonRetryableError extends Error {
116
160
  constructor(message: string, name?: string);
117
161
  }
@@ -120,34 +164,31 @@ declare const isNonRetryableError: (value: unknown) => value is NonRetryableErro
120
164
  /** Constructor shape of `cloudflare:workflows`' native `NonRetryableError`. */
121
165
  type NativeNonRetryableErrorConstructor = new (message: string, name?: string) => Error;
122
166
  /**
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
- */
167
+ * Rebuild a portable {@link NonRetryableError} as the native Cloudflare one,
168
+ * preserving its `name`, `message`, `cause`, and `stack`. Used at the `src/do`
169
+ * boundary where the native constructor is available; everywhere else the
170
+ * portable error is thrown unchanged (and still honored by name).
171
+ */
128
172
  declare const toNativeNonRetryableError: (error: NonRetryableError, NativeNonRetryableError: NativeNonRetryableErrorConstructor) => Error;
129
173
  /**
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
- */
174
+ * If `error` is a portable {@link NonRetryableError} and a native constructor is
175
+ * available, rethrow it as the native error; otherwise rethrow `error` as-is.
176
+ * Always throws — the `never` return lets callers `return convertNonRetryableError(...)`.
177
+ */
134
178
  declare const convertNonRetryableError: (error: unknown, NativeNonRetryableError: NativeNonRetryableErrorConstructor | undefined) => never;
135
179
  /** Hard cap on branches per `ctx.parallel` call — auto-scale, never silently spawn unbounded DOs. */
136
180
  declare const MAX_BRANCHES = 100;
137
- /** The params key the parent injects so a child knows to signal completion back. Stripped before the user handler sees `ctx.params`. */
138
-
139
181
  /**
140
- * Build a single fan-out branch: a declared child workflow referenced by its
141
- * `lunora/workflows.ts` export name, plus the params it is created with. Pass the
142
- * output type as the generic argument so `ctx.parallel(...)` infers the result
143
- * tuple — e.g. `branch("imageTag", { key })` typed as `branch` of `{ tags }`.
144
- */
182
+ * Build a single fan-out branch: a declared child workflow referenced by its
183
+ * `lunora/workflows.ts` export name, plus the params it is created with. Pass the
184
+ * output type as the generic argument so `ctx.parallel(...)` infers the result
185
+ * tuple — e.g. `branch("imageTag", { key })` typed as `branch` of `{ tags }`.
186
+ */
145
187
  declare const branch: <Output = unknown>(workflow: string, params?: Record<string, unknown>, options?: {
146
188
  compensateWith?: string;
147
189
  id?: string;
148
190
  timeout?: number | string;
149
191
  }) => WorkflowBranch<Output>;
150
- /** Build the success outcome a completed branch reports to its parent. */
151
192
  /** The lifecycle mutations the REST API exposes via `PATCH .../instances/{id}`. */
152
193
  type WorkflowInstanceAction = "pause" | "resume" | "terminate";
153
194
  /** Configuration for a {@link WorkflowsRestClient}. */
@@ -194,7 +235,14 @@ interface WorkflowInstancePage {
194
235
  perPage: number;
195
236
  totalCount?: number;
196
237
  }
197
- /** Thrown when the REST API responds non-2xx or `success: false`; carries the status plus body for the caller to surface. */
238
+ /**
239
+ * Thrown when the REST API responds non-2xx or `success: false`; carries the
240
+ * status plus a capped body preview, with the full body on `cause`.
241
+ *
242
+ * The preview is capped because the Cloudflare API's auth/authorization error
243
+ * text (and its HTML gateway pages) would otherwise ride out verbatim —
244
+ * `WORKFLOWS_REST_ERROR` is non-internal, so the message reaches the browser.
245
+ */
198
246
  declare class WorkflowsRestError extends LunoraError {
199
247
  constructor(status: number, body: string);
200
248
  }
@@ -219,16 +267,18 @@ interface WorkflowsRestClient {
219
267
  }>;
220
268
  }
221
269
  /**
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
- */
270
+ * Build a {@link WorkflowsRestClient}. Each call hits the account-scoped REST
271
+ * endpoint with the bearer token, unwraps Cloudflare's
272
+ * `{ success, errors, result, result_info }` envelope, and normalizes the
273
+ * snake_case payload into the camelCase shapes the studio renders.
274
+ */
227
275
  declare const createWorkflowsRestClient: (config: WorkflowsRestConfig) => WorkflowsRestClient;
228
276
  interface RunContextOptions<Params> {
229
277
  env: Record<string, unknown>;
230
278
  event: WorkflowEventLike<Params>;
231
279
  exportName: string;
280
+ /** The invoking context's `ctx.exports`, where Cloudflare exposes the workflows declared in wrangler `exports`. */
281
+ exports?: unknown;
232
282
  fetchImpl?: typeof fetch;
233
283
  /** Native `cloudflare:workflows` `NonRetryableError` — injected by `src/do`; absent in Node tests. */
234
284
  nonRetryableErrorClass?: NativeNonRetryableErrorConstructor;
@@ -237,31 +287,56 @@ interface RunContextOptions<Params> {
237
287
  /** Assemble the {@link WorkflowRunContext} passed to a `defineWorkflow` handler. */
238
288
  declare const createWorkflowRunContext: <Params = Record<string, unknown>>(options: RunContextOptions<Params>) => WorkflowRunContext<Params>;
239
289
  /**
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
- */
290
+ * Validate a step's args through its validator map, prefixing any
291
+ * `ValidationError` with `step args.<key>` so the failure points at the
292
+ * offending field. Delegates to `@lunora/values`' shared {@link parseValidatorMap}
293
+ * — the same parser the procedure builder and HTTP routes use — so the
294
+ * optional-skip and error-prefix semantics stay in lockstep across the framework.
295
+ */
246
296
  declare const validateStepArgs: (validators: StepArgsValidator, source: Record<string, unknown>) => Record<string, unknown>;
247
297
  /** Dependencies needed to run a step: the native step API plus the workflow's env / runner / logger. */
248
298
  interface RunStepDeps {
299
+ /** The instance's `dedupNamespace(exportName, instanceId)` — the prefix of the replay-dedup ids the steps' dispatches carry (see `dedup-id.ts`). */
300
+ dedupNamespace: string;
249
301
  /** The Worker environment bindings, surfaced on the step context. */
250
302
  env: Record<string, unknown>;
251
303
  /** Structured logger surfaced on the step context. */
252
304
  log: WorkflowLogger;
253
305
  /** Native `cloudflare:workflows` `NonRetryableError` constructor — injected by `src/do`; absent in Node tests. */
254
306
  nonRetryableErrorClass?: NativeNonRetryableErrorConstructor;
255
- /** The Lunora function runner, surfaced on the step context. */
307
+ /**
308
+ * The bare Lunora function runner. Each step attempt (and each rollback)
309
+ * surfaces it wrapped in `waitOutDeclines`, bounded by that attempt's own
310
+ * timeout.
311
+ */
256
312
  run: WorkflowRunFunction;
257
313
  /** The native Cloudflare durable-step API. */
258
314
  step: WorkflowStepLike;
259
315
  }
260
316
  /**
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
- */
317
+ * Build the `ctx.runStep` function bound to one workflow invocation. Each call
318
+ * runs the step through `step.do(...)`: validate args → run body → validate
319
+ * result (when `returns` is declared), with any portable `NonRetryableError`
320
+ * converted to the native one and any declared rollback forwarded to Cloudflare.
321
+ */
266
322
  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 };
323
+ /** Dependencies one workflow invocation's `ctx.waitForEvent` closes over. */
324
+ interface WaitForEventDeps {
325
+ /** Native `cloudflare:workflows` `NonRetryableError` constructor — injected by `src/do`; absent in Node tests. */
326
+ nonRetryableErrorClass?: NativeNonRetryableErrorConstructor;
327
+ /** The native Cloudflare durable-step API. */
328
+ step: WorkflowStepLike;
329
+ }
330
+ /**
331
+ * Build `ctx.waitForEvent` for one workflow invocation.
332
+ *
333
+ * Every failure here is **non-retryable**. A malformed definition or a reserved
334
+ * step name is a deterministic programmer error, and a payload that fails the
335
+ * validator has already consumed the event — replaying the wait cannot produce a
336
+ * different value, it can only hibernate the instance until its timeout. Each is
337
+ * raised through the shared {@link raiseNonRetryable}, the same classification path
338
+ * `ctx.runStep` uses, so the native error reaches Cloudflare and the instance fails
339
+ * fast.
340
+ */
341
+ declare const createWaitForEvent: (deps: WaitForEventDeps) => WorkflowWaitForEventFunction;
342
+ 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, workflowClassName, workflowDefaultName };