@lunora/workflow 1.0.0-alpha.2 → 1.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +6 -0
- package/dist/do/index.d.mts +16 -16
- package/dist/do/index.d.ts +16 -16
- package/dist/do/index.mjs +1 -32
- package/dist/index.d.mts +127 -132
- package/dist/index.d.ts +127 -132
- package/dist/index.mjs +1 -8
- package/dist/packem_shared/MAX_BRANCHES-L0FzeGTH.mjs +1 -0
- package/dist/packem_shared/NonRetryableError-CXD-mZe4.mjs +1 -0
- package/dist/packem_shared/WorkflowsRestError--PIoe9uw.mjs +1 -0
- package/dist/packem_shared/branch-marker-B2lebQA6.mjs +1 -0
- package/dist/packem_shared/createRunStep-CzGKC1eZ.mjs +1 -0
- package/dist/packem_shared/createWorkflowContext-Dw8a1U68.mjs +1 -0
- package/dist/packem_shared/createWorkflowRunContext-BNh1APpj.mjs +1 -0
- package/dist/packem_shared/createWorkflows-3djec-oS.mjs +1 -0
- package/dist/packem_shared/defineStep-rJ0XPdAx.mjs +1 -0
- package/dist/packem_shared/defineWorkflow-fBuCDkGT.mjs +1 -0
- package/dist/packem_shared/{types.d-CQO_koGe.d.mts → types.d-CZ1pcdmw.d.mts} +162 -81
- package/dist/packem_shared/{types.d-CQO_koGe.d.ts → types.d-CZ1pcdmw.d.ts} +162 -81
- package/package.json +4 -3
- package/dist/packem_shared/NonRetryableError-Dn2dTyBS.mjs +0 -27
- package/dist/packem_shared/WorkflowsRestError-b06i7K5j.mjs +0 -118
- package/dist/packem_shared/createRunStep-8jOXxP2o.mjs +0 -54
- package/dist/packem_shared/createWorkflowContext-D6thzmlF.mjs +0 -14
- package/dist/packem_shared/createWorkflowLogger-DR8P4ZoY.mjs +0 -76
- package/dist/packem_shared/createWorkflows-BoSYVIXg.mjs +0 -23
- package/dist/packem_shared/defineStep-DJQtLw7g.mjs +0 -28
- package/dist/packem_shared/defineWorkflow-DbUC-oCN.mjs +0 -15
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { ValidatorMap, InferValidatorMap, Validator } from '@lunora/values';
|
|
2
2
|
/**
|
|
3
|
-
* Opaque reference to a Lunora function. Mirrors the `FunctionReference` shape
|
|
4
|
-
* emitted by `@lunora/codegen` (and consumed by `@lunora/client`). We avoid a
|
|
5
|
-
* direct dependency to keep this package usable from the codegen pipeline
|
|
6
|
-
* itself — identical rationale to `@lunora/scheduler`'s copy.
|
|
7
|
-
*
|
|
8
|
-
* The runtime identifier lives in `__lunoraRef` — this MUST stay in lockstep
|
|
9
|
-
* with the codegen emit + `@lunora/client`'s `FunctionReference`.
|
|
10
|
-
*/
|
|
3
|
+
* Opaque reference to a Lunora function. Mirrors the `FunctionReference` shape
|
|
4
|
+
* emitted by `@lunora/codegen` (and consumed by `@lunora/client`). We avoid a
|
|
5
|
+
* direct dependency to keep this package usable from the codegen pipeline
|
|
6
|
+
* itself — identical rationale to `@lunora/scheduler`'s copy.
|
|
7
|
+
*
|
|
8
|
+
* The runtime identifier lives in `__lunoraRef` — this MUST stay in lockstep
|
|
9
|
+
* with the codegen emit + `@lunora/client`'s `FunctionReference`.
|
|
10
|
+
*/
|
|
11
11
|
interface FunctionReference {
|
|
12
12
|
readonly __lunoraRef: string;
|
|
13
13
|
/** Marker phantom type — discriminates queries / mutations / actions. */
|
|
@@ -53,17 +53,17 @@ interface WorkflowInstanceLike {
|
|
|
53
53
|
terminate: () => Promise<void>;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
* The subset of the Cloudflare `Workflow` binding the package consumes.
|
|
57
|
-
* `createBatch` is a non-optional method on the real binding (Cloudflare's
|
|
58
|
-
* `Workflow` class declares it unconditionally), so it is required here too —
|
|
59
|
-
* the handle never has to guard for its absence.
|
|
60
|
-
*/
|
|
56
|
+
* The subset of the Cloudflare `Workflow` binding the package consumes.
|
|
57
|
+
* `createBatch` is a non-optional method on the real binding (Cloudflare's
|
|
58
|
+
* `Workflow` class declares it unconditionally), so it is required here too —
|
|
59
|
+
* the handle never has to guard for its absence.
|
|
60
|
+
*/
|
|
61
61
|
interface WorkflowBindingLike<Params = Record<string, unknown>> {
|
|
62
62
|
create: (options?: WorkflowCreateOptions<Params>) => Promise<WorkflowInstanceLike>;
|
|
63
63
|
createBatch: (batch: ReadonlyArray<WorkflowCreateOptions<Params>>) => Promise<WorkflowInstanceLike[]>;
|
|
64
64
|
get: (id: string) => Promise<WorkflowInstanceLike>;
|
|
65
65
|
}
|
|
66
|
-
/** The `event` argument a workflow `run` receives. Mirrors `WorkflowEvent
|
|
66
|
+
/** The `event` argument a workflow `run` receives. Mirrors `WorkflowEvent<T>`. */
|
|
67
67
|
interface WorkflowEventLike<Params = Record<string, unknown>> {
|
|
68
68
|
readonly instanceId: string;
|
|
69
69
|
readonly payload: Readonly<Params>;
|
|
@@ -80,10 +80,10 @@ interface WorkflowStepConfigLike {
|
|
|
80
80
|
timeout?: number | string;
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
|
-
* The per-attempt info Cloudflare passes a `step.do` callback. Mirrors
|
|
84
|
-
* `WorkflowStepContext` — `attempt` is the 1-based retry counter (`> 1` on
|
|
85
|
-
* retries) and `step` carries the durable step's name + invocation count.
|
|
86
|
-
*/
|
|
83
|
+
* The per-attempt info Cloudflare passes a `step.do` callback. Mirrors
|
|
84
|
+
* `WorkflowStepContext` — `attempt` is the 1-based retry counter (`> 1` on
|
|
85
|
+
* retries) and `step` carries the durable step's name + invocation count.
|
|
86
|
+
*/
|
|
87
87
|
interface WorkflowStepContextLike {
|
|
88
88
|
/** 1-based attempt counter — `> 1` means this is a retry. */
|
|
89
89
|
attempt: number;
|
|
@@ -114,19 +114,19 @@ interface WorkflowStepRollbackOptionsLike<T = unknown> {
|
|
|
114
114
|
rollbackConfig?: WorkflowStepConfigLike;
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
|
-
* The durable step API Cloudflare hands the workflow body. Mirrors
|
|
118
|
-
* `WorkflowStep` — `do` memoizes + retries its callback, `sleep`/`sleepUntil`
|
|
119
|
-
* are durable delays, and `waitForEvent` hibernates until an external event.
|
|
120
|
-
* `do`'s two overloads mirror Cloudflare's: an optional leading `config` and an
|
|
121
|
-
* optional trailing `rollback` (compensation run when a later step fails).
|
|
122
|
-
*
|
|
123
|
-
* Known mirror gap: Cloudflare constrains `do
|
|
124
|
-
* a non-serializable step result (a function, `Map`, class instance, …) is a
|
|
125
|
-
* compile error there. `Rpc.Serializable` lives in `@cloudflare/workers-types`
|
|
126
|
-
* and is not Node-importable, so this Node-safe mirror uses a bare
|
|
127
|
-
* cannot enforce that — a non-serializable result type-checks here but fails at
|
|
128
|
-
* runtime on the platform. Keep step results JSON-serialisable.
|
|
129
|
-
*/
|
|
117
|
+
* The durable step API Cloudflare hands the workflow body. Mirrors
|
|
118
|
+
* `WorkflowStep` — `do` memoizes + retries its callback, `sleep`/`sleepUntil`
|
|
119
|
+
* are durable delays, and `waitForEvent` hibernates until an external event.
|
|
120
|
+
* `do`'s two overloads mirror Cloudflare's: an optional leading `config` and an
|
|
121
|
+
* optional trailing `rollback` (compensation run when a later step fails).
|
|
122
|
+
*
|
|
123
|
+
* Known mirror gap: Cloudflare constrains `do<T extends Rpc.Serializable<T>>` so
|
|
124
|
+
* a non-serializable step result (a function, `Map`, class instance, …) is a
|
|
125
|
+
* compile error there. `Rpc.Serializable` lives in `@cloudflare/workers-types`
|
|
126
|
+
* and is not Node-importable, so this Node-safe mirror uses a bare `<T>` and
|
|
127
|
+
* cannot enforce that — a non-serializable result type-checks here but fails at
|
|
128
|
+
* runtime on the platform. Keep step results JSON-serialisable.
|
|
129
|
+
*/
|
|
130
130
|
interface WorkflowStepLike {
|
|
131
131
|
do: {
|
|
132
132
|
<T>(name: string, callback: (context: WorkflowStepContextLike) => Promise<T>, rollback?: WorkflowStepRollbackOptionsLike<T>): Promise<T>;
|
|
@@ -155,25 +155,25 @@ interface RunFunctionOptions {
|
|
|
155
155
|
shardKey?: string;
|
|
156
156
|
}
|
|
157
157
|
/**
|
|
158
|
-
* Calls a Lunora query / mutation / action from inside a workflow and resolves
|
|
159
|
-
* with its result. Wrap it in {@link WorkflowStepLike.do} to make the call a
|
|
160
|
-
* durable, memoized, retried step:
|
|
161
|
-
*
|
|
162
|
-
* ```ts
|
|
163
|
-
* const charge = await ctx.step.do("charge", () => ctx.run(api.payments.charge, { id }));
|
|
164
|
-
* ```
|
|
165
|
-
*/
|
|
158
|
+
* Calls a Lunora query / mutation / action from inside a workflow and resolves
|
|
159
|
+
* with its result. Wrap it in {@link WorkflowStepLike.do} to make the call a
|
|
160
|
+
* durable, memoized, retried step:
|
|
161
|
+
*
|
|
162
|
+
* ```ts
|
|
163
|
+
* const charge = await ctx.step.do("charge", () => ctx.run(api.payments.charge, { id }));
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
166
|
type WorkflowRunFunction = <F extends FunctionReference>(function_: F, args?: ArgsOf<F>, options?: RunFunctionOptions) => Promise<unknown>;
|
|
167
167
|
/** Map of validators describing a step's args record — the same shape a Lunora function's `args` uses. Alias of `@lunora/values`' shared {@link ValidatorMap}. */
|
|
168
168
|
type StepArgsValidator = ValidatorMap;
|
|
169
169
|
/** Infer the args object type from a {@link StepArgsValidator} (optional validators → optional keys). Alias of `@lunora/values`' shared {@link InferValidatorMap}. */
|
|
170
170
|
type InferStepArgs<A extends StepArgsValidator> = InferValidatorMap<A>;
|
|
171
171
|
/**
|
|
172
|
-
* The context a {@link StepDefinition} handler receives as its first argument
|
|
173
|
-
* (the validated args are the second). Bundles the native per-attempt info
|
|
174
|
-
* (`attempt`, `config`, `step`) with the Worker `env`, the Lunora runner, and a
|
|
175
|
-
* logger.
|
|
176
|
-
*/
|
|
172
|
+
* The context a {@link StepDefinition} handler receives as its first argument
|
|
173
|
+
* (the validated args are the second). Bundles the native per-attempt info
|
|
174
|
+
* (`attempt`, `config`, `step`) with the Worker `env`, the Lunora runner, and a
|
|
175
|
+
* logger.
|
|
176
|
+
*/
|
|
177
177
|
interface StepRunContext {
|
|
178
178
|
/** 1-based retry counter — `> 1` means Cloudflare is retrying the step. */
|
|
179
179
|
readonly attempt: number;
|
|
@@ -226,10 +226,10 @@ interface StepConfig<A extends StepArgsValidator, Result> {
|
|
|
226
226
|
rollbackConfig?: WorkflowStepConfigLike;
|
|
227
227
|
}
|
|
228
228
|
/**
|
|
229
|
-
* A `defineStep` result — a reusable, schema-validated durable step. Run it from
|
|
230
|
-
* a workflow body with `ctx.runStep(step, args)`. The phantom generics carry the
|
|
231
|
-
* inferred args + result types to the call site.
|
|
232
|
-
*/
|
|
229
|
+
* A `defineStep` result — a reusable, schema-validated durable step. Run it from
|
|
230
|
+
* a workflow body with `ctx.runStep(step, args)`. The phantom generics carry the
|
|
231
|
+
* inferred args + result types to the call site.
|
|
232
|
+
*/
|
|
233
233
|
interface StepDefinition<A extends StepArgsValidator = StepArgsValidator, Result = unknown> {
|
|
234
234
|
/** Validators for the step's args. */
|
|
235
235
|
readonly args: A;
|
|
@@ -254,20 +254,97 @@ interface RunStepOptions {
|
|
|
254
254
|
config?: WorkflowStepConfigLike;
|
|
255
255
|
}
|
|
256
256
|
/**
|
|
257
|
-
* Run a reusable {@link StepDefinition} as a durable, memoized, retried step:
|
|
258
|
-
* validates the args before the body runs and the result after (when the step
|
|
259
|
-
* declares `returns`), and forwards any rollback handler to Cloudflare.
|
|
260
|
-
*
|
|
261
|
-
* ```ts
|
|
262
|
-
* const data = await ctx.runStep(fetchImage, { imageKey });
|
|
263
|
-
* ```
|
|
264
|
-
*/
|
|
257
|
+
* Run a reusable {@link StepDefinition} as a durable, memoized, retried step:
|
|
258
|
+
* validates the args before the body runs and the result after (when the step
|
|
259
|
+
* declares `returns`), and forwards any rollback handler to Cloudflare.
|
|
260
|
+
*
|
|
261
|
+
* ```ts
|
|
262
|
+
* const data = await ctx.runStep(fetchImage, { imageKey });
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
265
|
type WorkflowRunStepFunction = <A extends StepArgsValidator, Result>(step: StepDefinition<A, Result>, args: InferStepArgs<A>, options?: RunStepOptions) => Promise<Result>;
|
|
266
266
|
/**
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
|
|
267
|
+
* One branch of a {@link WorkflowParallelFunction} fan-out — a declared child
|
|
268
|
+
* workflow (referenced by its `lunora/workflows.ts` export name) plus the params
|
|
269
|
+
* it is created with. The phantom `Output` carries the child's result type into
|
|
270
|
+
* the `ctx.parallel(...)` result tuple. Build one with the `branch(...)` helper.
|
|
271
|
+
*/
|
|
272
|
+
interface WorkflowBranch<Output = unknown> {
|
|
273
|
+
/** Phantom marker for the branch output type — never present at runtime. */
|
|
274
|
+
readonly __output?: Output;
|
|
275
|
+
/**
|
|
276
|
+
* Optional group-saga compensation (plan 075 Phase 3): the `lunora/workflows.ts`
|
|
277
|
+
* export name of a workflow to run if a **sibling** branch in the same
|
|
278
|
+
* `ctx.parallel(...)` group fails **after** this branch has already completed.
|
|
279
|
+
* It is spawned fire-and-forget (a durable, replay-safe idempotent create) with
|
|
280
|
+
* {@link BranchCompensationParams} as its `ctx.params`. Omit for no
|
|
281
|
+
* compensation — a group where no branch sets this behaves exactly as a plain
|
|
282
|
+
* fan-out (fail-fast, no rollback).
|
|
283
|
+
*/
|
|
284
|
+
readonly compensateWith?: string;
|
|
285
|
+
/** Optional explicit child instance id (defaults to a deterministic parent-derived id). */
|
|
286
|
+
readonly id?: string;
|
|
287
|
+
/** The params the child instance is created with — surfaced as the child's `ctx.params`. */
|
|
288
|
+
readonly params?: Record<string, unknown>;
|
|
289
|
+
/** Optional wait timeout for this branch (the parent's `waitForEvent` timeout). */
|
|
290
|
+
readonly timeout?: number | string;
|
|
291
|
+
/** The `lunora/workflows.ts` export name of the child workflow to run. */
|
|
292
|
+
readonly workflow: string;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* The `ctx.params` a group-saga compensation workflow (a branch's
|
|
296
|
+
* {@link WorkflowBranch.compensateWith}) receives when a sibling's failure rolls
|
|
297
|
+
* back the group. Everything is plain-serialisable — the compensation is an
|
|
298
|
+
* ordinary declared workflow, so it can `ctx.runStep(...)` its own undo logic.
|
|
299
|
+
*/
|
|
300
|
+
interface BranchCompensationParams {
|
|
301
|
+
/** Index signature: this is a workflow `params` bag, so it is a valid `Record<string, unknown>` payload. */
|
|
302
|
+
[key: string]: unknown;
|
|
303
|
+
/** The export name of the completed branch being compensated. */
|
|
304
|
+
branch: string;
|
|
305
|
+
/** The serialised error of the sibling branch whose failure triggered the group rollback. */
|
|
306
|
+
error: {
|
|
307
|
+
message: string;
|
|
308
|
+
name: string;
|
|
309
|
+
};
|
|
310
|
+
/** Declaration-order index of the completed branch being compensated. */
|
|
311
|
+
index: number;
|
|
312
|
+
/** The completed branch's output value — what it returned before the group failed. */
|
|
313
|
+
output?: unknown;
|
|
314
|
+
}
|
|
315
|
+
/** Map a tuple of {@link WorkflowBranch}es to the tuple of their output types, preserving order. */
|
|
316
|
+
type WorkflowBranchOutputs<B extends ReadonlyArray<WorkflowBranch>> = { -readonly [K in keyof B]: B[K] extends WorkflowBranch<infer Output> ? Output : never; };
|
|
317
|
+
/**
|
|
318
|
+
* Run branches as isolated child workflow instances and resolve with their
|
|
319
|
+
* outputs in declaration order. Each branch gets its own Durable Object (own
|
|
320
|
+
* memory / CPU / retry budget); the parent hibernates while they execute. Rejects
|
|
321
|
+
* (non-retryable) on the first branch that fails.
|
|
322
|
+
*
|
|
323
|
+
* ```ts
|
|
324
|
+
* const [tags, thumb] = await ctx.parallel([
|
|
325
|
+
* branch("imageTag", { key }),
|
|
326
|
+
* branch("thumbnail", { key }),
|
|
327
|
+
* ]);
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
type WorkflowParallelFunction = <const B extends ReadonlyArray<WorkflowBranch>>(branches: B) => Promise<WorkflowBranchOutputs<B>>;
|
|
331
|
+
/** Per-call options for {@link WorkflowSpawnFunction}. */
|
|
332
|
+
interface WorkflowSpawnOptions {
|
|
333
|
+
/** Explicit child instance id (defaults to a deterministic parent-derived id). */
|
|
334
|
+
id?: string;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Fire-and-forget start of a declared child workflow from inside a workflow body
|
|
338
|
+
* — replay-safe (idempotent create), returns a live handle to the child. Use
|
|
339
|
+
* {@link WorkflowParallelFunction} instead when you need to await results.
|
|
340
|
+
*/
|
|
341
|
+
type WorkflowSpawnFunction = (workflow: string, params?: Record<string, unknown>, options?: WorkflowSpawnOptions) => Promise<WorkflowInstanceLike>;
|
|
342
|
+
/**
|
|
343
|
+
* The context object passed to a `defineWorkflow` handler. Bundles the native
|
|
344
|
+
* Cloudflare durability primitives (`step`, `event`) with the Lunora runner
|
|
345
|
+
* (`run`), the reusable-step runner (`runStep`), the fan-out primitives
|
|
346
|
+
* (`parallel` / `spawn`), the Worker `env`, and a logger.
|
|
347
|
+
*/
|
|
271
348
|
interface WorkflowRunContext<Params = Record<string, unknown>> {
|
|
272
349
|
/** The Worker environment bindings. */
|
|
273
350
|
readonly env: Record<string, unknown>;
|
|
@@ -275,12 +352,16 @@ interface WorkflowRunContext<Params = Record<string, unknown>> {
|
|
|
275
352
|
readonly event: WorkflowEventLike<Params>;
|
|
276
353
|
/** Structured logger surfaced in `wrangler tail` / Studio logs. */
|
|
277
354
|
readonly log: WorkflowLogger;
|
|
355
|
+
/** Run branches as isolated child workflow instances and await their outputs (declaration-ordered tuple). */
|
|
356
|
+
readonly parallel: WorkflowParallelFunction;
|
|
278
357
|
/** Convenience alias for `event.payload`. */
|
|
279
358
|
readonly params: Readonly<Params>;
|
|
280
359
|
/** Invoke a Lunora function; wrap in `step.do(...)` for durability. */
|
|
281
360
|
readonly run: WorkflowRunFunction;
|
|
282
361
|
/** Run a reusable, schema-validated {@link StepDefinition} as a durable step. */
|
|
283
362
|
readonly runStep: WorkflowRunStepFunction;
|
|
363
|
+
/** Fire-and-forget start of a declared child workflow (replay-safe; returns a live handle). */
|
|
364
|
+
readonly spawn: WorkflowSpawnFunction;
|
|
284
365
|
/** The native Cloudflare Workflows durable-step API. */
|
|
285
366
|
readonly step: WorkflowStepLike;
|
|
286
367
|
}
|
|
@@ -291,19 +372,19 @@ interface WorkflowConfig<Params = Record<string, unknown>, Output = unknown> {
|
|
|
291
372
|
/** The workflow body — the multi-step durable program. */
|
|
292
373
|
handler: WorkflowHandler<Params, Output>;
|
|
293
374
|
/**
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
375
|
+
* Optional override for the deployed workflow name — the `workflows[].name`
|
|
376
|
+
* written to `wrangler.jsonc`. Defaults to a kebab-cased form of the
|
|
377
|
+
* `lunora/workflows.ts` export name (`orderPipeline` → `order-pipeline`).
|
|
378
|
+
* This does NOT change the binding name, which is always derived from the
|
|
379
|
+
* export name (`orderPipeline` → `WORKFLOW_ORDER_PIPELINE`).
|
|
380
|
+
*/
|
|
300
381
|
name?: string;
|
|
301
382
|
}
|
|
302
383
|
/**
|
|
303
|
-
* A `defineWorkflow` result — the config plus the runtime brand codegen and the
|
|
304
|
-
* config layer use to discover it. The phantom `__params` / `__output` carry
|
|
305
|
-
* the inferred types to the generated `ctx.workflows` handle.
|
|
306
|
-
*/
|
|
384
|
+
* A `defineWorkflow` result — the config plus the runtime brand codegen and the
|
|
385
|
+
* config layer use to discover it. The phantom `__params` / `__output` carry
|
|
386
|
+
* the inferred types to the generated `ctx.workflows` handle.
|
|
387
|
+
*/
|
|
307
388
|
interface WorkflowDefinition<Params = Record<string, unknown>, Output = unknown> extends WorkflowConfig<Params, Output> {
|
|
308
389
|
/** Phantom marker for the output type — never present at runtime. */
|
|
309
390
|
readonly __output?: Output;
|
|
@@ -313,9 +394,9 @@ interface WorkflowDefinition<Params = Record<string, unknown>, Output = unknown>
|
|
|
313
394
|
readonly isLunoraWorkflow: true;
|
|
314
395
|
}
|
|
315
396
|
/**
|
|
316
|
-
* A typed handle to one declared workflow, addressable from `ctx.workflows`.
|
|
317
|
-
* Thin pass-through over the Cloudflare `Workflow` binding.
|
|
318
|
-
*/
|
|
397
|
+
* A typed handle to one declared workflow, addressable from `ctx.workflows`.
|
|
398
|
+
* Thin pass-through over the Cloudflare `Workflow` binding.
|
|
399
|
+
*/
|
|
319
400
|
interface WorkflowHandle<Params = Record<string, unknown>> {
|
|
320
401
|
/** Start a new instance (optionally with an id + params). */
|
|
321
402
|
create: (options?: WorkflowCreateOptions<Params>) => Promise<WorkflowInstanceLike>;
|
|
@@ -325,9 +406,9 @@ interface WorkflowHandle<Params = Record<string, unknown>> {
|
|
|
325
406
|
get: (id: string) => Promise<WorkflowInstanceLike>;
|
|
326
407
|
}
|
|
327
408
|
/**
|
|
328
|
-
* The `ctx.workflows` surface available on `MutationCtx` and `ActionCtx`. Each
|
|
329
|
-
* declared workflow is reachable by its `lunora/workflows.ts` export name.
|
|
330
|
-
*/
|
|
409
|
+
* The `ctx.workflows` surface available on `MutationCtx` and `ActionCtx`. Each
|
|
410
|
+
* declared workflow is reachable by its `lunora/workflows.ts` export name.
|
|
411
|
+
*/
|
|
331
412
|
interface Workflows {
|
|
332
413
|
/** Resolve the handle for a declared workflow by export name. */
|
|
333
414
|
get: <Params = Record<string, unknown>>(name: string) => WorkflowHandle<Params>;
|
|
@@ -335,10 +416,10 @@ interface Workflows {
|
|
|
335
416
|
/** Options for `createWorkflows`. */
|
|
336
417
|
interface LunoraWorkflowsOptions {
|
|
337
418
|
/**
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
419
|
+
* Map of `lunora/workflows.ts` export name → its Cloudflare `Workflow`
|
|
420
|
+
* binding. Codegen builds this from `env` (`{ orderPipeline:
|
|
421
|
+
* env.WORKFLOW_ORDER_PIPELINE }`); for manual wiring construct it yourself.
|
|
422
|
+
*/
|
|
342
423
|
bindings: Record<string, WorkflowBindingLike>;
|
|
343
424
|
}
|
|
344
|
-
export { ArgsOf as A,
|
|
425
|
+
export { ArgsOf as A, BranchCompensationParams as B, WorkflowSpawnFunction as C, WorkflowSpawnOptions as D, WorkflowStatusResult as E, FunctionReference as F, WorkflowStepConfigLike as G, WorkflowStepContextLike as H, InferStepArgs as I, WorkflowStepRollbackOptionsLike as J, LunoraWorkflowsOptions as L, RunFunctionOptions as R, StepArgsValidator as S, WorkflowDefinition as W, Workflows as a, StepConfig as b, StepDefinition as c, WorkflowConfig as d, WorkflowBranch as e, WorkflowInstanceStatus as f, WorkflowEventLike as g, WorkflowStepLike as h, WorkflowRunContext as i, WorkflowLogger as j, WorkflowRunFunction as k, WorkflowRunStepFunction as l, RunStepOptions as m, StepHandler as n, StepRollbackContext as o, StepRollbackHandler as p, StepRunContext as q, WorkflowBindingLike as r, WorkflowBranchOutputs as s, WorkflowCreateOptions as t, WorkflowHandle as u, WorkflowHandler as v, WorkflowInstanceLike as w, WorkflowParallelFunction as x, WorkflowRollbackContextLike as y, WorkflowRollbackHandlerLike 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.21",
|
|
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",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"directory": "packages/workflow"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
|
-
"dist",
|
|
26
|
+
"./dist",
|
|
27
27
|
"README.md",
|
|
28
28
|
"LICENSE.md",
|
|
29
29
|
"__assets__"
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@lunora/
|
|
51
|
+
"@lunora/errors": "1.0.0-alpha.14",
|
|
52
|
+
"@lunora/values": "1.0.0-alpha.19"
|
|
52
53
|
},
|
|
53
54
|
"engines": {
|
|
54
55
|
"node": "^22.15.0 || >=24.11.0"
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const NON_RETRYABLE_BRAND = "__lunoraNonRetryable";
|
|
2
|
-
class NonRetryableError extends Error {
|
|
3
|
-
constructor(message, name = "NonRetryableError") {
|
|
4
|
-
super(message);
|
|
5
|
-
this.name = name;
|
|
6
|
-
this[NON_RETRYABLE_BRAND] = true;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
const isNonRetryableError = (value) => value instanceof Error && value[NON_RETRYABLE_BRAND] === true;
|
|
10
|
-
const toNativeNonRetryableError = (error, NativeNonRetryableError) => {
|
|
11
|
-
const native = new NativeNonRetryableError(error.message, error.name);
|
|
12
|
-
if (error.stack !== void 0) {
|
|
13
|
-
native.stack = error.stack;
|
|
14
|
-
}
|
|
15
|
-
if (error.cause !== void 0 && native.cause === void 0) {
|
|
16
|
-
native.cause = error.cause;
|
|
17
|
-
}
|
|
18
|
-
return native;
|
|
19
|
-
};
|
|
20
|
-
const convertNonRetryableError = (error, NativeNonRetryableError) => {
|
|
21
|
-
if (NativeNonRetryableError !== void 0 && isNonRetryableError(error)) {
|
|
22
|
-
throw toNativeNonRetryableError(error, NativeNonRetryableError);
|
|
23
|
-
}
|
|
24
|
-
throw error;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export { NonRetryableError, convertNonRetryableError, isNonRetryableError, toNativeNonRetryableError };
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
const API_BASE = "https://api.cloudflare.com/client/v4/accounts";
|
|
2
|
-
const KNOWN_STATUSES = {
|
|
3
|
-
complete: true,
|
|
4
|
-
errored: true,
|
|
5
|
-
paused: true,
|
|
6
|
-
queued: true,
|
|
7
|
-
running: true,
|
|
8
|
-
terminated: true,
|
|
9
|
-
unknown: true,
|
|
10
|
-
waiting: true,
|
|
11
|
-
waitingForPause: true
|
|
12
|
-
};
|
|
13
|
-
const toStatus = (value) => typeof value === "string" && Object.hasOwn(KNOWN_STATUSES, value) ? value : "unknown";
|
|
14
|
-
const asString = (value) => typeof value === "string" && value !== "" ? value : void 0;
|
|
15
|
-
const stringOr = (value, fallback) => typeof value === "string" ? value : fallback;
|
|
16
|
-
const asBoolean = (value) => typeof value === "boolean" ? value : void 0;
|
|
17
|
-
const countAttempts = (value) => {
|
|
18
|
-
if (Array.isArray(value)) {
|
|
19
|
-
return value.length;
|
|
20
|
-
}
|
|
21
|
-
return typeof value === "number" ? value : void 0;
|
|
22
|
-
};
|
|
23
|
-
const toSummary = (raw) => {
|
|
24
|
-
return {
|
|
25
|
-
createdOn: asString(raw["created_on"]),
|
|
26
|
-
endedOn: asString(raw["ended_on"]),
|
|
27
|
-
id: stringOr(raw["id"], ""),
|
|
28
|
-
startedOn: asString(raw["started_on"]),
|
|
29
|
-
status: toStatus(raw["status"])
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
const toStep = (raw) => {
|
|
33
|
-
return {
|
|
34
|
-
attempts: countAttempts(raw["attempts"]),
|
|
35
|
-
end: asString(raw["end"]),
|
|
36
|
-
error: raw["error"],
|
|
37
|
-
name: stringOr(raw["name"], ""),
|
|
38
|
-
output: raw["output"],
|
|
39
|
-
start: asString(raw["start"]),
|
|
40
|
-
success: asBoolean(raw["success"]),
|
|
41
|
-
type: asString(raw["type"])
|
|
42
|
-
};
|
|
43
|
-
};
|
|
44
|
-
class WorkflowsRestError extends Error {
|
|
45
|
-
status;
|
|
46
|
-
constructor(status, body) {
|
|
47
|
-
super(`Cloudflare Workflows REST API returned ${String(status)}: ${body}`);
|
|
48
|
-
this.name = "WorkflowsRestError";
|
|
49
|
-
this.status = status;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
const createWorkflowsRestClient = (config) => {
|
|
53
|
-
const fetchImpl = config.fetch ?? globalThis.fetch.bind(globalThis);
|
|
54
|
-
const base = `${API_BASE}/${config.accountId}/workflows`;
|
|
55
|
-
const request = async (path, init) => {
|
|
56
|
-
const response = await fetchImpl(`${base}${path}`, {
|
|
57
|
-
...init,
|
|
58
|
-
headers: { Authorization: `Bearer ${config.apiToken}`, "Content-Type": "application/json" }
|
|
59
|
-
});
|
|
60
|
-
const text = await response.text();
|
|
61
|
-
let body;
|
|
62
|
-
try {
|
|
63
|
-
body = JSON.parse(text);
|
|
64
|
-
} catch {
|
|
65
|
-
throw new WorkflowsRestError(response.status, text);
|
|
66
|
-
}
|
|
67
|
-
if (!response.ok || body["success"] === false) {
|
|
68
|
-
throw new WorkflowsRestError(response.status, text);
|
|
69
|
-
}
|
|
70
|
-
return body;
|
|
71
|
-
};
|
|
72
|
-
return {
|
|
73
|
-
getInstance: async ({ instanceId, workflowName }) => {
|
|
74
|
-
const body = await request(`/${encodeURIComponent(workflowName)}/instances/${encodeURIComponent(instanceId)}`);
|
|
75
|
-
const result = body["result"] ?? {};
|
|
76
|
-
const steps = Array.isArray(result["steps"]) ? result["steps"] : [];
|
|
77
|
-
return {
|
|
78
|
-
...toSummary(result),
|
|
79
|
-
error: result["error"],
|
|
80
|
-
output: result["output"],
|
|
81
|
-
params: result["params"],
|
|
82
|
-
steps: steps.map((step) => toStep(step))
|
|
83
|
-
};
|
|
84
|
-
},
|
|
85
|
-
listInstances: async ({ page, perPage, status, workflowName }) => {
|
|
86
|
-
const query = new URLSearchParams();
|
|
87
|
-
if (status !== void 0) {
|
|
88
|
-
query.set("status", status);
|
|
89
|
-
}
|
|
90
|
-
if (page !== void 0) {
|
|
91
|
-
query.set("page", String(page));
|
|
92
|
-
}
|
|
93
|
-
if (perPage !== void 0) {
|
|
94
|
-
query.set("per_page", String(perPage));
|
|
95
|
-
}
|
|
96
|
-
const suffix = query.toString() === "" ? "" : `?${query.toString()}`;
|
|
97
|
-
const body = await request(`/${encodeURIComponent(workflowName)}/instances${suffix}`);
|
|
98
|
-
const result = Array.isArray(body["result"]) ? body["result"] : [];
|
|
99
|
-
const info = body["result_info"] ?? {};
|
|
100
|
-
return {
|
|
101
|
-
instances: result.map((instance) => toSummary(instance)),
|
|
102
|
-
page: typeof info["page"] === "number" ? info["page"] : page ?? 1,
|
|
103
|
-
perPage: typeof info["per_page"] === "number" ? info["per_page"] : perPage ?? result.length,
|
|
104
|
-
totalCount: typeof info["total_count"] === "number" ? info["total_count"] : void 0
|
|
105
|
-
};
|
|
106
|
-
},
|
|
107
|
-
setInstanceStatus: async ({ action, instanceId, workflowName }) => {
|
|
108
|
-
const body = await request(`/${encodeURIComponent(workflowName)}/instances/${encodeURIComponent(instanceId)}`, {
|
|
109
|
-
body: JSON.stringify({ status: action }),
|
|
110
|
-
method: "PATCH"
|
|
111
|
-
});
|
|
112
|
-
const result = body["result"] ?? {};
|
|
113
|
-
return { status: toStatus(result["status"]) };
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export { WorkflowsRestError, createWorkflowsRestClient };
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { parseValidatorMap } from '@lunora/values';
|
|
2
|
-
import { convertNonRetryableError, NonRetryableError } from './NonRetryableError-Dn2dTyBS.mjs';
|
|
3
|
-
|
|
4
|
-
const validateStepArgs = (validators, source) => parseValidatorMap(validators, source, "step args");
|
|
5
|
-
const createRunStep = (deps) => async (step, args, options) => {
|
|
6
|
-
const config = options?.config ?? step.config;
|
|
7
|
-
const validatedArgs = validateStepArgs(step.args, args);
|
|
8
|
-
const callback = async (nativeContext) => {
|
|
9
|
-
const stepContext = {
|
|
10
|
-
attempt: nativeContext.attempt,
|
|
11
|
-
config: nativeContext.config,
|
|
12
|
-
env: deps.env,
|
|
13
|
-
log: deps.log,
|
|
14
|
-
run: deps.run,
|
|
15
|
-
step: nativeContext.step
|
|
16
|
-
};
|
|
17
|
-
let result;
|
|
18
|
-
try {
|
|
19
|
-
result = await step.handler(stepContext, validatedArgs);
|
|
20
|
-
} catch (error) {
|
|
21
|
-
return convertNonRetryableError(error, deps.nonRetryableErrorClass);
|
|
22
|
-
}
|
|
23
|
-
if (!step.returns) {
|
|
24
|
-
return result;
|
|
25
|
-
}
|
|
26
|
-
try {
|
|
27
|
-
return step.returns.parse(result);
|
|
28
|
-
} catch (error) {
|
|
29
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
30
|
-
const nonRetryable = new NonRetryableError(`step "${step.name}" returns validation failed: ${message}`);
|
|
31
|
-
if (error !== void 0) {
|
|
32
|
-
nonRetryable.cause = error;
|
|
33
|
-
}
|
|
34
|
-
return convertNonRetryableError(nonRetryable, deps.nonRetryableErrorClass);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
const rollbackHandler = step.rollback;
|
|
38
|
-
const rollbackOptions = rollbackHandler ? {
|
|
39
|
-
rollback: async (rollbackContext) => {
|
|
40
|
-
await rollbackHandler({
|
|
41
|
-
args: validatedArgs,
|
|
42
|
-
env: deps.env,
|
|
43
|
-
error: rollbackContext.error,
|
|
44
|
-
log: deps.log,
|
|
45
|
-
output: rollbackContext.output,
|
|
46
|
-
run: deps.run
|
|
47
|
-
});
|
|
48
|
-
},
|
|
49
|
-
rollbackConfig: step.rollbackConfig
|
|
50
|
-
} : void 0;
|
|
51
|
-
return config === void 0 ? deps.step.do(step.name, callback, rollbackOptions) : deps.step.do(step.name, config, callback, rollbackOptions);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export { createRunStep, validateStepArgs };
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import createWorkflows from './createWorkflows-BoSYVIXg.mjs';
|
|
2
|
-
|
|
3
|
-
const createWorkflowContext = (env, specs) => {
|
|
4
|
-
const bindings = {};
|
|
5
|
-
for (const spec of specs) {
|
|
6
|
-
const binding = env[spec.binding];
|
|
7
|
-
if (binding && typeof binding.create === "function" && typeof binding.createBatch === "function" && typeof binding.get === "function") {
|
|
8
|
-
bindings[spec.exportName] = binding;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
return createWorkflows({ bindings });
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export { createWorkflowContext };
|