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

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.
@@ -1,5 +1,5 @@
1
1
  import { WorkflowEntrypoint, WorkflowEvent, WorkflowStep } from 'cloudflare:workers';
2
- import { W as WorkflowDefinition } from "../packem_shared/types.d-Fdeu2P2C.mjs";
2
+ import { W as WorkflowDefinition } from "../packem_shared/types.d-C9W6rEWv.mjs";
3
3
  import '@lunora/values';
4
4
  /**
5
5
  * Base class for the generated `WorkflowEntrypoint` classes. Applies a
@@ -1,5 +1,5 @@
1
1
  import { WorkflowEntrypoint, WorkflowEvent, WorkflowStep } from 'cloudflare:workers';
2
- import { W as WorkflowDefinition } from "../packem_shared/types.d-Fdeu2P2C.js";
2
+ import { W as WorkflowDefinition } from "../packem_shared/types.d-C9W6rEWv.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,8 +1,8 @@
1
1
  import { WorkflowEntrypoint } from 'cloudflare:workers';
2
2
  import { NonRetryableError } from 'cloudflare:workflows';
3
3
  import { convertNonRetryableError } from '../packem_shared/NonRetryableError-Dn2dTyBS.mjs';
4
- import { stripBranchMarker, signalBranchParent, extractBranchMarker, errorOutcome, okOutcome } from '../packem_shared/MAX_BRANCHES-C9MJIFii.mjs';
5
- import { createWorkflowRunContext } from '../packem_shared/createWorkflowRunContext-BsMyGuMR.mjs';
4
+ import { stripBranchMarker, signalBranchParent, extractBranchMarker, errorOutcome, okOutcome } from '../packem_shared/MAX_BRANCHES-D6cQabJ1.mjs';
5
+ import { createWorkflowRunContext } from '../packem_shared/createWorkflowRunContext-CYI9SeI9.mjs';
6
6
 
7
7
  class LunoraWorkflow extends WorkflowEntrypoint {
8
8
  /** The `lunora/workflows.ts` export name, for log correlation. */
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
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-Fdeu2P2C.mjs";
2
- export type { A as ArgsOf, 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, B as WorkflowSpawnFunction, C as WorkflowSpawnOptions, D as WorkflowStatusResult, E as WorkflowStepConfigLike, G as WorkflowStepContextLike, H as WorkflowStepRollbackOptionsLike } from "./packem_shared/types.d-Fdeu2P2C.mjs";
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";
3
3
  import '@lunora/values';
4
4
  /** Wiring info for one declared workflow, emitted by codegen into the generated shard. */
