@lunora/workflow 1.0.0-alpha.29 → 1.0.0-alpha.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -1
- package/dist/do/index.d.mts +1 -1
- package/dist/do/index.d.ts +1 -1
- package/dist/do/index.mjs +1 -1
- package/dist/index.d.mts +53 -4
- package/dist/index.d.ts +53 -4
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/MAX_BRANCHES-C05O5LY9.mjs +1 -0
- package/dist/packem_shared/createWaitForEvent-DjWsI895.mjs +1 -0
- package/dist/packem_shared/{createWorkflowContext-DG-jCSoa.mjs → createWorkflowContext-zQR_9N-v.mjs} +1 -1
- package/dist/packem_shared/createWorkflowRunContext-CNvJHMci.mjs +1 -0
- package/dist/packem_shared/createWorkflows-LvAyUyKq.mjs +1 -0
- package/dist/packem_shared/defineWorkflowEvent-suERPEEV.mjs +1 -0
- package/dist/packem_shared/{types.d-CZ1pcdmw.d.mts → types.d-BmtJnXue.d.mts} +65 -2
- package/dist/packem_shared/{types.d-CZ1pcdmw.d.ts → types.d-BmtJnXue.d.ts} +65 -2
- package/package.json +1 -1
- package/dist/packem_shared/MAX_BRANCHES-ZS8UAw_t.mjs +0 -1
- package/dist/packem_shared/createWorkflowRunContext-D1vdFtWC.mjs +0 -1
- package/dist/packem_shared/createWorkflows-uoAhGKCA.mjs +0 -1
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,7 @@ 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([...])`,
|
|
195
|
+
A handle exposes `create({ id?, params?, retention? })`, `createBatch([...])`, `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()`, and the untyped `sendEvent({ type, payload })`.
|
|
173
196
|
|
|
174
197
|
### Runtime requirements
|
|
175
198
|
|
package/dist/do/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WorkflowEntrypoint, WorkflowEvent, WorkflowStep } from 'cloudflare:workers';
|
|
2
|
-
import { W as WorkflowDefinition } from "../packem_shared/types.d-
|
|
2
|
+
import { W as WorkflowDefinition } from "../packem_shared/types.d-BmtJnXue.mjs";
|
|
3
3
|
import '@lunora/values';
|
|
4
4
|
/**
|
|
5
5
|
* Base class for the generated `WorkflowEntrypoint` classes. Applies a
|
package/dist/do/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WorkflowEntrypoint, WorkflowEvent, WorkflowStep } from 'cloudflare:workers';
|
|
2
|
-
import { W as WorkflowDefinition } from "../packem_shared/types.d-
|
|
2
|
+
import { W as WorkflowDefinition } from "../packem_shared/types.d-BmtJnXue.js";
|
|
3
3
|
import '@lunora/values';
|
|
4
4
|
/**
|
|
5
5
|
* Base class for the generated `WorkflowEntrypoint` classes. Applies a
|
package/dist/do/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{WorkflowEntrypoint as p}from"cloudflare:workers";import{NonRetryableError as i}from"cloudflare:workflows";import{convertNonRetryableError as m}from"../packem_shared/NonRetryableError-9ZqDUzwT.mjs";import{extractBranchMarker as f,stripBranchMarker as h,signalBranchParentSafe as l,errorOutcome as u,okOutcome as y}from"../packem_shared/MAX_BRANCHES-
|
|
1
|
+
import{WorkflowEntrypoint as p}from"cloudflare:workers";import{NonRetryableError as i}from"cloudflare:workflows";import{convertNonRetryableError as m}from"../packem_shared/NonRetryableError-9ZqDUzwT.mjs";import{extractBranchMarker as f,stripBranchMarker as h,signalBranchParentSafe as l,errorOutcome as u,okOutcome as y}from"../packem_shared/MAX_BRANCHES-C05O5LY9.mjs";import{createWorkflowRunContext as d}from"../packem_shared/createWorkflowRunContext-CNvJHMci.mjs";class N extends p{#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=f(t.payload),c=r?{...t,payload:h(t.payload)}:t,a=d({env:this.env,event:c,exportName:this.#r,nonRetryableErrorClass:i,step:o});let n;try{n=await this.#t.handler(a)}catch(s){return r&&await l({env:this.env,log:a.log,step:o},r,u(s)),m(s,i)}return r&&await l({env:this.env,log:a.log,step:o},r,y(n)),n}}export{N as default};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as Workflows, L as LunoraWorkflowsOptions, S as StepArgsValidator,
|
|
2
|
-
export type { A as ArgsOf, B as BranchCompensationParams, F as FunctionReference, I as InferStepArgs, R as RunFunctionOptions,
|
|
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-BmtJnXue.mjs";
|
|
2
|
+
export type { A as ArgsOf, B as BranchCompensationParams, F as FunctionReference, I as InferStepArgs, R as RunFunctionOptions, o as RunStepOptions, p as StepHandler, q as StepRollbackContext, r as StepRollbackHandler, s as StepRunContext, t as WaitForEventOptions, u as WorkflowBindingLike, v as WorkflowBranchOutputs, w as WorkflowCreateOptions, x as WorkflowHandle, y as WorkflowHandler, z as WorkflowInstanceLike, C as WorkflowParallelFunction, D as WorkflowRollbackContextLike, E as WorkflowRollbackHandlerLike, G as WorkflowSpawnFunction, H as WorkflowSpawnOptions, J as WorkflowStatusResult, K as WorkflowStepConfigLike, M as WorkflowStepContextLike, N as WorkflowStepRollbackOptionsLike } from "./packem_shared/types.d-BmtJnXue.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
7
|
/** The Cloudflare `Workflow` binding name, e.g. `WORKFLOW_ORDER_PIPELINE`. */
|
|
@@ -23,6 +23,36 @@ declare const createWorkflowContext: (env: Record<string, unknown>, specs: Reado
|
|
|
23
23
|
* an unknown name throws with the list of declared workflows.
|
|
24
24
|
*/
|
|
25
25
|
declare const createWorkflows: (options: LunoraWorkflowsOptions) => Workflows;
|
|
26
|
+
/**
|
|
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;
|
|
26
56
|
/**
|
|
27
57
|
* Declare a reusable durable step. Same `args` map shape a Lunora `query` /
|
|
28
58
|
* `mutation` / `action` uses, so a step reads like a function:
|
|
@@ -261,4 +291,23 @@ interface RunStepDeps {
|
|
|
261
291
|
* converted to the native one and any declared rollback forwarded to Cloudflare.
|
|
262
292
|
*/
|
|
263
293
|
declare const createRunStep: (deps: RunStepDeps) => WorkflowRunStepFunction;
|
|
264
|
-
|
|
294
|
+
/** Dependencies one workflow invocation's `ctx.waitForEvent` closes over. */
|
|
295
|
+
interface WaitForEventDeps {
|
|
296
|
+
/** Native `cloudflare:workflows` `NonRetryableError` constructor — injected by `src/do`; absent in Node tests. */
|
|
297
|
+
nonRetryableErrorClass?: NativeNonRetryableErrorConstructor;
|
|
298
|
+
/** The native Cloudflare durable-step API. */
|
|
299
|
+
step: WorkflowStepLike;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Build `ctx.waitForEvent` for one workflow invocation.
|
|
303
|
+
*
|
|
304
|
+
* Every failure here is **non-retryable**. A malformed definition or a reserved
|
|
305
|
+
* step name is a deterministic programmer error, and a payload that fails the
|
|
306
|
+
* validator has already consumed the event — replaying the wait cannot produce a
|
|
307
|
+
* different value, it can only hibernate the instance until its timeout. Each is
|
|
308
|
+
* raised through the shared {@link raiseNonRetryable}, the same classification path
|
|
309
|
+
* `ctx.runStep` uses, so the native error reaches Cloudflare and the instance fails
|
|
310
|
+
* fast.
|
|
311
|
+
*/
|
|
312
|
+
declare const createWaitForEvent: (deps: WaitForEventDeps) => WorkflowWaitForEventFunction;
|
|
313
|
+
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, isNonRetryableError, isStepDefinition, isWorkflowDefinition, isWorkflowEventDefinition, toNativeNonRetryableError, validateStepArgs, workflowBindingName, workflowClassName, workflowDefaultName };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as Workflows, L as LunoraWorkflowsOptions, S as StepArgsValidator,
|
|
2
|
-
export type { A as ArgsOf, B as BranchCompensationParams, F as FunctionReference, I as InferStepArgs, R as RunFunctionOptions,
|
|
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-BmtJnXue.js";
|
|
2
|
+
export type { A as ArgsOf, B as BranchCompensationParams, F as FunctionReference, I as InferStepArgs, R as RunFunctionOptions, o as RunStepOptions, p as StepHandler, q as StepRollbackContext, r as StepRollbackHandler, s as StepRunContext, t as WaitForEventOptions, u as WorkflowBindingLike, v as WorkflowBranchOutputs, w as WorkflowCreateOptions, x as WorkflowHandle, y as WorkflowHandler, z as WorkflowInstanceLike, C as WorkflowParallelFunction, D as WorkflowRollbackContextLike, E as WorkflowRollbackHandlerLike, G as WorkflowSpawnFunction, H as WorkflowSpawnOptions, J as WorkflowStatusResult, K as WorkflowStepConfigLike, M as WorkflowStepContextLike, N as WorkflowStepRollbackOptionsLike } from "./packem_shared/types.d-BmtJnXue.js";
|
|
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
7
|
/** The Cloudflare `Workflow` binding name, e.g. `WORKFLOW_ORDER_PIPELINE`. */
|
|
@@ -23,6 +23,36 @@ declare const createWorkflowContext: (env: Record<string, unknown>, specs: Reado
|
|
|
23
23
|
* an unknown name throws with the list of declared workflows.
|
|
24
24
|
*/
|
|
25
25
|
declare const createWorkflows: (options: LunoraWorkflowsOptions) => Workflows;
|
|
26
|
+
/**
|
|
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;
|
|
26
56
|
/**
|
|
27
57
|
* Declare a reusable durable step. Same `args` map shape a Lunora `query` /
|
|
28
58
|
* `mutation` / `action` uses, so a step reads like a function:
|
|
@@ -261,4 +291,23 @@ interface RunStepDeps {
|
|
|
261
291
|
* converted to the native one and any declared rollback forwarded to Cloudflare.
|
|
262
292
|
*/
|
|
263
293
|
declare const createRunStep: (deps: RunStepDeps) => WorkflowRunStepFunction;
|
|
264
|
-
|
|
294
|
+
/** Dependencies one workflow invocation's `ctx.waitForEvent` closes over. */
|
|
295
|
+
interface WaitForEventDeps {
|
|
296
|
+
/** Native `cloudflare:workflows` `NonRetryableError` constructor — injected by `src/do`; absent in Node tests. */
|
|
297
|
+
nonRetryableErrorClass?: NativeNonRetryableErrorConstructor;
|
|
298
|
+
/** The native Cloudflare durable-step API. */
|
|
299
|
+
step: WorkflowStepLike;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Build `ctx.waitForEvent` for one workflow invocation.
|
|
303
|
+
*
|
|
304
|
+
* Every failure here is **non-retryable**. A malformed definition or a reserved
|
|
305
|
+
* step name is a deterministic programmer error, and a payload that fails the
|
|
306
|
+
* validator has already consumed the event — replaying the wait cannot produce a
|
|
307
|
+
* different value, it can only hibernate the instance until its timeout. Each is
|
|
308
|
+
* raised through the shared {@link raiseNonRetryable}, the same classification path
|
|
309
|
+
* `ctx.runStep` uses, so the native error reaches Cloudflare and the instance fails
|
|
310
|
+
* fast.
|
|
311
|
+
*/
|
|
312
|
+
declare const createWaitForEvent: (deps: WaitForEventDeps) => WorkflowWaitForEventFunction;
|
|
313
|
+
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, isNonRetryableError, isStepDefinition, isWorkflowDefinition, isWorkflowEventDefinition, toNativeNonRetryableError, validateStepArgs, workflowBindingName, workflowClassName, workflowDefaultName };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createWorkflowContext as e}from"./packem_shared/createWorkflowContext-
|
|
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 m,isWorkflowDefinition as x,workflowBindingName as k,workflowClassName as W,workflowDefaultName as c}from"./packem_shared/defineWorkflow-tKaIifbZ.mjs";import{NonRetryableError as N,convertNonRetryableError as R,isNonRetryableError as v,toNativeNonRetryableError as d}from"./packem_shared/NonRetryableError-9ZqDUzwT.mjs";import{MAX_BRANCHES as C,branch as S}from"./packem_shared/MAX_BRANCHES-C05O5LY9.mjs";import{WorkflowsRestError as y,createWorkflowsRestClient as D}from"./packem_shared/WorkflowsRestError-HhBsn3PQ.mjs";import{createWorkflowRunContext as g}from"./packem_shared/createWorkflowRunContext-CNvJHMci.mjs";import{c as h,v as F}from"./packem_shared/run-step-7RiMJYR7.mjs";import{createWaitForEvent as M}from"./packem_shared/createWaitForEvent-DjWsI895.mjs";export{C as MAX_BRANCHES,N as NonRetryableError,y as WorkflowsRestError,S as branch,R as convertNonRetryableError,h as createRunStep,M as createWaitForEvent,e as createWorkflowContext,g as createWorkflowRunContext,f as createWorkflows,D as createWorkflowsRestClient,p as defineStep,m as defineWorkflow,a as defineWorkflowEvent,v as isNonRetryableError,s as isStepDefinition,x as isWorkflowDefinition,i as isWorkflowEventDefinition,d as toNativeNonRetryableError,F as validateStepArgs,k as workflowBindingName,W as workflowClassName,c as workflowDefaultName};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as E}from"@lunora/errors";import{h as y,B as I,a as p}from"./branch-marker-CCpWfS5k.mjs";import{RESERVED_EVENT_TYPE_PREFIX as v}from"./defineWorkflowEvent-suERPEEV.mjs";import{NonRetryableError as d}from"./NonRetryableError-9ZqDUzwT.mjs";const h=100,g="lunora:spawn:",$="lunora:await:",x="lunora:signal:",B="lunora:compensate:",m=`${v}branch:`,b=(t,r,n)=>({compensateWith:n?.compensateWith,id:n?.id,params:r,timeout:n?.timeout,workflow:t}),w=t=>t instanceof Error?{message:t.message,name:t.name}:{message:String(t),name:"Error"},A=t=>({status:"ok",value:t}),N=t=>({error:w(t),status:"error"}),f=async(t,r,n)=>{for(let a=r.length-1;a>=0;a-=1){const o=r[a],c=o?.plan.item.compensateWith;if(!(o===void 0||c===void 0))try{const s=t.resolveBinding(c);await t.step.do(`${B}${o.plan.childId}`,async()=>{const e=`${o.plan.childId}:compensate`,i={branch:o.plan.item.workflow,error:n,index:o.plan.index,output:o.output};return await s.create({id:e,params:i}),e})}catch(s){t.log?.error(`ctx.parallel: group-saga compensation "${c}" for branch "${o.plan.item.workflow}" (#${String(o.plan.index)}) failed`,s)}}},k=t=>async n=>{if(n.length===0)return[];if(n.length>h)throw new d(`ctx.parallel: ${String(n.length)} branches exceeds the cap of ${String(h)} — split the fan-out or raise the work into fewer child workflows`);const a=n.map((e,i)=>{const l=t.nextChildId(e.id);return{childId:l,eventType:`${m}${l}`,index:i,item:e}}),o=new Set;for(const e of a){if(o.has(e.childId))throw new d(`ctx.parallel: duplicate branch id "${e.childId}" — each branch in a group must resolve to a unique child instance id (check explicit \`id\` options)`);o.add(e.childId)}await Promise.all(a.map(e=>t.step.do(`${g}${e.childId}`,async()=>{const i=t.resolveBinding(e.item.workflow),l={eventType:e.eventType,index:e.index,parentBinding:t.parentBinding,parentId:t.instanceId};return await i.create({id:e.childId,params:{...e.item.params,[p]:l}}),e.childId})));const c=[],s=[];for(const e of a){let i;try{i=(await t.step.waitForEvent(`${$}${e.childId}`,{timeout:e.item.timeout,type:e.eventType})).payload}catch(l){const u=w(l);throw await f(t,s,u),new d(`ctx.parallel: branch "${e.item.workflow}" (#${String(e.index)}) join failed: ${u.message}`)}if(i.status==="error")throw await f(t,s,i.error),new d(`ctx.parallel: branch "${e.item.workflow}" (#${String(e.index)}) failed: ${i.error.message}`);s.push({output:i.value,plan:e}),c.push(i.value)}return c},W=t=>async(r,n,a)=>{if(y(n))throw new E("BAD_REQUEST",`@lunora/workflow: params ${I}`);const o=t.nextChildId(a?.id);return await t.step.do(`${g}${o}`,async()=>(await t.resolveBinding(r).create({id:o,params:n}),o)),t.resolveBinding(r).get(o)},C=t=>{if(typeof t!="object"||t===null)return;const r=t[p];if(typeof r!="object"||r===null)return;const n=r;if(!(typeof n.eventType!="string"||typeof n.parentBinding!="string"||typeof n.parentId!="string"||typeof n.index!="number")&&!(!n.parentBinding.startsWith("WORKFLOW_")||!n.eventType.startsWith(m)))return{eventType:n.eventType,index:n.index,parentBinding:n.parentBinding,parentId:n.parentId}},F=t=>{if(typeof t!="object"||t===null)return t;const r={...t};return Reflect.deleteProperty(r,p),r},T=async(t,r,n)=>{const a=t.env[r.parentBinding];if(!a||typeof a.get!="function")return;const o=a.get.bind(a);await t.step.do(`${x}${String(r.index)}`,async()=>(await(await o(r.parentId)).sendEvent({payload:n,type:r.eventType}),r.eventType))},M=async(t,r,n)=>{try{await T(t,r,n)}catch(a){t.log?.error(`@lunora/workflow: failed to signal branch parent "${r.parentId}" (event "${r.eventType}")`,a)}};export{h as MAX_BRANCHES,b as branch,k as createParallel,W as createSpawn,N as errorOutcome,C as extractBranchMarker,A as okOutcome,T as signalBranchParent,M as signalBranchParentSafe,F as stripBranchMarker};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{eventDefinitionProblem as m,RESERVED_EVENT_TYPE_PREFIX as i}from"./defineWorkflowEvent-suERPEEV.mjs";import{raiseNonRetryable as a}from"./NonRetryableError-9ZqDUzwT.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};
|
package/dist/packem_shared/{createWorkflowContext-DG-jCSoa.mjs → createWorkflowContext-zQR_9N-v.mjs}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import r from"./createWorkflows-
|
|
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{a as v,c as w}from"./run-step-7RiMJYR7.mjs";import{LunoraError as i}from"@lunora/errors";import{workflowBindingName as s}from"./defineWorkflow-tKaIifbZ.mjs";import{createSpawn as u,createParallel as m}from"./MAX_BRANCHES-C05O5LY9.mjs";import{createWaitForEvent as p}from"./createWaitForEvent-DjWsI895.mjs";const g=e=>({debug:(r,...n)=>{console.debug(e,r,...n)},error:(r,...n)=>{console.error(e,r,...n)},info:(r,...n)=>{console.info(e,r,...n)},warn:(r,...n)=>{console.warn(e,r,...n)}}),I=e=>{const r=g(`[workflow:${e.exportName}]`),n=v({env:e.env,fetchImpl:e.fetchImpl,label:"@lunora/workflow"}),f=t=>{const a=s(t),o=e.env[a];if(!o||typeof o.create!="function"||typeof o.get!="function")throw new i("INTERNAL",`@lunora/workflow: cannot spawn child workflow "${t}" — no Workflow binding "${a}" on env (is it declared in lunora/workflows.ts?)`);return o};let l=0;const d=t=>{if(t!==void 0)return t;const a=`${e.event.instanceId}-c${String(l)}`;return l+=1,a},c={env:e.env,instanceId:e.event.instanceId,log:r,nextChildId:d,parentBinding:s(e.exportName),resolveBinding:f,step:e.step};return{env:e.env,event:e.event,log:r,parallel:m(c),params:e.event.payload,run:n,runStep:w({env:e.env,log:r,nonRetryableErrorClass:e.nonRetryableErrorClass,run:n,step:e.step}),spawn:u(c),step:e.step,waitForEvent:p({nonRetryableErrorClass:e.nonRetryableErrorClass,step:e.step})}};export{I 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 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};
|
|
@@ -263,6 +263,55 @@ interface RunStepOptions {
|
|
|
263
263
|
* ```
|
|
264
264
|
*/
|
|
265
265
|
type WorkflowRunStepFunction = <A extends StepArgsValidator, Result>(step: StepDefinition<A, Result>, args: InferStepArgs<A>, options?: RunStepOptions) => Promise<Result>;
|
|
266
|
+
/**
|
|
267
|
+
* A `defineWorkflowEvent` result — the single source of truth for one external
|
|
268
|
+
* event's wire `type` and payload shape. Both ends of the exchange import the same
|
|
269
|
+
* definition (`ctx.waitForEvent(orderApproved)` inside the workflow,
|
|
270
|
+
* `workflows.get(w).sendEvent(id, orderApproved, payload)` from the caller), so
|
|
271
|
+
* there is no string to typo and no second place to update on a rename, and the
|
|
272
|
+
* payload is parsed at both ends instead of crossing as `unknown`.
|
|
273
|
+
*
|
|
274
|
+
* It does NOT make every mismatch a compile error: two definitions with the same
|
|
275
|
+
* payload shape are mutually assignable, so sending `orderRejected` where the
|
|
276
|
+
* workflow awaits `orderApproved` still type-checks (and still hibernates until
|
|
277
|
+
* the timeout). What it removes is the hand-matched literal.
|
|
278
|
+
*/
|
|
279
|
+
interface WorkflowEventDefinition<Payload = unknown> {
|
|
280
|
+
/** Runtime brand check (see `isWorkflowEventDefinition`). */
|
|
281
|
+
readonly isLunoraWorkflowEvent: true;
|
|
282
|
+
/** Validator for the event payload — parsed on send and again on receive. */
|
|
283
|
+
readonly payload: Validator<Payload>;
|
|
284
|
+
/** The wire event type Cloudflare matches `sendEvent` against `waitForEvent`. */
|
|
285
|
+
readonly type: string;
|
|
286
|
+
}
|
|
287
|
+
/** Per-call options for {@link WorkflowWaitForEventFunction}. */
|
|
288
|
+
interface WaitForEventOptions {
|
|
289
|
+
/**
|
|
290
|
+
* The durable step label, defaulting to `event:<type>`.
|
|
291
|
+
*
|
|
292
|
+
* Cloudflare identifies a memoized step by this name, so the default couples
|
|
293
|
+
* step identity to the wire type: renaming the event type also renames the
|
|
294
|
+
* step, and an instance that already recorded the wait replays into a *fresh*
|
|
295
|
+
* one that nothing will ever satisfy. Pass a stable `name` on any wait that can
|
|
296
|
+
* outlive a deploy (an approval held for days), and to tell two waits on the
|
|
297
|
+
* same event type apart in the timeline.
|
|
298
|
+
*/
|
|
299
|
+
name?: string;
|
|
300
|
+
/** How long to wait before the wait rejects. Defaults to Cloudflare's 24h. */
|
|
301
|
+
timeout?: number | string;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Hibernate until a declared event is delivered to this instance, then resolve
|
|
305
|
+
* with its validated payload. The typed wrapper over
|
|
306
|
+
* {@link WorkflowStepLike.waitForEvent}: the event's `type` comes from the
|
|
307
|
+
* definition instead of a hand-written string, and the payload is parsed through
|
|
308
|
+
* the definition's validator before the workflow resumes on it.
|
|
309
|
+
*
|
|
310
|
+
* ```ts
|
|
311
|
+
* const { approvedBy } = await ctx.waitForEvent(orderApproved, { timeout: "1 hour" });
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
type WorkflowWaitForEventFunction = <Payload>(event: WorkflowEventDefinition<Payload>, options?: WaitForEventOptions) => Promise<Payload>;
|
|
266
315
|
/**
|
|
267
316
|
* One branch of a {@link WorkflowParallelFunction} fan-out — a declared child
|
|
268
317
|
* workflow (referenced by its `lunora/workflows.ts` export name) plus the params
|
|
@@ -364,6 +413,8 @@ interface WorkflowRunContext<Params = Record<string, unknown>> {
|
|
|
364
413
|
readonly spawn: WorkflowSpawnFunction;
|
|
365
414
|
/** The native Cloudflare Workflows durable-step API. */
|
|
366
415
|
readonly step: WorkflowStepLike;
|
|
416
|
+
/** Hibernate until a declared external event arrives; resolves with its validated payload. */
|
|
417
|
+
readonly waitForEvent: WorkflowWaitForEventFunction;
|
|
367
418
|
}
|
|
368
419
|
/** The workflow body. Receives a {@link WorkflowRunContext}, returns the output. */
|
|
369
420
|
type WorkflowHandler<Params = Record<string, unknown>, Output = unknown> = (context: WorkflowRunContext<Params>) => Output | Promise<Output>;
|
|
@@ -395,7 +446,8 @@ interface WorkflowDefinition<Params = Record<string, unknown>, Output = unknown>
|
|
|
395
446
|
}
|
|
396
447
|
/**
|
|
397
448
|
* A typed handle to one declared workflow, addressable from `ctx.workflows`.
|
|
398
|
-
* Thin pass-through over the Cloudflare `Workflow` binding
|
|
449
|
+
* Thin pass-through over the Cloudflare `Workflow` binding, plus the declared-event
|
|
450
|
+
* send (which the raw binding cannot type).
|
|
399
451
|
*/
|
|
400
452
|
interface WorkflowHandle<Params = Record<string, unknown>> {
|
|
401
453
|
/** Start a new instance (optionally with an id + params). */
|
|
@@ -404,6 +456,17 @@ interface WorkflowHandle<Params = Record<string, unknown>> {
|
|
|
404
456
|
createBatch: (batch: ReadonlyArray<WorkflowCreateOptions<Params>>) => Promise<WorkflowInstanceLike[]>;
|
|
405
457
|
/** Get a handle to an existing instance by id. */
|
|
406
458
|
get: (id: string) => Promise<WorkflowInstanceLike>;
|
|
459
|
+
/**
|
|
460
|
+
* Deliver a declared event to one instance of this workflow — the typed
|
|
461
|
+
* counterpart of the workflow body's `ctx.waitForEvent`. The wire type comes
|
|
462
|
+
* from the definition (never a hand-written string) and the payload is parsed
|
|
463
|
+
* through the definition's validator **before** the send, so a bad value fails
|
|
464
|
+
* the caller's request instead of waking the workflow on garbage.
|
|
465
|
+
*
|
|
466
|
+
* Mirrors `ctx.agents.<name>.sendEvent(id, …)`: the instance is addressed by
|
|
467
|
+
* id rather than by holding an instance handle, so the common case is one call.
|
|
468
|
+
*/
|
|
469
|
+
sendEvent: <Payload>(instanceId: string, event: WorkflowEventDefinition<Payload>, payload: Payload) => Promise<void>;
|
|
407
470
|
}
|
|
408
471
|
/**
|
|
409
472
|
* The `ctx.workflows` surface available on `MutationCtx` and `ActionCtx`. Each
|
|
@@ -422,4 +485,4 @@ interface LunoraWorkflowsOptions {
|
|
|
422
485
|
*/
|
|
423
486
|
bindings: Record<string, WorkflowBindingLike>;
|
|
424
487
|
}
|
|
425
|
-
export { ArgsOf as A, BranchCompensationParams as B,
|
|
488
|
+
export { ArgsOf as A, BranchCompensationParams as B, WorkflowParallelFunction as C, WorkflowRollbackContextLike as D, WorkflowRollbackHandlerLike as E, FunctionReference as F, WorkflowSpawnFunction as G, WorkflowSpawnOptions as H, InferStepArgs as I, WorkflowStatusResult as J, WorkflowStepConfigLike as K, LunoraWorkflowsOptions as L, WorkflowStepContextLike as M, WorkflowStepRollbackOptionsLike as N, RunFunctionOptions as R, StepArgsValidator as S, WorkflowDefinition as W, Workflows as a, WorkflowEventDefinition as b, StepConfig as c, StepDefinition as d, WorkflowConfig as e, WorkflowBranch as f, WorkflowInstanceStatus as g, WorkflowEventLike as h, WorkflowStepLike as i, WorkflowRunContext as j, WorkflowLogger as k, WorkflowRunFunction as l, WorkflowRunStepFunction as m, WorkflowWaitForEventFunction as n, RunStepOptions as o, StepHandler as p, StepRollbackContext as q, StepRollbackHandler as r, StepRunContext as s, WaitForEventOptions as t, WorkflowBindingLike as u, WorkflowBranchOutputs as v, WorkflowCreateOptions as w, WorkflowHandle as x, WorkflowHandler as y, WorkflowInstanceLike as z };
|
|
@@ -263,6 +263,55 @@ interface RunStepOptions {
|
|
|
263
263
|
* ```
|
|
264
264
|
*/
|
|
265
265
|
type WorkflowRunStepFunction = <A extends StepArgsValidator, Result>(step: StepDefinition<A, Result>, args: InferStepArgs<A>, options?: RunStepOptions) => Promise<Result>;
|
|
266
|
+
/**
|
|
267
|
+
* A `defineWorkflowEvent` result — the single source of truth for one external
|
|
268
|
+
* event's wire `type` and payload shape. Both ends of the exchange import the same
|
|
269
|
+
* definition (`ctx.waitForEvent(orderApproved)` inside the workflow,
|
|
270
|
+
* `workflows.get(w).sendEvent(id, orderApproved, payload)` from the caller), so
|
|
271
|
+
* there is no string to typo and no second place to update on a rename, and the
|
|
272
|
+
* payload is parsed at both ends instead of crossing as `unknown`.
|
|
273
|
+
*
|
|
274
|
+
* It does NOT make every mismatch a compile error: two definitions with the same
|
|
275
|
+
* payload shape are mutually assignable, so sending `orderRejected` where the
|
|
276
|
+
* workflow awaits `orderApproved` still type-checks (and still hibernates until
|
|
277
|
+
* the timeout). What it removes is the hand-matched literal.
|
|
278
|
+
*/
|
|
279
|
+
interface WorkflowEventDefinition<Payload = unknown> {
|
|
280
|
+
/** Runtime brand check (see `isWorkflowEventDefinition`). */
|
|
281
|
+
readonly isLunoraWorkflowEvent: true;
|
|
282
|
+
/** Validator for the event payload — parsed on send and again on receive. */
|
|
283
|
+
readonly payload: Validator<Payload>;
|
|
284
|
+
/** The wire event type Cloudflare matches `sendEvent` against `waitForEvent`. */
|
|
285
|
+
readonly type: string;
|
|
286
|
+
}
|
|
287
|
+
/** Per-call options for {@link WorkflowWaitForEventFunction}. */
|
|
288
|
+
interface WaitForEventOptions {
|
|
289
|
+
/**
|
|
290
|
+
* The durable step label, defaulting to `event:<type>`.
|
|
291
|
+
*
|
|
292
|
+
* Cloudflare identifies a memoized step by this name, so the default couples
|
|
293
|
+
* step identity to the wire type: renaming the event type also renames the
|
|
294
|
+
* step, and an instance that already recorded the wait replays into a *fresh*
|
|
295
|
+
* one that nothing will ever satisfy. Pass a stable `name` on any wait that can
|
|
296
|
+
* outlive a deploy (an approval held for days), and to tell two waits on the
|
|
297
|
+
* same event type apart in the timeline.
|
|
298
|
+
*/
|
|
299
|
+
name?: string;
|
|
300
|
+
/** How long to wait before the wait rejects. Defaults to Cloudflare's 24h. */
|
|
301
|
+
timeout?: number | string;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Hibernate until a declared event is delivered to this instance, then resolve
|
|
305
|
+
* with its validated payload. The typed wrapper over
|
|
306
|
+
* {@link WorkflowStepLike.waitForEvent}: the event's `type` comes from the
|
|
307
|
+
* definition instead of a hand-written string, and the payload is parsed through
|
|
308
|
+
* the definition's validator before the workflow resumes on it.
|
|
309
|
+
*
|
|
310
|
+
* ```ts
|
|
311
|
+
* const { approvedBy } = await ctx.waitForEvent(orderApproved, { timeout: "1 hour" });
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
type WorkflowWaitForEventFunction = <Payload>(event: WorkflowEventDefinition<Payload>, options?: WaitForEventOptions) => Promise<Payload>;
|
|
266
315
|
/**
|
|
267
316
|
* One branch of a {@link WorkflowParallelFunction} fan-out — a declared child
|
|
268
317
|
* workflow (referenced by its `lunora/workflows.ts` export name) plus the params
|
|
@@ -364,6 +413,8 @@ interface WorkflowRunContext<Params = Record<string, unknown>> {
|
|
|
364
413
|
readonly spawn: WorkflowSpawnFunction;
|
|
365
414
|
/** The native Cloudflare Workflows durable-step API. */
|
|
366
415
|
readonly step: WorkflowStepLike;
|
|
416
|
+
/** Hibernate until a declared external event arrives; resolves with its validated payload. */
|
|
417
|
+
readonly waitForEvent: WorkflowWaitForEventFunction;
|
|
367
418
|
}
|
|
368
419
|
/** The workflow body. Receives a {@link WorkflowRunContext}, returns the output. */
|
|
369
420
|
type WorkflowHandler<Params = Record<string, unknown>, Output = unknown> = (context: WorkflowRunContext<Params>) => Output | Promise<Output>;
|
|
@@ -395,7 +446,8 @@ interface WorkflowDefinition<Params = Record<string, unknown>, Output = unknown>
|
|
|
395
446
|
}
|
|
396
447
|
/**
|
|
397
448
|
* A typed handle to one declared workflow, addressable from `ctx.workflows`.
|
|
398
|
-
* Thin pass-through over the Cloudflare `Workflow` binding
|
|
449
|
+
* Thin pass-through over the Cloudflare `Workflow` binding, plus the declared-event
|
|
450
|
+
* send (which the raw binding cannot type).
|
|
399
451
|
*/
|
|
400
452
|
interface WorkflowHandle<Params = Record<string, unknown>> {
|
|
401
453
|
/** Start a new instance (optionally with an id + params). */
|
|
@@ -404,6 +456,17 @@ interface WorkflowHandle<Params = Record<string, unknown>> {
|
|
|
404
456
|
createBatch: (batch: ReadonlyArray<WorkflowCreateOptions<Params>>) => Promise<WorkflowInstanceLike[]>;
|
|
405
457
|
/** Get a handle to an existing instance by id. */
|
|
406
458
|
get: (id: string) => Promise<WorkflowInstanceLike>;
|
|
459
|
+
/**
|
|
460
|
+
* Deliver a declared event to one instance of this workflow — the typed
|
|
461
|
+
* counterpart of the workflow body's `ctx.waitForEvent`. The wire type comes
|
|
462
|
+
* from the definition (never a hand-written string) and the payload is parsed
|
|
463
|
+
* through the definition's validator **before** the send, so a bad value fails
|
|
464
|
+
* the caller's request instead of waking the workflow on garbage.
|
|
465
|
+
*
|
|
466
|
+
* Mirrors `ctx.agents.<name>.sendEvent(id, …)`: the instance is addressed by
|
|
467
|
+
* id rather than by holding an instance handle, so the common case is one call.
|
|
468
|
+
*/
|
|
469
|
+
sendEvent: <Payload>(instanceId: string, event: WorkflowEventDefinition<Payload>, payload: Payload) => Promise<void>;
|
|
407
470
|
}
|
|
408
471
|
/**
|
|
409
472
|
* The `ctx.workflows` surface available on `MutationCtx` and `ActionCtx`. Each
|
|
@@ -422,4 +485,4 @@ interface LunoraWorkflowsOptions {
|
|
|
422
485
|
*/
|
|
423
486
|
bindings: Record<string, WorkflowBindingLike>;
|
|
424
487
|
}
|
|
425
|
-
export { ArgsOf as A, BranchCompensationParams as B,
|
|
488
|
+
export { ArgsOf as A, BranchCompensationParams as B, WorkflowParallelFunction as C, WorkflowRollbackContextLike as D, WorkflowRollbackHandlerLike as E, FunctionReference as F, WorkflowSpawnFunction as G, WorkflowSpawnOptions as H, InferStepArgs as I, WorkflowStatusResult as J, WorkflowStepConfigLike as K, LunoraWorkflowsOptions as L, WorkflowStepContextLike as M, WorkflowStepRollbackOptionsLike as N, RunFunctionOptions as R, StepArgsValidator as S, WorkflowDefinition as W, Workflows as a, WorkflowEventDefinition as b, StepConfig as c, StepDefinition as d, WorkflowConfig as e, WorkflowBranch as f, WorkflowInstanceStatus as g, WorkflowEventLike as h, WorkflowStepLike as i, WorkflowRunContext as j, WorkflowLogger as k, WorkflowRunFunction as l, WorkflowRunStepFunction as m, WorkflowWaitForEventFunction as n, RunStepOptions as o, StepHandler as p, StepRollbackContext as q, StepRollbackHandler as r, StepRunContext as s, WaitForEventOptions as t, WorkflowBindingLike as u, WorkflowBranchOutputs as v, WorkflowCreateOptions as w, WorkflowHandle as x, WorkflowHandler as y, WorkflowInstanceLike as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/workflow",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.30",
|
|
4
4
|
"description": "Durable workflows for Lunora: defineWorkflow over Cloudflare Workflows, generated WorkflowEntrypoint classes, and the ctx.workflows surface",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as y}from"@lunora/errors";import{h as E,B as v,a as p}from"./branch-marker-CCpWfS5k.mjs";import{NonRetryableError as d}from"./NonRetryableError-9ZqDUzwT.mjs";const h=100,g="lunora:spawn:",I="lunora:await:",$="lunora:signal:",x="lunora:compensate:",m="lunora:branch:",R=(t,r,n)=>({compensateWith:n?.compensateWith,id:n?.id,params:r,timeout:n?.timeout,workflow:t}),w=t=>t instanceof Error?{message:t.message,name:t.name}:{message:String(t),name:"Error"},b=t=>({status:"ok",value:t}),_=t=>({error:w(t),status:"error"}),f=async(t,r,n)=>{for(let o=r.length-1;o>=0;o-=1){const a=r[o],c=a?.plan.item.compensateWith;if(!(a===void 0||c===void 0))try{const s=t.resolveBinding(c);await t.step.do(`${x}${a.plan.childId}`,async()=>{const e=`${a.plan.childId}:compensate`,i={branch:a.plan.item.workflow,error:n,index:a.plan.index,output:a.output};return await s.create({id:e,params:i}),e})}catch(s){t.log?.error(`ctx.parallel: group-saga compensation "${c}" for branch "${a.plan.item.workflow}" (#${String(a.plan.index)}) failed`,s)}}},A=t=>async n=>{if(n.length===0)return[];if(n.length>h)throw new d(`ctx.parallel: ${String(n.length)} branches exceeds the cap of ${String(h)} — split the fan-out or raise the work into fewer child workflows`);const o=n.map((e,i)=>{const l=t.nextChildId(e.id);return{childId:l,eventType:`${m}${l}`,index:i,item:e}}),a=new Set;for(const e of o){if(a.has(e.childId))throw new d(`ctx.parallel: duplicate branch id "${e.childId}" — each branch in a group must resolve to a unique child instance id (check explicit \`id\` options)`);a.add(e.childId)}await Promise.all(o.map(e=>t.step.do(`${g}${e.childId}`,async()=>{const i=t.resolveBinding(e.item.workflow),l={eventType:e.eventType,index:e.index,parentBinding:t.parentBinding,parentId:t.instanceId};return await i.create({id:e.childId,params:{...e.item.params,[p]:l}}),e.childId})));const c=[],s=[];for(const e of o){let i;try{i=(await t.step.waitForEvent(`${I}${e.childId}`,{timeout:e.item.timeout,type:e.eventType})).payload}catch(l){const u=w(l);throw await f(t,s,u),new d(`ctx.parallel: branch "${e.item.workflow}" (#${String(e.index)}) join failed: ${u.message}`)}if(i.status==="error")throw await f(t,s,i.error),new d(`ctx.parallel: branch "${e.item.workflow}" (#${String(e.index)}) failed: ${i.error.message}`);s.push({output:i.value,plan:e}),c.push(i.value)}return c},k=t=>async(r,n,o)=>{if(E(n))throw new y("BAD_REQUEST",`@lunora/workflow: params ${v}`);const a=t.nextChildId(o?.id);return await t.step.do(`${g}${a}`,async()=>(await t.resolveBinding(r).create({id:a,params:n}),a)),t.resolveBinding(r).get(a)},N=t=>{if(typeof t!="object"||t===null)return;const r=t[p];if(typeof r!="object"||r===null)return;const n=r;if(!(typeof n.eventType!="string"||typeof n.parentBinding!="string"||typeof n.parentId!="string"||typeof n.index!="number")&&!(!n.parentBinding.startsWith("WORKFLOW_")||!n.eventType.startsWith(m)))return{eventType:n.eventType,index:n.index,parentBinding:n.parentBinding,parentId:n.parentId}},W=t=>{if(typeof t!="object"||t===null)return t;const r={...t};return Reflect.deleteProperty(r,p),r},B=async(t,r,n)=>{const o=t.env[r.parentBinding];if(!o||typeof o.get!="function")return;const a=o.get.bind(o);await t.step.do(`${$}${String(r.index)}`,async()=>(await(await a(r.parentId)).sendEvent({payload:n,type:r.eventType}),r.eventType))},C=async(t,r,n)=>{try{await B(t,r,n)}catch(o){t.log?.error(`@lunora/workflow: failed to signal branch parent "${r.parentId}" (event "${r.eventType}")`,o)}};export{h as MAX_BRANCHES,R as branch,A as createParallel,k as createSpawn,_ as errorOutcome,N as extractBranchMarker,b as okOutcome,B as signalBranchParent,C as signalBranchParentSafe,W as stripBranchMarker};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as w,c as u}from"./run-step-7RiMJYR7.mjs";import{LunoraError as v}from"@lunora/errors";import{workflowBindingName as f}from"./defineWorkflow-tKaIifbZ.mjs";import{createSpawn as i,createParallel as m}from"./MAX_BRANCHES-ZS8UAw_t.mjs";const g=e=>({debug:(n,...r)=>{console.debug(e,n,...r)},error:(n,...r)=>{console.error(e,n,...r)},info:(n,...r)=>{console.info(e,n,...r)},warn:(n,...r)=>{console.warn(e,n,...r)}}),h=e=>{const n=g(`[workflow:${e.exportName}]`),r=w({env:e.env,fetchImpl:e.fetchImpl,label:"@lunora/workflow"}),s=t=>{const a=f(t),o=e.env[a];if(!o||typeof o.create!="function"||typeof o.get!="function")throw new v("INTERNAL",`@lunora/workflow: cannot spawn child workflow "${t}" — no Workflow binding "${a}" on env (is it declared in lunora/workflows.ts?)`);return o};let l=0;const d=t=>{if(t!==void 0)return t;const a=`${e.event.instanceId}-c${String(l)}`;return l+=1,a},c={env:e.env,instanceId:e.event.instanceId,log:n,nextChildId:d,parentBinding:f(e.exportName),resolveBinding:s,step:e.step};return{env:e.env,event:e.event,log:n,parallel:m(c),params:e.event.payload,run:r,runStep:u({env:e.env,log:n,nonRetryableErrorClass:e.nonRetryableErrorClass,run:r,step:e.step}),spawn:i(c),step:e.step}};export{h as createWorkflowRunContext};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as s}from"@lunora/errors";import{h as w,B as f}from"./branch-marker-CCpWfS5k.mjs";const a=o=>{if(w(o?.params))throw new s("BAD_REQUEST",`@lunora/workflow: params ${f}`)},l=o=>({create:async r=>(a(r),o.create(r)),createBatch:async r=>{for(const e of r)a(e);return o.createBatch(r)},get:async r=>o.get(r)}),i=o=>{const r=o.bindings??{};return{get:e=>{const n=r[e];if(n===void 0){const t=Object.keys(r),c=t.length===0?"no workflows are declared":`known workflows: ${t.join(", ")}`;throw new s("INTERNAL",`@lunora/workflow: no workflow named "${e}" (${c})`)}return l(n)}}};export{i as default};
|