5
5
  interface WorkflowBindingSpec {
@@ -142,6 +142,7 @@ declare const MAX_BRANCHES = 100;
142
142
  * tuple — e.g. `branch("imageTag", { key })` typed as `branch` of `{ tags }`.
143
143
  */
144
144
  declare const branch: <Output = unknown>(workflow: string, params?: Record<string, unknown>, options?: {
145
+ compensateWith?: string;
145
146
  id?: string;
146
147
  timeout?: number | string;
147
148
  }) => WorkflowBranch<Output>;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
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-Fdeu2P2C.js";
2
- export type { A as ArgsOf, 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, B as WorkflowSpawnFunction, C as WorkflowSpawnOptions, D as WorkflowStatusResult, E as WorkflowStepConfigLike, G as WorkflowStepContextLike, H as WorkflowStepRollbackOptionsLike } from "./packem_shared/types.d-Fdeu2P2C.js";
1
+ import { a as Workflows, L as LunoraWorkflowsOptions, S as StepArgsValidator, b as StepConfig, c as StepDefinition, d as WorkflowConfig, W as WorkflowDefinition, e as WorkflowBranch, f as WorkflowInstanceStatus, g as WorkflowEventLike, h as WorkflowStepLike, i as WorkflowRunContext, j as WorkflowLogger, k as WorkflowRunFunction, l as WorkflowRunStepFunction } from "./packem_shared/types.d-C9W6rEWv.js";
2
+ export type { A as ArgsOf, B as BranchCompensationParams, F as FunctionReference, I as InferStepArgs, R as RunFunctionOptions, m as RunStepOptions, n as StepHandler, o as StepRollbackContext, p as StepRollbackHandler, q as StepRunContext, r as WorkflowBindingLike, s as WorkflowBranchOutputs, t as WorkflowCreateOptions, u as WorkflowHandle, v as WorkflowHandler, w as WorkflowInstanceLike, x as WorkflowParallelFunction, y as WorkflowRollbackContextLike, z as WorkflowRollbackHandlerLike, C as WorkflowSpawnFunction, D as WorkflowSpawnOptions, E as WorkflowStatusResult, G as WorkflowStepConfigLike, H as WorkflowStepContextLike, J as WorkflowStepRollbackOptionsLike } from "./packem_shared/types.d-C9W6rEWv.js";
3
3
  import '@lunora/values';
4
4
  /** Wiring info for one declared workflow, emitted by codegen into the generated shard. */
5
5
  interface WorkflowBindingSpec {
@@ -142,6 +142,7 @@ declare const MAX_BRANCHES = 100;
142
142
  * tuple — e.g. `branch("imageTag", { key })` typed as `branch` of `{ tags }`.
143
143
  */
144
144
  declare const branch: <Output = unknown>(workflow: string, params?: Record<string, unknown>, options?: {
145
+ compensateWith?: string;
145
146
  id?: string;
146
147
  timeout?: number | string;
147
148
  }) => WorkflowBranch<Output>;
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ export { default as createWorkflows } from './packem_shared/createWorkflows-BoSY
3
3
  export { defineStep, isStepDefinition } from './packem_shared/defineStep-DJQtLw7g.mjs';
4
4
  export { defineWorkflow, isWorkflowDefinition, workflowBindingName, workflowClassName, workflowDefaultName } from './packem_shared/defineWorkflow-DbUC-oCN.mjs';
5
5
  export { NonRetryableError, convertNonRetryableError, isNonRetryableError, toNativeNonRetryableError } from './packem_shared/NonRetryableError-Dn2dTyBS.mjs';
6
- export { MAX_BRANCHES, branch } from './packem_shared/MAX_BRANCHES-C9MJIFii.mjs';
6
+ export { MAX_BRANCHES, branch } from './packem_shared/MAX_BRANCHES-D6cQabJ1.mjs';
7
7
  export { WorkflowsRestError, createWorkflowsRestClient } from './packem_shared/WorkflowsRestError-b06i7K5j.mjs';
8
- export { createWorkflowRunContext } from './packem_shared/createWorkflowRunContext-BsMyGuMR.mjs';
8
+ export { createWorkflowRunContext } from './packem_shared/createWorkflowRunContext-CYI9SeI9.mjs';
9
9
  export { createRunStep, validateStepArgs } from './packem_shared/createRunStep-8jOXxP2o.mjs';
@@ -5,9 +5,10 @@ const BRANCH_MARKER_KEY = "__lunoraBranch";
5
5
  const SPAWN_STEP_PREFIX = "lunora:spawn:";
6
6
  const AWAIT_STEP_PREFIX = "lunora:await:";
7
7
  const SIGNAL_STEP_PREFIX = "lunora:signal:";
8
+ const COMPENSATE_STEP_PREFIX = "lunora:compensate:";
8
9
  const BRANCH_EVENT_PREFIX = "lunora:branch:";
9
10
  const branch = (workflow, params, options) => {
10
- return { id: options?.id, params, timeout: options?.timeout, workflow };
11
+ return { compensateWith: options?.compensateWith, id: options?.id, params, timeout: options?.timeout, workflow };
11
12
  };
12
13
  const serializeError = (error) => {
13
14
  if (error instanceof Error) {
@@ -21,6 +22,26 @@ const okOutcome = (value) => {
21
22
  const errorOutcome = (error) => {
22
23
  return { error: serializeError(error), status: "error" };
23
24
  };
25
+ const compensateCompleted = async (deps, completed, error) => {
26
+ for (let cursor = completed.length - 1; cursor >= 0; cursor -= 1) {
27
+ const done = completed[cursor];
28
+ const compensateWith = done?.plan.item.compensateWith;
29
+ if (done === void 0 || compensateWith === void 0) {
30
+ continue;
31
+ }
32
+ await deps.step.do(`${COMPENSATE_STEP_PREFIX}${done.plan.childId}`, async () => {
33
+ const compensateId = `${done.plan.childId}:compensate`;
34
+ const compensationParams = {
35
+ branch: done.plan.item.workflow,
36
+ error,
37
+ index: done.plan.index,
38
+ output: done.output
39
+ };
40
+ await deps.resolveBinding(compensateWith).create({ id: compensateId, params: compensationParams });
41
+ return compensateId;
42
+ });
43
+ }
44
+ };
24
45
  const createParallel = (deps) => {
25
46
  const run = async (branches) => {
26
47
  if (branches.length === 0) {
@@ -46,6 +67,7 @@ const createParallel = (deps) => {
46
67
  )
47
68
  );
48
69
  const results = [];
70
+ const completed = [];
49
71
  for (const plan of planned) {
50
72
  const event = await deps.step.waitForEvent(`${AWAIT_STEP_PREFIX}${plan.childId}`, {
51
73
  timeout: plan.item.timeout,
@@ -53,8 +75,10 @@ const createParallel = (deps) => {
53
75
  });
54
76
  const outcome = event.payload;
55
77
  if (outcome.status === "error") {
78
+ await compensateCompleted(deps, completed, outcome.error);
56
79
  throw new NonRetryableError(`ctx.parallel: branch "${plan.item.workflow}" (#${String(plan.index)}) failed: ${outcome.error.message}`);
57
80
  }
81
+ completed.push({ output: outcome.value, plan });
58
82
  results.push(outcome.value);
59
83
  }
60
84
  return results;
@@ -1,5 +1,5 @@
1
1
  import { workflowBindingName } from './defineWorkflow-DbUC-oCN.mjs';
2
- import { createSpawn, createParallel } from './MAX_BRANCHES-C9MJIFii.mjs';
2
+ import { createSpawn, createParallel } from './MAX_BRANCHES-D6cQabJ1.mjs';
3
3
  import { createRunStep } from './createRunStep-8jOXxP2o.mjs';
4
4
 
5
5
  const createDispatchLogger = (prefix) => {
@@ -272,6 +272,16 @@ type WorkflowRunStepFunction = <A extends StepArgsValidator, Result>(step: StepD
272
272
  interface WorkflowBranch<Output = unknown> {
273
273
  /** Phantom marker for the branch output type — never present at runtime. */
274
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;
275
285
  /** Optional explicit child instance id (defaults to a deterministic parent-derived id). */
276
286
  readonly id?: string;
277
287
  /** The params the child instance is created with — surfaced as the child's `ctx.params`. */
@@ -281,6 +291,27 @@ interface WorkflowBranch<Output = unknown> {
281
291
  /** The `lunora/workflows.ts` export name of the child workflow to run. */
282
292
  readonly workflow: string;
283
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&lt;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
+ }
284
315
  /** Map a tuple of {@link WorkflowBranch}es to the tuple of their output types, preserving order. */
285
316
  type WorkflowBranchOutputs<B extends ReadonlyArray<WorkflowBranch>> = { -readonly [K in keyof B]: B[K] extends WorkflowBranch<infer Output> ? Output : never };
286
317
  /**
@@ -391,4 +422,4 @@ interface LunoraWorkflowsOptions {
391
422
  */
392
423
  bindings: Record<string, WorkflowBindingLike>;
393
424
  }
394
- export { ArgsOf as A, WorkflowSpawnFunction as B, WorkflowSpawnOptions as C, WorkflowStatusResult as D, WorkflowStepConfigLike as E, FunctionReference as F, WorkflowStepContextLike as G, WorkflowStepRollbackOptionsLike as H, InferStepArgs as I, 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 };
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 };
@@ -272,6 +272,16 @@ type WorkflowRunStepFunction = <A extends StepArgsValidator, Result>(step: StepD
272
272
  interface WorkflowBranch<Output = unknown> {
273
273
  /** Phantom marker for the branch output type — never present at runtime. */
274
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;
275
285
  /** Optional explicit child instance id (defaults to a deterministic parent-derived id). */
276
286
  readonly id?: string;
277
287
  /** The params the child instance is created with — surfaced as the child's `ctx.params`. */
@@ -281,6 +291,27 @@ interface WorkflowBranch<Output = unknown> {
281
291
  /** The `lunora/workflows.ts` export name of the child workflow to run. */
282
292
  readonly workflow: string;
283
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&lt;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
+ }
284
315
  /** Map a tuple of {@link WorkflowBranch}es to the tuple of their output types, preserving order. */
285
316
  type WorkflowBranchOutputs<B extends ReadonlyArray<WorkflowBranch>> = { -readonly [K in keyof B]: B[K] extends WorkflowBranch<infer Output> ? Output : never };
286
317
  /**
@@ -391,4 +422,4 @@ interface LunoraWorkflowsOptions {
391
422
  */
392
423
  bindings: Record<string, WorkflowBindingLike>;
393
424
  }
394
- export { ArgsOf as A, WorkflowSpawnFunction as B, WorkflowSpawnOptions as C, WorkflowStatusResult as D, WorkflowStepConfigLike as E, FunctionReference as F, WorkflowStepContextLike as G, WorkflowStepRollbackOptionsLike as H, InferStepArgs as I, 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 };
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.4",
3
+ "version": "1.0.0-alpha.5",
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",