UNPKG

@dudousxd/nestjs-durable

Version:

Durable workflows for NestJS — module, decorators, discovery and boot recovery

1 lines 196 kB
{"version":3,"sources":["../src/index.ts","../src/attributes-of.ts","../src/decorators.ts","../src/durable-start-client.ts","../src/durable-worker.module.ts","../src/discovery-helpers.ts","../src/durable.module.ts","../src/durable-step.registrar.ts","../src/role.ts","../src/entity.ts","../src/in-app-worker.ts","../src/proxy-run-gateway.ts","../src/retention-poller.ts","../src/run-request-responder.ts","../src/store-run-gateway.ts","../src/tenant-event-republisher.ts","../src/timer-poller.ts","../src/tokens.ts","../src/workflow.registrar.ts","../src/input-validation.ts","../src/step-interceptor.ts","../src/workflow.service.ts"],"sourcesContent":["export * from './attributes-of';\nexport * from './context-accessor';\nexport * from './decorators';\nexport { DurableStartClient } from './durable-start-client';\nexport * from './durable-worker.module';\nexport * from './durable.module';\nexport * from './entity';\nexport * from './in-app-worker';\nexport * from './proxy-run-gateway';\nexport * from './role';\nexport * from './run-request-responder';\nexport * from './step-interceptor';\nexport * from './store-run-gateway';\nexport * from './tenant-event-republisher';\nexport * from './tokens';\nexport * from './workflow.service';\n\n// Facade re-exports: the everyday `@dudousxd/nestjs-durable-core` surface a consumer touches\n// alongside this package's own decorators/module/tokens, so e.g. the `Workflow` decorator (here)\n// and `WorkflowEngine`/`WorkflowCtx` (core), or `RUN_GATEWAY` (here) and the `RunGateway` class\n// (core), no longer require importing both packages to pick up the paired symbol. Additive only —\n// re-exports only what already exists on core's public index; core's own new exports (e.g. typed\n// search-attribute helpers) are surfaced there, not duplicated here.\n//\n// Anything in core that is a CLASS — `RunGateway` and `WorkflowEngine` are both abstract classes\n// that double as their own DI token — has to be re-exported below as a VALUE. Putting one in the\n// `export type` block breaks consumers in a way neither `tsc` nor a build catches: the emitted\n// `.d.ts` rollup drops the `type` modifier, so the name type-checks as a value, while the JS\n// bundle correctly omits it — `import { RunGateway } from '@dudousxd/nestjs-durable'` compiles\n// green and is `undefined` at runtime, and `moduleRef.get(undefined)` fails only on the code path\n// that resolves it. Keep classes here, types above.\nexport type {\n  AttributeFilter,\n  EngineEvent,\n  InferSearchAttributes,\n  RunDetail,\n  RunListItem,\n  RunQuery,\n  RunStatus,\n  RunWaiting,\n  SearchAttributes,\n  SearchAttributesSchema,\n  StepCheckpoint,\n  StepEvent,\n  StepLogger,\n  WorkflowCtx,\n  WorkflowHandler,\n  WorkflowRun,\n} from '@dudousxd/nestjs-durable-core';\nexport { readSearchAttributes, RunGateway, WorkflowEngine } from '@dudousxd/nestjs-durable-core';\n","import {\n  type SearchAttributes,\n  type WorkflowClass,\n  type WorkflowCtx,\n  readSearchAttributes,\n} from '@dudousxd/nestjs-durable-core';\nimport { getWorkflowMeta } from './decorators';\n\n/**\n * The search-attributes shape a `@Workflow` class declares — extracted from its `run` method's\n * `ctx: WorkflowCtx<A>` annotation, mirroring the `WorkflowInputOf`/`WorkflowOutputOf` structural-\n * typing idiom in core's `workflow-ref.ts` (which extract `run`'s `input`/return type the same way).\n * A class whose `run` leaves `ctx` untyped (or types it as the bare `WorkflowCtx`) resolves to the\n * untyped `SearchAttributes` default — matching `WorkflowCtx` itself.\n */\nexport type WorkflowAttributesOf<C> = C extends abstract new (\n  ...args: never[]\n) => {\n  run(ctx: WorkflowCtx<infer A>, input: never): unknown;\n}\n  ? A\n  : SearchAttributes;\n\n/**\n * Read a run's search attributes **by workflow class**, with the schema resolved from that class's\n * `@Workflow({ searchAttributes })` decorator metadata — the same single-source-of-truth idiom as\n * triggering a workflow by class (`ctx.child(ShippingWorkflow, input)`, `engine.start(CheckoutWorkflow,\n * input)`): the decorator is the one place the schema lives, and every reader references the\n * WORKFLOW, never re-imports or re-declares the schema itself. The return type is inferred\n * structurally from the class's `run(ctx: WorkflowCtx<A>, …)` annotation (see\n * {@link WorkflowAttributesOf}), so a valid read is typed to `A` with no explicit type argument.\n *\n * Delegates to core's `readSearchAttributes(schema, run)` for the actual read, so the same lenient\n * safe-parse semantics apply: a run whose stored `searchAttributes` predate the schema, or fail it,\n * reads back as `{}` rather than throwing (see `readSearchAttributes`'s doc comment).\n *\n * @throws if `workflow` isn't a `@Workflow`-decorated class (no metadata at all — nothing to resolve\n * the schema from).\n * @throws if `workflow` is a `@Workflow` class that never declared a `searchAttributes` schema —\n * reading attributes by class needs one to resolve against.\n *\n * @example\n * ```ts\n * import { z } from 'zod';\n * import { Injectable } from '@nestjs/common';\n * import {\n *   attributesOf,\n *   InferSearchAttributes,\n *   Workflow,\n *   WorkflowCtx,\n *   WorkflowHandler,\n * } from '@dudousxd/nestjs-durable';\n *\n * const orderAttrs = z.object({ tier: z.enum(['free', 'pro']), amount: z.number() });\n * type OrderAttrs = InferSearchAttributes<typeof orderAttrs>;\n *\n * @Workflow({ name: 'checkout', searchAttributes: orderAttrs })\n * class CheckoutWorkflow implements WorkflowHandler<{ orderId: string }, void, OrderAttrs> {\n *   async run(ctx: WorkflowCtx<OrderAttrs>, input: { orderId: string }): Promise<void> {\n *     await ctx.upsertSearchAttributes({ tier: 'pro', amount: 100 });\n *   }\n * }\n *\n * @Injectable()\n * class CheckoutDashboardService {\n *   constructor(private readonly store: StateStore) {}\n *\n *   async tierOf(runId: string) {\n *     const run = await this.store.getRun(runId);\n *     const attrs = attributesOf(CheckoutWorkflow, run ?? {}); // OrderAttrs\n *     return attrs.tier;\n *   }\n * }\n * ```\n */\nexport function attributesOf<C extends WorkflowClass>(\n  workflow: C,\n  run: { searchAttributes?: SearchAttributes | null | undefined },\n): WorkflowAttributesOf<C> {\n  // biome-ignore lint/complexity/noBannedTypes: getWorkflowMeta reads reflect-metadata off the class target, same contract as workflowName/bindWorkflowClass\n  const meta = getWorkflowMeta(workflow as unknown as Function);\n  if (!meta) {\n    throw new Error(\n      `attributesOf: ${workflow.name} is not a @Workflow class — is it decorated with @Workflow({ name, searchAttributes })?`,\n    );\n  }\n  if (!meta.searchAttributes) {\n    throw new Error(\n      `attributesOf: workflow '${meta.name}' declares no searchAttributes schema — reading attributes by class requires the workflow to declare its schema: @Workflow({ name: '${meta.name}', searchAttributes: mySchema }).`,\n    );\n  }\n  return readSearchAttributes(meta.searchAttributes, run) as WorkflowAttributesOf<C>;\n}\n","import {\n  DURABLE_STEP_CONFIG,\n  DURABLE_STEP_NAME,\n  type SearchAttributesSchema,\n  type SingletonConfig,\n  type StepConfig,\n  type StepError,\n  WORKFLOW_NAME_KEY,\n  type WorkflowRef,\n} from '@dudousxd/nestjs-durable-core';\nimport 'reflect-metadata';\nimport type { z } from 'zod';\n\nexport const WORKFLOW_METADATA = Symbol('nestjs-durable:workflow');\n\nexport interface WorkflowMeta {\n  name: string;\n  version: string;\n  /** The workflow this workflow's dead runs route to (a name or a class). See `WorkflowOptions`. */\n  deadLetterWorkflow?: WorkflowRef | undefined;\n  /** Static searchable labels stamped on every run of this workflow. See `WorkflowOptions`. */\n  tags?: string[] | undefined;\n  /** Per-key serialization (a durable mutex). See `WorkflowOptions`. */\n  singleton?: SingletonConfig | undefined;\n  /** Max wall-clock lifetime before a run is cancelled (e.g. `'2h'`). See `WorkflowOptions`. */\n  executionTimeout?: string | number | undefined;\n  /** class-validator DTO validated at start. See `WorkflowOptions`. */\n  inputSchema?: (new (...args: any[]) => object) | undefined;\n  /** Custom input validator (throws on invalid). See `WorkflowOptions`. */\n  validateInput?: ((input: unknown) => void | Promise<void>) | undefined;\n  /** Typed/validated `searchAttributes` shape. See `WorkflowOptions`. */\n  searchAttributes?: SearchAttributesSchema | undefined;\n  /** Event names that start a fresh run of this workflow. See `WorkflowOptions`. */\n  onEvent?: string[] | undefined;\n  /** Debounce `onEvent` triggers — fire once it's quiet for this long. See `WorkflowOptions`. */\n  debounce?: string | number | undefined;\n  /** Batch `onEvent` triggers — fire on size or window. See `WorkflowOptions`. */\n  batch?: { maxSize: number; within: string | number } | undefined;\n  /** Capabilities a live worker must advertise to run this workflow's turns (handshake §7.5). See\n   *  `WorkflowOptions`. */\n  requires?: string[] | undefined;\n}\n\nexport interface WorkflowOptions {\n  name: string;\n  version?: string;\n  /**\n   * Route this workflow's dead-lettered runs to another **registered** workflow — by class\n   * (`deadLetterWorkflow: CheckoutDlqWorkflow`, refactor-safe) or by name for a cross-runtime\n   * handler. For a handler co-located on the same class, prefer an inline `@DeadLetter()` method\n   * instead — it takes precedence, and declaring both is a boot-time error. The handler receives a\n   * {@link DeadLetter} payload, idempotent by a `dlq:<runId>` id.\n   */\n  deadLetterWorkflow?: WorkflowRef;\n  /**\n   * Static labels stamped on **every run** of this workflow (e.g. `tags: ['etl', 'critical']`) and\n   * merged with any per-run tags passed to `start`. Searchable/filterable in the dashboard.\n   */\n  tags?: string[];\n  /**\n   * Serialize runs of this workflow that share a key — a durable FIFO mutex. e.g.\n   * `singleton: { key: (input) => `base:${input.baseId}` }` runs at most one pipeline per base at a\n   * time; same-key runs queue (suspended) and admit in creation order as slots free. `limit` (default\n   * 1) raises the concurrency.\n   */\n  singleton?: SingletonConfig;\n  /**\n   * Max wall-clock lifetime for a run of this workflow (e.g. `'2h'`, `'7 days'`, or ms). A run that\n   * outlives it is moved to `cancelled` (`execution_timeout`) by the timer poller — a backstop for\n   * runs that get stuck or loop forever. Omit for no limit.\n   */\n  executionTimeout?: string | number;\n  /**\n   * Capabilities a live worker MUST advertise to run this workflow's turns (handshake design §7.5).\n   * Only meaningful for a group-served / remote workflow (an in-app worker or a cross-runtime body):\n   * the control-plane dispatches a turn only to a capability-capable + protocol-compatible worker, and\n   * if descriptors are published on its group but none qualifies the run parks `blocked` until the\n   * recovery poll finds one. Absent/empty = \"runs anywhere\" (a legacy fleet skips the guard, §7.7).\n   */\n  requires?: string[];\n  /**\n   * Validate the workflow input at `start` against a **class-validator DTO** (the same\n   * `plainToInstance` + `validate` NestJS runs in controllers) — invalid input is rejected before any\n   * run is created. Needs the optional peers `class-validator` + `class-transformer`.\n   * `@Workflow({ inputSchema: CheckoutInput })`.\n   */\n  inputSchema?: new (\n    ...args: any[]\n  ) => object;\n  /**\n   * Custom input validator (throws on invalid) — an escape hatch for zod/yup/etc. instead of\n   * `inputSchema`. Takes precedence over `inputSchema` if both are set.\n   */\n  validateInput?: (input: unknown) => void | Promise<void>;\n  /**\n   * Validate this workflow's {@link WorkflowCtx.upsertSearchAttributes} writes against a **Standard\n   * Schema** (https://standardschema.dev — zod 3.24+, valibot, arktype, …). `ctx.upsertSearchAttributes`\n   * validates the MERGED result (existing attributes shallow-merged with the patch) on every call —\n   * an invalid merge throws, naming this workflow, the offending key(s), and the schema's issues. The\n   * schema's inferred output must be search-attribute-shaped (flat `string`/`number`/`boolean` values\n   * only) — a schema whose output has a nested object/array is a compile-time error here. Omit for\n   * the prior unvalidated behavior.\n   *\n   * ```ts\n   * import { z } from 'zod';\n   *\n   * const orderAttrs = z.object({\n   *   tier: z.enum(['free', 'pro']),\n   *   amount: z.number(),\n   * });\n   *\n   * @Workflow({ name: 'checkout', searchAttributes: orderAttrs })\n   * class CheckoutWorkflow {\n   *   async run(ctx: WorkflowCtx<InferSearchAttributes<typeof orderAttrs>>, input: CheckoutInput) {\n   *     await ctx.upsertSearchAttributes({ tier: input.tier, amount: input.total });\n   *   }\n   * }\n   * ```\n   */\n  searchAttributes?: SearchAttributesSchema;\n  /**\n   * Start a fresh run of this workflow whenever any of these events is published via\n   * `publishEvent(name, payload)` — the payload becomes the run's input. e.g.\n   * `onEvent: ['user.registered', 'user.invited']`. The same subscription can also be declared with\n   * the `@OnDurableEvent(...)` class decorator; the two are merged.\n   */\n  onEvent?: string[];\n  /**\n   * Coalesce `onEvent` triggers by **debouncing** — start one run only once events have been quiet\n   * for this long (resets on each event), with the LAST payload. e.g. `debounce: '30s'`.\n   */\n  debounce?: string | number;\n  /**\n   * Coalesce `onEvent` triggers by **batching** — start one run with all payloads (`{ events: [...] }`)\n   * once `maxSize` is reached or `within` elapses from the first event. e.g.\n   * `batch: { maxSize: 100, within: '10s' }`.\n   */\n  batch?: { maxSize: number; within: string | number };\n}\n\n/**\n * Marks a provider class as a durable workflow. Its `run(ctx, input)` method becomes the\n * workflow function the engine executes and replays.\n */\nexport function Workflow(options: WorkflowOptions): ClassDecorator {\n  return (target) => {\n    const meta: WorkflowMeta = {\n      name: options.name,\n      version: options.version ?? '1',\n      deadLetterWorkflow: options.deadLetterWorkflow,\n      tags: options.tags,\n      singleton: options.singleton,\n      executionTimeout: options.executionTimeout,\n      inputSchema: options.inputSchema,\n      validateInput: options.validateInput,\n      searchAttributes: options.searchAttributes,\n      onEvent: options.onEvent,\n      debounce: options.debounce,\n      batch: options.batch,\n      requires: options.requires,\n    };\n    Reflect.defineMetadata(WORKFLOW_METADATA, meta, target);\n    // Stamp the registered name so this class can be used as a typed workflow ref (ctx.child,\n    // engine.start, deadLetterWorkflow) and resolved back to its name via `workflowName`.\n    Object.defineProperty(target, WORKFLOW_NAME_KEY, {\n      value: options.name,\n      configurable: true,\n    });\n  };\n}\n\n// biome-ignore lint/complexity/noBannedTypes: matches reflect-metadata's class target type\nexport function getWorkflowMeta(target: Function): WorkflowMeta | undefined {\n  return Reflect.getMetadata(WORKFLOW_METADATA, target) as WorkflowMeta | undefined;\n}\n\nexport const DURABLE_STEP_METADATA = Symbol('nestjs-durable:step-handler');\n\nexport interface DurableStepMeta {\n  /** The resolved routing name — derived (`Class.method`) or explicit, always present. */\n  name: string;\n  /** Opt-in runtime input schema, validated when the handler is served (see `scanSteps`). Absent on\n   *  a bare `@Step()` — compile-time types from the method signature only, no runtime check. */\n  input?: z.ZodType | undefined;\n  /** Opt-in runtime output schema, validated before the handler's result is handed back. */\n  output?: z.ZodType | undefined;\n}\n\n/**\n * `@Step({ name?, input?, output?, retries?, backoff?, backoffMs?, backoffMaxMs?, jitter?,\n * timeoutMs? })` — the object call form. See {@link Step}.\n */\nexport interface StepOptions {\n  /** Explicit routing name, overriding the derived `Class.method`. */\n  name?: string;\n  /** Runtime input schema, validated at the serve boundary (opt-in — a bare `@Step()` skips it). */\n  input?: z.ZodType;\n  /** Runtime output schema, validated before the result is returned (opt-in). */\n  output?: z.ZodType;\n  /**\n   * Def-level durable-dispatch policy stamped under `DURABLE_STEP_CONFIG` and read by `ctx.step` at\n   * the dispatch boundary (see `stepConfigOf`) — a per-call `ctx.step(ref, input, opts)` overrides\n   * these field-by-field. Max attempts before the step (and run) fails.\n   */\n  retries?: number;\n  /** How the delay between retries grows: `fixed` (constant) or `exp` (doubles each attempt). */\n  backoff?: 'fixed' | 'exp';\n  /** Base delay in ms between retries. Omit (or 0) to retry with no delay. */\n  backoffMs?: number;\n  /** Upper bound on the (exponential) backoff delay. */\n  backoffMaxMs?: number;\n  /** Add random jitter (50–100% of the computed delay) to avoid thundering-herd retries. */\n  jitter?: boolean;\n  /**\n   * Liveness window for a dispatched step: no result/heartbeat within this many ms presumes the\n   * worker dead and fails the dispatch with a `RemoteStepTimeout` (retryable — re-dispatches per\n   * `retries`). Omit to wait indefinitely.\n   */\n  timeoutMs?: number;\n  /**\n   * Capabilities a live worker MUST advertise to run this step (handshake design §7.5). The\n   * control-plane routes the step only to workers whose descriptor advertises every name here; if\n   * descriptors are published on the step's group but none is capability-capable + protocol-compatible,\n   * the run parks `blocked` (never a silent hang) until the recovery poll finds one. Absent/empty =\n   * \"runs anywhere\" (the default — a legacy fleet publishing no descriptors skips the guard, §7.7). A\n   * per-call `ctx.step(ref, input, { requires })` overrides this.\n   */\n  requires?: string[];\n}\n\n/** Build the {@link StepConfig} to stamp under `DURABLE_STEP_CONFIG` from `@Step` options — omitting\n *  every unset field, so a bare `@Step()` (or one with only `name`/`input`/`output`) stamps `undefined`\n *  and leaves `ctx.step` reading nothing but a per-call `opts` override. */\nfunction stepConfigFrom(options: StepOptions): StepConfig | undefined {\n  const config: StepConfig = {\n    retries: options.retries,\n    backoff: options.backoff,\n    backoffMs: options.backoffMs,\n    backoffMaxMs: options.backoffMaxMs,\n    jitter: options.jitter,\n    timeoutMs: options.timeoutMs,\n    requires: options.requires,\n  };\n  const hasAnyField = Object.values(config).some((value) => value !== undefined);\n  return hasAnyField ? config : undefined;\n}\n\n/**\n * Marks a provider method as a durable step handler. An in-process transport (e.g. the\n * event-emitter transport) or a co-located/thin worker runtime routes a dispatched task to it BY\n * NAME — `ctx.step(this.svc.method, input)` reads that same name off the method reference (stamped\n * via the shared, cross-package `DURABLE_STEP_NAME` symbol from `@dudousxd/nestjs-durable-core`), so\n * there's no separately-declared def linking a call site to this handler. The method's single\n * argument is the step input (plus an optional `StepLogger` second arg); its return value is the\n * step output.\n *\n * Three call forms:\n * - `@Step()` — bare: the routing name is DERIVED from the method as `` `${ClassName}.${method}` ``\n *   (e.g. `ExtractionService.runExtractionPage`) — refactor-safe, no magic string.\n * - `@Step('custom:name')` — explicit name override (stable across refactors, or a cross-runtime\n *   contract with a non-JS worker that has no `@Step` of its own).\n * - `@Step({ name?, input?, output?, retries?, backoff?, backoffMs?, backoffMaxMs?, jitter?,\n *   timeoutMs? })` — optional name override, opt-in RUNTIME zod schemas, and a def-level\n *   durable-retry/liveness-timeout policy. `input` validates before the method runs; `output`\n *   validates its return value before it's handed back (see `scanSteps`). `retries`/`backoff`/\n *   `backoffMs`/`backoffMaxMs`/`jitter`/`timeoutMs` are the policy `ctx.step` reads off this method\n *   reference (via `stepConfigOf`) to build the dispatched `StepDef`'s durable retry/backoff and\n *   remote-liveness timeout — a per-call `ctx.step(ref, input, opts)` overrides them field-by-field.\n *   A bare `@Step()` carries none of this and skips validation/retry/timeout — compile-time types\n *   from the method signature are the only check, and the step dispatches with no retry/timeout.\n */\nexport function Step(nameOrOptions?: string | StepOptions): MethodDecorator {\n  return (target, propertyKey, descriptor: PropertyDescriptor) => {\n    const options =\n      typeof nameOrOptions === 'string' ? { name: nameOrOptions } : (nameOrOptions ?? {});\n    const derivedName = options.name ?? `${target.constructor.name}.${String(propertyKey)}`;\n    const meta: DurableStepMeta = {\n      name: derivedName,\n      input: options.input,\n      output: options.output,\n    };\n    Reflect.defineMetadata(DURABLE_STEP_METADATA, meta, descriptor.value as object);\n    // Stamp the SAME resolved name core's `ctx.step` reads off a method reference (`stepNameOf`) —\n    // the cross-package contract letting `ctx.step(this.svc.method, input)` route without a\n    // separately-declared def. `DURABLE_STEP_NAME` is core's `Symbol.for`-keyed constant, so a\n    // duplicate copy of core still reads back the same key (see `step-name-symbol.ts`).\n    (descriptor.value as { [DURABLE_STEP_NAME]?: string })[DURABLE_STEP_NAME] = derivedName;\n    // Stamp the def-level dispatch policy (retries/backoff/timeoutMs) core's `ctx.step` reads via\n    // `stepConfigOf`, under the same `Symbol.for`-keyed contract as `DURABLE_STEP_NAME`. Omitted\n    // entirely when no policy field is set, so a bare `@Step()` stamps nothing extra.\n    const config = stepConfigFrom(options);\n    if (config !== undefined) {\n      (descriptor.value as { [DURABLE_STEP_CONFIG]?: StepConfig })[DURABLE_STEP_CONFIG] = config;\n    }\n    return descriptor;\n  };\n}\n\n/**\n * @deprecated Use `@Step` instead. `@DurableStep` is a back-compat alias of {@link Step} and\n * writes the same `DURABLE_STEP_METADATA`, so discovery/registrars treat them identically.\n */\nexport const DurableStep = Step;\n\n// biome-ignore lint/complexity/noBannedTypes: reflect-metadata reads from the method function\nexport function getDurableStepMeta(method: Function): DurableStepMeta | undefined {\n  return Reflect.getMetadata(DURABLE_STEP_METADATA, method) as DurableStepMeta | undefined;\n}\n\nexport const DEAD_LETTER_METADATA = Symbol('nestjs-durable:dead-letter');\n\n/**\n * The payload a dead-letter handler receives: the dead run's id, its workflow, the input it was\n * started with (typed via `TInput`), and the failure that killed it.\n */\nexport interface DeadLetter<TInput = unknown> {\n  /** Id of the run that was dead-lettered (inspectable + retriable in the dashboard). */\n  deadRunId: string;\n  /** Name of the workflow whose run died. */\n  workflow: string;\n  /** The original input the dead run was started with. */\n  input: TInput;\n  /** The structured error that moved the run to `dead`, when known. */\n  error?: StepError;\n}\n\n/**\n * Marks a method on a `@Workflow` class as that workflow's **inline dead-letter handler**. When a\n * run of the workflow is moved to `dead` (exceeded `maxRecoveryAttempts`), this method runs — as a\n * durable workflow itself, auto-registered as `<workflow>.dlq` with a `dlq:<runId>` id — receiving a\n * {@link DeadLetter} payload. It shares the class's injected dependencies, so the handler lives in\n * the same file as the workflow it protects.\n *\n * Takes precedence over `@Workflow({ deadLetterWorkflow })` and the module-level `deadLetterWorkflow`\n * default. The method signature is `(ctx, dead)` — the same `ctx` a workflow `run` gets.\n */\nexport function DeadLetter(): MethodDecorator {\n  return (_target, _propertyKey, descriptor: PropertyDescriptor) => {\n    Reflect.defineMetadata(DEAD_LETTER_METADATA, true, descriptor.value as object);\n    return descriptor;\n  };\n}\n\n// biome-ignore lint/complexity/noBannedTypes: reflect-metadata reads from the method function\nexport function isDeadLetterHandler(method: Function): boolean {\n  return Reflect.getMetadata(DEAD_LETTER_METADATA, method) === true;\n}\n\nexport const ON_EVENT_METADATA = Symbol('nestjs-durable:on-event');\n\n/**\n * Subscribe a `@Workflow` class to one or more events: when any of them is published via\n * `publishEvent(name, payload)`, a fresh run of the workflow starts with the payload as input.\n * Equivalent to `@Workflow({ onEvent })` — use whichever reads better; multiple\n * `@OnDurableEvent(...)` decorators and the option are all merged.\n *\n * Named \"durable\" on purpose: `@nestjs/event-emitter` exports an `@OnEvent` decorator, and in an\n * app using both libs an auto-import picking the wrong one fails silently in either direction.\n */\nexport function OnDurableEvent(...events: string[]): ClassDecorator {\n  return (target) => {\n    const existing = (Reflect.getMetadata(ON_EVENT_METADATA, target) as string[] | undefined) ?? [];\n    Reflect.defineMetadata(ON_EVENT_METADATA, [...existing, ...events], target);\n  };\n}\n\n/** @deprecated Renamed to `OnDurableEvent` — this alias clashes with `@nestjs/event-emitter`'s `@OnEvent` and will be removed in the next minor. */\nexport const OnEvent = OnDurableEvent;\n\n/** All events a workflow class subscribes to — the union of `@Workflow({ onEvent })` and `@OnDurableEvent`. */\nexport function getOnEvents(meta: WorkflowMeta, target: object): string[] {\n  const fromDecorator =\n    (Reflect.getMetadata(ON_EVENT_METADATA, target) as string[] | undefined) ?? [];\n  return [...new Set([...(meta.onEvent ?? []), ...fromDecorator])];\n}\n","import { type StartRunDeps, startRun } from '@dudousxd/durable-worker';\nimport {\n  type RunResult,\n  type StartOptions,\n  type WorkflowClass,\n  type WorkflowInputOf,\n  workflowName,\n} from '@dudousxd/nestjs-durable-core';\nimport { Injectable } from '@nestjs/common';\nimport type { DurableModuleOptions } from './durable.module';\n\n/**\n * The **store-less `engine.start` facade** for a tenant worker. Provided under the `WorkflowEngine`\n * DI token by {@link import('./durable.module').DurableModule} (`forRoot({ connection })`, no\n * `store`), so tenant code calls `engine.start(...)` UNCHANGED — it has no idea it is a tenant.\n * Instead of touching a DB, `start` publishes a `StartRunMessage` on the SHARED `durable-start-run`\n * queue (Option B: tenant rides as message DATA, never as wire segmentation). The operator (control\n * plane, `namespace: undefined`) consumes it, stamps the run's namespace from `tenant`, and routes\n * the run's task to `<workflow>@<tenant>` — the partition THIS tenant's worker serves.\n *\n * `cancel`/`deleteRun` need the store/driver a tenant does not have; they throw. No wire message\n * exists for them (the operator owns cancellation/retention).\n */\n@Injectable()\nexport class DurableStartClient {\n  private readonly tenant: string;\n\n  constructor(\n    private readonly options: DurableModuleOptions,\n    private readonly deps?: StartRunDeps,\n  ) {\n    this.tenant = options.partition ?? 'default';\n  }\n\n  start<C extends WorkflowClass>(\n    workflow: C,\n    input: WorkflowInputOf<C>,\n    runId?: string,\n    opts?: StartOptions,\n  ): Promise<RunResult>;\n  start(workflow: string, input: unknown, runId?: string, opts?: StartOptions): Promise<RunResult>;\n  async start(\n    workflow: string,\n    input: unknown,\n    runId: string = globalThis.crypto.randomUUID(),\n    opts?: StartOptions,\n  ): Promise<RunResult> {\n    const name = workflowName(workflow);\n    await startRun(this.options.connection, {\n      tenant: this.tenant,\n      workflow: name,\n      input,\n      runId,\n      // Option B: DO NOT pass `namespace` — the start-run queue stays the shared\n      // `durable-start-run` the operator consumes; tenant rides only as message data.\n      ...(this.options.prefix !== undefined ? { prefix: this.options.prefix } : {}),\n      ...(opts?.tags !== undefined ? { tags: opts.tags } : {}),\n      ...(opts?.searchAttributes !== undefined ? { searchAttributes: opts.searchAttributes } : {}),\n      ...(this.deps !== undefined ? { deps: this.deps } : {}),\n    });\n    return { runId, status: 'pending' };\n  }\n\n  cancel(_runId: string): Promise<void> {\n    return tenantUnsupported('cancel');\n  }\n\n  deleteRun(_runId: string): Promise<void> {\n    return tenantUnsupported('deleteRun');\n  }\n\n  // — everything below rejects: a tenant worker holds no store/driver, only the start channel. —\n\n  // The remaining WorkflowEngine surface WorkflowService delegates to (resume/waitForRun/signal/\n  // signalWithStart/publishEvent) all need the store or driver a tenant does not have. A tenant only\n  // ever calls `start`; these exist so a mistaken call fails with a CLEAR, named tenant error instead\n  // of a cryptic `this.engine.X is not a function` — the facade is honest about what it cannot do.\n  // Params mirror the WorkflowEngine surface being faced; every method rejects without reading them.\n  resume(_runId: string): Promise<void> {\n    return tenantUnsupported('resume');\n  }\n\n  waitForRun(_runId: string, _opts?: { timeoutMs?: number }): Promise<void> {\n    return tenantUnsupported('waitForRun');\n  }\n\n  signal(_token: string, _payload: unknown): Promise<void> {\n    return tenantUnsupported('signal');\n  }\n\n  signalWithStart(\n    _workflow: string,\n    _input: unknown,\n    _runId: string,\n    _signal: { token: string; payload?: unknown },\n    _opts?: StartOptions,\n  ): Promise<void> {\n    return tenantUnsupported('signalWithStart');\n  }\n\n  publishEvent(\n    _name: string,\n    _payload: unknown,\n    _opts?: { id?: string; buffer?: boolean },\n  ): Promise<void> {\n    return tenantUnsupported('publishEvent');\n  }\n}\n\n/** Reject with a clear, named tenant error for an operation that needs a store/driver. */\nfunction tenantUnsupported(method: string): Promise<void> {\n  return Promise.reject(\n    new Error(\n      `${method}() is not available on a tenant worker (no store). Use the control plane for it.`,\n    ),\n  );\n}\n","import {\n  DurableWorkerRuntime,\n  type RunRedisWorkerOptions,\n  type RunningWorker,\n  runRedisWorker as defaultRunRedisWorker,\n} from '@dudousxd/durable-worker';\nimport {\n  DURABLE_OPTIONS_CANONICAL,\n  type DurableTopology,\n  type EngineEvent,\n  type GroupHealth,\n  type RunDetail,\n  type RunGateway,\n  type RunListItem,\n  type RunQuery,\n  type RunResult,\n  type RunWaiting,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n  Inject,\n  Injectable,\n  type OnApplicationBootstrap,\n  type OnApplicationShutdown,\n  type OnModuleInit,\n  type Provider,\n} from '@nestjs/common';\nimport { DiscoveryService, MetadataScanner } from '@nestjs/core';\nimport { scanSteps, scanWorkflows } from './discovery-helpers';\nimport type { DurableModuleOptions } from './durable.module';\n\n/**\n * The `runRedisWorker` function the module uses to start each partition's BullMQ consumer.\n * Defaults to the real one from `@dudousxd/durable-worker`; tests `overrideProvider` it with a\n * fake so no real Redis is needed.\n */\nexport const RUN_REDIS_WORKER = Symbol('nestjs-durable:run-redis-worker');\n\n/** The list of started {@link RunningWorker} handles (the single handle {@link ThinWorkerBootstrap}\n *  starts), closed on shutdown. */\nexport const DURABLE_WORKER_RUNNERS = Symbol('nestjs-durable:worker-runners');\n\n/** The signature of `runRedisWorker` — injected behind {@link RUN_REDIS_WORKER}. */\nexport type RunRedisWorkerFn = (opts: RunRedisWorkerOptions) => Promise<RunningWorker>;\n\n/** True for the **pure thin-worker** role — `connection` set, `store` unset. `DurableModule`\n *  keeps {@link ThinWorkflowRegistrar}/{@link ThinStepRegistrar}/{@link ThinWorkerBootstrap} inert\n *  (registered but no-op) outside this role — an operator (`store` set) drives its own bodies\n *  inline or, with `connection` also set, through the separate in-app-worker mechanics instead. */\nfunction isPureThinWorker(options: DurableModuleOptions): boolean {\n  return options.store === undefined && options.connection !== undefined;\n}\n\n/**\n * Discovers every provider carrying `@Workflow` metadata and registers its `run(ctx, input)` on the\n * thin {@link DurableWorkerRuntime}. Mirrors the engine-side `WorkflowRegistrar`, but registers on\n * the runner-core runtime instead of a `WorkflowEngine` — the runtime drives `run` with the thin\n * `WorkflowContext` (which `implements WorkflowCtx`), so the body runs unchanged. NO store/engine.\n * Inert (skips registration) outside the pure thin-worker role — see {@link isPureThinWorker}.\n */\n@Injectable()\nexport class ThinWorkflowRegistrar implements OnModuleInit {\n  constructor(\n    private readonly discovery: DiscoveryService,\n    private readonly runtime: DurableWorkerRuntime,\n    @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n  ) {}\n\n  onModuleInit(): void {\n    if (!isPureThinWorker(this.options)) return;\n    scanWorkflows(this.discovery, (meta, instance) =>\n      this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input)),\n    );\n  }\n}\n\n/**\n * Discovers every `@Step` method and registers it as a step handler on the thin\n * {@link DurableWorkerRuntime}. Mirrors the engine-side `DurableStepRegistrar`, but always registers\n * on the runtime (a thin worker IS the consumer — there is no in-process-vs-queue branch). Inert\n * outside the pure thin-worker role — see {@link isPureThinWorker}.\n */\n@Injectable()\nexport class ThinStepRegistrar implements OnModuleInit {\n  constructor(\n    private readonly discovery: DiscoveryService,\n    private readonly metadataScanner: MetadataScanner,\n    private readonly runtime: DurableWorkerRuntime,\n    @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n  ) {}\n\n  onModuleInit(): void {\n    if (!isPureThinWorker(this.options)) return;\n    scanSteps(this.discovery, this.metadataScanner, (meta, handler) =>\n      this.runtime.registerStep(meta.name, handler),\n    );\n  }\n}\n\n/**\n * Starts ONE `runRedisWorker` call on bootstrap — after both registrars have run, so every handler\n * is registered before any task is consumed — and closes it on shutdown. The runner (`@dudousxd/\n * durable-worker`) derives its subscription from `runtime.registeredNames()` (one queue per\n * registered `@Workflow`/`@Step` name), not a hand-declared group list. Inert outside the pure\n * thin-worker role — see {@link isPureThinWorker}: an operator (`store` set) never starts this\n * consumer, and `store + connection` starts its OWN co-located consumer instead (`in-app-worker.ts`).\n *\n * For the shutdown close to fire, enable Nest's shutdown hooks: `app.enableShutdownHooks()`.\n */\n@Injectable()\nexport class ThinWorkerBootstrap implements OnApplicationBootstrap, OnApplicationShutdown {\n  private readonly runners: RunningWorker[] = [];\n\n  constructor(\n    private readonly runtime: DurableWorkerRuntime,\n    @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n    @Inject(RUN_REDIS_WORKER) private readonly runRedisWorker: RunRedisWorkerFn,\n    @Inject(DURABLE_WORKER_RUNNERS) private readonly runnersSink: RunningWorker[],\n  ) {}\n\n  async onApplicationBootstrap(): Promise<void> {\n    const options = this.options;\n    if (!isPureThinWorker(options) || options.connection === undefined) return;\n    // ONE call: the runner subscribes one queue per name registered on `this.runtime` — populated by\n    // `ThinWorkflowRegistrar`/`ThinStepRegistrar`'s `onModuleInit`, which Nest's lifecycle guarantees\n    // run (across the whole app) before any `onApplicationBootstrap` hook.\n    const handle = await this.runRedisWorker({\n      runtime: this.runtime,\n      connection: options.connection,\n      ...(options.partition !== undefined ? { partition: options.partition } : {}),\n      ...(options.prefix !== undefined ? { prefix: options.prefix } : {}),\n      ...(options.instanceId !== undefined ? { instanceId: options.instanceId } : {}),\n      ...(options.concurrency !== undefined ? { concurrency: options.concurrency } : {}),\n    });\n    this.runners.push(handle);\n    this.runnersSink.push(handle);\n  }\n\n  async onApplicationShutdown(): Promise<void> {\n    await Promise.allSettled(this.runners.map((h) => h.close()));\n  }\n}\n\n/**\n * The thin-worker slice of {@link import('./durable.module').DurableModule}'s unified provider set —\n * active for the pure thin-worker role (`connection` set, `store` unset): registers discovered\n * `@Workflow`/`@Step` on a store-less {@link DurableWorkerRuntime} and starts ONE `runRedisWorker`\n * consumer, one queue PER REGISTERED NAME. Always present in the provider list (so a single\n * `DurableModule.forRootAsync` can serve any role once its options resolve); every class/factory here\n * is inert (see {@link isPureThinWorker}) outside that role.\n */\nexport function thinWorkerProviders(): Provider[] {\n  return [\n    {\n      provide: DurableWorkerRuntime,\n      // The runtime's `WorkflowWorker` falls back to its own `group` ctor param as the WORKFLOW's\n      // `workflowPartition` for any `ctx.step` call (see `workflow-context.ts`'s `resolveCallGroup`)\n      // — that fallback MUST equal this module's own `partition`, or a dispatched step's decision\n      // carries a mismatched token and the engine dispatches it to a queue nothing here subscribes\n      // to. Explicit `''` (never\n      // `undefined`) so it does NOT fall through to `WorkflowWorker`'s unrelated `'workflows'`\n      // default parameter.\n      useFactory: (options: DurableModuleOptions) =>\n        new DurableWorkerRuntime({ workflowGroup: options.partition ?? '' }),\n      inject: [DURABLE_OPTIONS_CANONICAL],\n    },\n    { provide: RUN_REDIS_WORKER, useValue: defaultRunRedisWorker },\n    { provide: DURABLE_WORKER_RUNNERS, useValue: [] as RunningWorker[] },\n    ThinWorkflowRegistrar,\n    ThinStepRegistrar,\n    ThinWorkerBootstrap,\n  ];\n}\n\n/** Reject with a clear, named tenant error for a `RunGateway` call made without a `transport`. The\n *  call site pins `T` from its own declared return type (e.g. `Promise<RunDetail | null>`); this\n *  helper only ever rejects, so it never actually produces a `T` value. */\nfunction tenantGatewayUnavailable<T>(method: string): Promise<T> {\n  return Promise.reject(\n    new Error(`RunGateway.${method}() is not available — pass \\`transport\\` to use RunGateway.`),\n  );\n}\n\n/**\n * The no-transport fallback bound to `RUN_GATEWAY` for a thin worker (`connection` set, `store`\n * unset) that didn't also pass a `transport` — mirrors `DurableStartClient`'s tenant-error idiom\n * (`tenantUnsupported`): every method rejects with a clear, named error instead of a cryptic\n * `this.gateway.X is not a function`. `subscribe` is synchronous on the `RunGateway` port, so it\n * throws synchronously (same message) rather than returning a rejected promise.\n */\nexport function unavailableRunGateway(): RunGateway {\n  return {\n    // Topology is local metadata, always answerable — a thin worker with no transport is still a tenant.\n    topology(): DurableTopology {\n      return { role: 'tenant' };\n    },\n    getRunDetail(_runId: string): Promise<RunDetail | null> {\n      return tenantGatewayUnavailable<RunDetail | null>('getRunDetail');\n    },\n    listRuns(_query: RunQuery): Promise<RunListItem[]> {\n      return tenantGatewayUnavailable<RunListItem[]>('listRuns');\n    },\n    waitingFor(_runIds: string[]): Promise<Record<string, RunWaiting>> {\n      return tenantGatewayUnavailable<Record<string, RunWaiting>>('waitingFor');\n    },\n    workerHealth(): Promise<GroupHealth[]> {\n      return tenantGatewayUnavailable<GroupHealth[]>('workerHealth');\n    },\n    cancel(_runId: string): Promise<RunResult | null> {\n      return tenantGatewayUnavailable<RunResult | null>('cancel');\n    },\n    retry(_runId: string): Promise<RunResult | null> {\n      return tenantGatewayUnavailable<RunResult | null>('retry');\n    },\n    continue(_runId: string): Promise<RunResult | null> {\n      return tenantGatewayUnavailable<RunResult | null>('continue');\n    },\n    retryWithInput(_runId: string, _input: unknown): Promise<{ runId: string } | null> {\n      return tenantGatewayUnavailable<{ runId: string } | null>('retryWithInput');\n    },\n    redispatchPending(_runId: string): Promise<(RunResult & { redispatched: number }) | null> {\n      return tenantGatewayUnavailable<(RunResult & { redispatched: number }) | null>(\n        'redispatchPending',\n      );\n    },\n    subscribe(_runId: string, _onEvent: (event: EngineEvent) => void): () => void {\n      throw new Error(\n        'RunGateway.subscribe() is not available — pass `transport` to use RunGateway.',\n      );\n    },\n  };\n}\n","import type { StepLogger, WorkflowCtx } from '@dudousxd/nestjs-durable-core';\nimport { DiscoveryService, MetadataScanner } from '@nestjs/core';\nimport { getDurableStepMeta, getWorkflowMeta } from './decorators';\nimport type { DurableStepMeta, WorkflowMeta } from './decorators';\n\nexport interface WorkflowInstance {\n  run(ctx: WorkflowCtx, input: unknown): Promise<unknown>;\n}\n\n/**\n * Walks every provider that carries `@Workflow` metadata and invokes `register(meta, instance)`.\n *\n * Guards applied (same as both registrars):\n *   - null / non-object provider instances are skipped\n *   - providers without `@Workflow` metadata are skipped\n *   - a `@Workflow` provider that has no `run` method throws at boot\n */\nexport function scanWorkflows(\n  discovery: DiscoveryService,\n  register: (meta: WorkflowMeta, instance: WorkflowInstance) => void,\n): void {\n  for (const wrapper of discovery.getProviders()) {\n    const { instance } = wrapper;\n    if (!instance || typeof instance !== 'object') continue;\n    const meta = getWorkflowMeta(instance.constructor);\n    if (!meta) continue;\n    const workflow = instance as WorkflowInstance;\n    if (typeof workflow.run !== 'function') {\n      throw new Error(`@Workflow ${meta.name} must define a run(ctx, input) method`);\n    }\n    register(meta, workflow);\n  }\n}\n\n/**\n * Walks every provider method that carries `@Step` metadata and invokes\n * `register(meta, boundHandler)`.\n *\n * Guards applied (same as both registrars):\n *   - null / non-object provider instances are skipped\n *   - non-function methods are skipped\n *   - methods without `@Step` metadata are skipped\n *\n * The handler passed to `register` is already bound to its instance, and is the ONE place a `@Step`\n * method actually runs with real input/output in this package — every registrar\n * (`DurableStepRegistrar`, `InAppWorkerBootstrap`, `ThinStepRegistrar`) funnels through here — so\n * this is where `@Step`'s optional `input`/`output` zod schemas are enforced: `input` validates\n * before the method runs, `output` validates its return value before it's handed back. A bare\n * `@Step()` (no schemas attached) skips both checks — `meta.input`/`meta.output` are `undefined`.\n */\nexport function scanSteps(\n  discovery: DiscoveryService,\n  scanner: MetadataScanner,\n  register: (\n    meta: DurableStepMeta,\n    handler: (input: unknown, log: StepLogger) => Promise<unknown>,\n  ) => void,\n): void {\n  for (const wrapper of discovery.getProviders()) {\n    const { instance } = wrapper;\n    if (!instance || typeof instance !== 'object') continue;\n    const prototype = Object.getPrototypeOf(instance);\n    for (const methodName of scanner.getAllMethodNames(prototype)) {\n      const method = (instance as Record<string, unknown>)[methodName];\n      if (typeof method !== 'function') continue;\n      const meta = getDurableStepMeta(method);\n      if (!meta) continue;\n      const boundMethod = method as (input: unknown, log: StepLogger) => unknown;\n      const handler = async (input: unknown, log: StepLogger): Promise<unknown> => {\n        const validInput = meta.input ? meta.input.parse(input) : input;\n        const output = await boundMethod.call(instance, validInput, log);\n        return meta.output ? meta.output.parse(output) : output;\n      };\n      register(meta, handler);\n    }\n  }\n}\n","import type { ConcurrencyOption } from '@dudousxd/durable-worker';\nimport {\n  type AdmissionBackend,\n  type ControlPlane,\n  DURABLE_OPTIONS,\n  DURABLE_OPTIONS_CANONICAL,\n  type NamedTransport,\n  type QueueConfig,\n  type RetentionPolicy,\n  RunGateway,\n  STATE_STORE,\n  STATE_STORE_CANONICAL,\n  type ScheduledWorkflow,\n  type StateStore,\n  TRANSPORT,\n  TRANSPORT_CANONICAL,\n  type Transport,\n  WorkflowEngine,\n  type WorkflowRef,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n  type DynamicModule,\n  Inject,\n  Injectable,\n  type InjectionToken,\n  Module,\n  type OnApplicationBootstrap,\n  type OnModuleDestroy,\n  type Provider,\n} from '@nestjs/common';\nimport { DiscoveryModule, ModuleRef } from '@nestjs/core';\nimport type { ContextAccessor } from './context-accessor';\nimport { DurableStartClient } from './durable-start-client';\nimport { DurableStepRegistrar } from './durable-step.registrar';\nimport { thinWorkerProviders, unavailableRunGateway } from './durable-worker.module';\nimport { EntityService } from './entity';\nimport { inAppWorkerProviders } from './in-app-worker';\nimport { ProxyRunGateway } from './proxy-run-gateway';\nimport { RetentionPoller } from './retention-poller';\nimport { isDrivingOperator } from './role';\nimport { RunRequestResponder, type RunRequestTransport } from './run-request-responder';\nimport { StoreRunGateway } from './store-run-gateway';\nimport { TenantEventRepublisher } from './tenant-event-republisher';\nimport { TimerPoller } from './timer-poller';\nimport { CONTEXT_ACCESSOR } from './tokens';\nimport { WorkflowRegistrar } from './workflow.registrar';\nimport { WorkflowService } from './workflow.service';\n\n/**\n * Locate the context accessor non-strictly: an accessor provided by ANY module (a global\n * `ContextModule` from `@dudousxd/nestjs-context`, or the app root) is found via {@link ModuleRef},\n * because DurableModule is `global` and its engine factory can't see another module's local\n * providers directly. Returns undefined when the optional peer isn't installed/bound.\n */\nfunction resolveAccessor(moduleRef: ModuleRef): ContextAccessor | undefined {\n  try {\n    return moduleRef.get<ContextAccessor>(CONTEXT_ACCESSOR, { strict: false });\n  } catch {\n    return undefined;\n  }\n}\n\n/**\n * Build an opaque context carrier from a {@link ContextAccessor} — the auto-feed default for the\n * engine's `context` option when `@dudousxd/nestjs-context` is present and the app passed no own\n * reader. Drops fields the accessor doesn't populate, so an empty/anonymous request yields `{}`.\n */\nfunction carrierFromAccessor(accessor: ContextAccessor): Record<string, unknown> | undefined {\n  const carrier: Record<string, unknown> = {};\n  const traceId = accessor.traceId();\n  if (traceId !== undefined) carrier.traceId = traceId;\n  const tenantId = accessor.tenantId();\n  if (tenantId !== undefined) carrier.tenantId = tenantId;\n  const userRef = accessor.userRef();\n  if (userRef !== undefined) carrier.userRef = userRef;\n  return carrier;\n}\n\n/**\n * The slice of `@dudousxd/nestjs-context`'s module-level `Context` singleton we use to re-hydrate the\n * originating context around a local step body. Structural — we never import the package's types (it\n * is an OPTIONAL peer); the guarded dynamic import below captures whatever the installed package\n * exports as `Context`.\n */\ninterface ContextRuntime {\n  /** Re-hydrate a context from an opaque carrier and run `fn` inside its ALS scope. */\n  deserialize<T>(carrier: Record<string, unknown>, fn: () => T): T;\n}\n\nfunction isContextRuntime(x: unknown): x is ContextRuntime {\n  return !!x && typeof (x as ContextRuntime).deserialize === 'function';\n}\n\n/**\n * Resolve `@dudousxd/nestjs-context`'s runtime `Context` singleton via a GUARDED dynamic import — used\n * to build the engine's `rehydrate` bridge. The runtime `Context` (its `deserialize`/ALS `run`) is a\n * module-level singleton, NOT a DI provider behind {@link CONTEXT_ACCESSOR}, so it can't be resolved\n * through ModuleRef — we import it directly. We do NOT add a hard/static import (the peer is optional):\n * a failure (peer not installed) returns undefined and the engine falls back to passthrough re-hydration.\n */\nasync function resolveContextRuntime(): Promise<ContextRuntime | undefined> {\n  try {\n    // Indirect the specifier through a variable so the compiler does not try to STATICALLY resolve\n    // the optional peer (it may be uninstalled). This stays a runtime guarded import — a missing\n    // module rejects and is caught below.\n    const specifier = '@dudousxd/nestjs-context';\n    const mod = (await import(specifier)) as { Context?: unknown };\n    return isContextRuntime(mod.Context) ? mod.Context : undefined;\n  } catch {\n    return undefined;\n  }\n}\n\n/** True when `x` can act as a control plane (broadcast pub/sub) — e.g. a broadcast-capable transport. */\nfunction isControlPlane(x: unknown): x is ControlPlane {\n  return (\n    !!x &&\n    typeof (x as ControlPlane).publishControl === 'function' &&\n    typeof (x as ControlPlane).onControl === 'function'\n  );\n}\n\n/** A store that can derive a tenant-namespace-scoped view of itself (the MikroORM adapter). */\ninterface ScopeableStore {\n  /** Derive a store confined to `scope.namespace`, sharing the same backing connection. */\n  withScope(scope: { namespace?: string }): StateStore;\n}\n\n/**\n * True when `store` can produce a namespace-scoped view of itself. Structural check (same idiom as\n * {@link isControlPlane}) so the store-agnostic module never imports a concrete adapter — the\n * pre-built `store` is only re-scoped when it actually carries the capability.\n */\nfunction isScopeableStore(store: StateStore): store is StateStore & ScopeableStore {\n  return typeof (store as StateStore & Partial<ScopeableStore>).withScope === 'function';\n}\n\n/**\n * Apply opt-in tenant read scoping to a pre-built store. The module receives `store` already\n * instantiated (it is store-agnostic — no MikroORM import), so it cannot reconstruct it; instead it\n * asks the store for a scoped view via the optional {@link ScopeableStore.withScope} capability. When\n * `scopeReads` is off, `namespace` is unset, or the store can't scope itself, the original store is\n * returned unchanged (the operator view) — the caller can always pass an already-scoped store.\n */\nfunction scopedStore(store: StateStore, options: DurableModuleOptions): StateStore {\n  if (options.scopeReads !== true || options.namespace === undefined) return store;\n  if (!isScopeableStore(store)) return store;\n  return store.withScope({ namespace: options.namespace });\n}\n\n/**\n * Retention config for the {@link RetentionPoller}. One or more {@link RetentionPolicy policies}\n * (status sets must be disjoint — validated at boot), swept together on a shared interval.\n *\n * ```ts\n * retention: {\n *   sweepInterval: '1m',\n *   batchSize: 1_000,\n *   policies: [\n *     { statuses: ['completed', 'cancelled'], maxAge: '14d', maxCount: 200 },\n *     { statuses: ['failed'], maxAge: '90d' }, // keep failures longer for debugging\n *   ],\n * }\n * ```\n */\nexport interface DurableRetentionOptions {\n  /** The retention rules, one per (disjoint) status group. */\n  policies: RetentionPolicy[];\n  /**\n   * How often to run the prune sweep. A number is milliseconds; a string is an `ms`-style duration\n   * (`'1m'`, `'5m'` — `'m'` is minutes). `0` runs it once on boot only. Defaults to 60000 (1 minute).\n   */\n  sweepInterval?: number | string;\n  /** Max runs hard-deleted per batch (per policy, looped until drained). Defaults to 1000. */\n  batchSize?: number;\n}\n\n/**\n * Options for `DurableModule.forRoot`/`forRootAsync`. The **role is inferred** from which of `store`/\n * `connection` are set:\n *\n * - `{ store, transport }` — **operator**: a real `WorkflowEngine` + `StoreRunGateway` + drivers/\n *   timer/retention/registrars, executing registered bodies INLINE. Driven by {@link drive} (default\n *   `true`).\n * - `{ connection }` (no `store`) — **thin worker**: `WorkflowEngine` resolves to a store-less\n *   `DurableStartClient`; `RUN_GATEWAY` is a `ProxyRunGateway` when `transport` is also given, else\n *   every method rejects with a clear error. No store/timer/retention/entity — just discovered\n *   `@Workflow`/`@Step` handlers served over ONE `runRedisWorker` consumer.\n * - `{ store, transport, connection }` — an **operator that also runs a co-located worker**: every\n *   `@Workflow` is registered GROUP-SERVED (dispatched over the transport, PER WORKFLOW NAME, instead\n *   of inline) and a co-located consumer (one `runRedisWorker` call) replays the same bodies.\n *\n * `forRoot` throws when neither `store` nor `connection` is set, and when `store` is set without a\n * `transport`.\n *\n * Set {@link DurableModuleOptions.topology} to name the role explicitly instead of relying on this\n * inference — it additionally VALIDATES the axes above (`namespace` vs `partition`) that this prose\n * is otherwise the only source of truth for. Omitting it changes nothing.\n */\nexport interface DurableModuleOptions {\n  /** State store — set this to play the **operator** role (see the interface doc for the full role\n   *  matrix). Omit for a store-less **thin worker** (`connection` only). */\n  store?: StateStore;\n  transport?: Transport;\n  /**\n   * An ordered pool of named transports for failover / multi-broker setups. The engine dispatches on\n   * the first and fails over to the next; a step pins one via `ctx.step(handler, input, { transport })`.\n   * Use instead of `transport`.\n   */\n  transports?: NamedTransport[];\n  /**\n   * Cross-instance broadcast pub/sub (lifecycle events + cancellation). Defaults to the (first)\n   * transport when it can broadcast (event-emitter, BullMQ); set explicitly to use a dedicated one.\n   */\n  controlPlane?: ControlPlane;\n  /** Interval (ms) for the durable-timer poller. `0` disables it. Defaults to 1000. Operator only. */\n  timerPollMs?: number;\n  /**\n   * Auto-create the durable tables on boot via `store.ensureSchema()`. Defaults to true. Turn\n   * off in production and call the store adapter's `ensure*DurableSchema()` from a migration.\n   * Operator only.\n   */\n  autoSchema?: boolean;\n  /**\n   * Worker-pool namespace for this instance (forwarded to the engine). The poll paths\n   * (`runPending`/`recoverIncomplete`/`resumeDueTimers`/`sweepTimeouts`) only act on runs in this\n   * namespace. Set distinct values to safely share ONE state store across non-interchangeable\n   * pools — e.g. a developer's local instance vs the deployed cluster. **Omit it to make this\n   * instance an OPERATOR** — an unset namespace drives/recovers/resumes runs of EVERY namespace and\n   * leaves the transport on its bare prefix. See `WorkflowEngineDeps.namespace`. Not to be confused\n   * with {@link partition} (the co-located/thin-worker QUEUE routing suffix).\n   */\n  namespace?: string;\n  /**\n   * Multi-instance recovery lease, in ms — how long an instance owns a run it picked up before\n   * another may take over. Defaults to 30000. Set above your longest synchronous run. Operator only.\n   */\n  leaseMs?: number;\n  /** Unique id for this instance (for leases, and the co-located/thin-worker consumer's heartbeats). */\n  instanceId?: string;\n  /**\n   * Cap recovery attempts before a still-`running` run is moved to the `dead` dead-letter state\n   * (a poison pill that crashes the process every boot). Omit for unlimited. Operator only.\n   */\n  maxRecoveryAttempts?: number;\n  /**\n   * Opt-in liveness deadline (ms) for a remote (polyglot) workflow `advance`. If the worker neither\n   * returns a decision nor sends a run-scoped heartbeat within this window, the engine presumes it dead\n   * and lets recovery re-drive; each heartbeat rearms the window so a slow-but-alive worker is never\n   * re-driven. Pair with a worker SDK that emits run-scoped heartbeats (`@dudousxd/durable-worker` ≥ the\n   * release that ships them, and the Python `durable-worker`). Omit for the prior unbounded await.\n   * Operator only.\n   */\n  remoteAdvanceSilenceMs?: number;\n  /**\n   * The **default** workflow to route dead-lettered runs to, for workflows that don't declare their\n   * own. When a run is moved to `dead` (exceeded `maxRecoveryAttempts`), the started handler gets a\n   * `DeadLetter` payload `{ deadRunId, workflow, input, error }` (idempotent by a `dlq:<runId>` id) —\n   * it can alert, compensate, or queue for review. Resolution per dead run: the workflow's inline\n   * `@DeadLetter()` method → its `@Workflow({ deadLetterWorkflow })` reference → this default. Omit\n   * everything to just leave dead runs parked (inspectable + retriable from the dashboard). Accepts a\n   * workflow class (refactor-safe) or a name (cross-runtime). Operator only.\n   */\n  deadLetterWorkflow?: WorkflowRef;\n  /**\n   * Whether an **operator** instance actively DRIVES runs — polls pending, recovers crashed\n   * (`recoverIncomplete`), resumes due timers, sweeps timeouts, prunes retention, and consumes local\n   * steps — as opposed to a read-only/dashboard replica. Defaults to `true`. Set `false` for a\n   * **dashboard/dispatch-only** instance (e.g. an API pod) that mounts the store/dashboard but must\n   * not process or recover workflows — leave that to another driving instance. `drive: false` also\n   * installs the engine's no-op run dispatcher, so a freshly `start()`ed run stays enqueue-only until\n   * a driving instance's poll picks it up. Ignored (irrelevant) for a thin worker (no `store`).\n   */\n  drive?: boolean;\n  /** Max ms to wait for in-flight runs on shutdown before exiting. Defaults to 10000. Operator only. */\n  shutdownTimeoutMs?: number;\n  /**\n   * Recurring workflows to start on a schedule (fixed interval or cron). The timer poller fires\n   * them each tick on **driving** instances only; `engine.start` is idempotent by the schedule's\n   * time-bucket run id, so racing instances start each window exactly once. Cron schedules need the\n   * optional `cron-parser` peer dependency. Operator only.\n   */\n  schedules?: ScheduledWorkflow[];\n  /**\n   * Hard-prune terminal run history on an interval so `durable_workflow_runs` (and its child tables)\n   * stays bounded — without it, completed/failed/cancelled runs accumulate forever and the timer\n   * poller's per-tick status scans get linearly slower. Driving instances only. Omit to keep all\n   * history (the default). Requires a store adapter that implements `pruneTerminalRuns` (the\n   * MikroORM adapter does); other adapters no-op with a warning. See {@link DurableRetentionOptions}.\n   * Operator only.\n   */\n  retention?: DurableRetentionOptions;\n  /**\n   * Build the public callback URL for a `ctx.webhook()` token, e.g.\n   * ``(t) => `https://api.example.com/durable/api/webhooks/${t}` ``. Populates `DurableWebhook.url`\n   * so a step can hand the URL to a third party. The dashboard's `POST webhooks/:token` receives the\n   * callback. Omit to build URLs yourself from the token. Operator only.\n   */\n  webhookUrl?: (token: string) => string;\n  /**\n   * Flow-control queues for remote steps, registered on the engine at startup. Reference one from a\n   * workflow with `ctx.step(handler, input, { queue: name })` to cap its concurrency / admission rate.\n   * Operator only.\n   */\n  queues?: QueueConfig[];\n  /**\n   * Admission backend for the flow-control `queues`. Defaults to in-process (per-instance) caps. Pass\n   * a `RedisAdmissionBackend` (from `@dudousxd/nestjs-durable-admission-redis`) to make concurrency /\n   * rate-limit / priority ordering GLOBAL across every engine replica. Operator only.\n   */\n  admission?: AdmissionBackend;\n  /**\n   * Provide the current W3C `traceparent` to stamp on dispatched remote tasks, so workers continue\n   * the distributed trace. Pass `otelTraceparent` from `@dudousxd/nestjs-durable-otel`. Operator only.\n   */\n  traceparent?: () => string | undefined;\n  /**\n   * Provide an opaque context carrier (tenant / user / correlation ids) to stamp on dispatched remote\n   * tasks, so workers re-expose it to the step handler alongside the `traceparent`. The engine never\n   * inspects its shape.\n   *\n   * **Auto-feed**: if you omit this AND `@dudousxd/nestjs-context` is installed (its accessor is bound\n   * to the shared `CONTEXT_ACCESSOR` token), DurableModule defaults this to a reader that builds\n   * `{ traceId, tenantId, userRef }` from the accessor — so a workflow dispatched within a request\n   * automatically carries the originating context across process boundaries. Pass your own reader to\n   * override the auto-feed; with neither, the carrier is omitted (unchanged behavior).\n   *\n   * Re-evaluated at each (re)dispatch — including a retry or a crash/scale-down resume that the engine\n   * drives OUTSIDE the originating request scope, where this reader may return empty or stale values.\n   * Treat the carrier as best-effort correlation/propagation metadata only — do NOT treat it as an\n   * authorization boundary. Operator only.\n   */\n  context?: () => Record<string, unknown> | undefined;\n  /** Attempts for each saga compensation when a run fails. Default 1 (no retry). Idempotent undos.\n   *  Operator only. */\n  compensationRetries?: number;\n  /**\n   * Opt into tenant read scoping: when `true` AND {@link namespace} is set, the module confines the\n   * store's reads to that namespace (a tenant-boundary view) instead of the operator view that sees\n   * all namespaces. Default `false` — the control plane (e.g. flip's `/ctrl` operator screens) stays\n   * unscoped. Requires a store that exposes the `withScope` capability (the MikroORM adapter does);\n   * a pre-built store without it is used as-is (construct it already-scoped instead). Operator only.\n   */\n  scopeReads?: boolean;\n  /**\n   * ioredis connection (string or options) for a **thin worker** (`connection` only) or the\n   * **co-located worker** consumer (`store` + `connection`). Set this to play a worker role (see the\n   * interface doc for the full role matrix). Omit to keep every `@Workflow` on the operator's inline\n   * fast path with zero dispatch round-trips.\n   */\n  connection?: string | Record<string, unknown>;\n  /**\n   * The isolation partition a worker role serves — a thin worker's or co-located worker's queue\n   * subscription, AND (for a co-located worker) the suffix each `@Workflow`'s dispatch token carries.\n   * Each handler's queue token is `tenantGroup(sanitizeQueueToken(name), partition)`\n   * (`@dudousxd/nestjs-durable-core`), so `undefined`, `''`, or `'default'` stays byte-identical to\n   * the bare (sanitized) name (single-tenant deployment unchanged), and any other partition serves\n   * `<name>@<partition>` — matching the queue name an operator's convention dispatch routes that\n   * tenant's runs to (`tenantGroup(run.workflow, run.namespace)` on the engine side). Not to be\n   * confused with {@link namespace} (the operator's own poll-scoping axis). Ignored for a plain\n   * operator (no `connection`).\n   */\n  partition?: string;\n  /** Key prefix namespacing the durable queues for a worker role's consumer. Defaults to `durable`\n   *  (matches the transport). Ignored for a plain operator (no `connection`). */\n  prefix?: string;\n  /**\n   * How many tasks a worker role's co-located/thin consumer runs concurrently PER SUBSCRIBED QUEUE\n   * (BullMQ Worker concurrency — the same limit is applied to every per-name queue the single\n   * `runRedisWorker` call starts). Defaults to 1. Raise it so a fanned-out batch (e.g. the N remote\n   * steps of a `gather`) runs in parallel instead of serially.\n   *\n   * Pass `'adaptive'` (or `{ mode:'adaptive', ... }`) to let the consumer self-tune its concurrency\n   * (latency gradient + RAM brake + backpressure) and publish a live status on its heartbeat. Ignored\n   * for a plain operator (no `connection`).\n   */\n  concurrency?: ConcurrencyOption;\n  /**\n   * Per-handler concurrency override, keyed by workflow/step NAME. Falls back to {@link concurrency}\n   * (then 1). NOT YET WIRED THROUGH: `runRedisWorker` (`@dudousxd/durable-worker`) currently applies\n   * one {@link concurrency} limit uniformly across every per-name queue it subscribes to from a\n   * single call — reserved here for when per-name concurrency lands there.\n   */\n  concurrencyByHandler?: Record<string, ConcurrencyOption>;\n  /** Timeout for a thin worker's `RunGateway` round-trip over `transport` before it rejects. Defaults\n   *  to 10_000ms inside `ProxyRunGateway`. Ignored for an operator (bound to `StoreRunGateway`). */\n  runGatewayTimeoutMs?: number;\n  /**\n   * An explicit preset that NAMES the deployment role instead of leaving it to `store`/`connection`\n   * inference, and VALIDATES the axes that are otherwise only prose (see the interface doc's role\n   * matrix, and {@link namespace} vs {@link partition}). Omit to keep the existing inference —\n   * `topology` is entirely additive and changes nothing when unset.\n   *\n   * **`namespace` vs `partition` — the two axes this preset locks down:**\n   * - `namespace` is the OPERATOR's poll-scoping axis: which runs a control-plane instance\n   *   drives/recovers/resumes (`runPending`/`recoverIncomplete`/`resumeDueTimers`/`sweepTimeouts`).\n   *   Unset makes the instance see (drive) EVERY namespace.\n   *   `{ role: 'control-plane' }` is the only preset that allows it.\n   * - `partition` is the WORKER's queue-routing suffix: which queue a thin/co-located worker's\n   *   consumer subscribes to (`<name>@<partition>`), matching the queue an operator's convention\n   *   dispatch routes a run's `namespace` to. `{ role: 'tenant' }` sets this FOR you from `tenant` —\n   *   you never set `partition` directly under `topology`.\n   *\n   * **One `tenant` word, two axes — the preset maps it to the right one per role:** on a\n   * `control-plane` an optional `tenant` scopes the OPERATOR to its own runs (maps to `namespace` —\n   * a self-contained local stack sharing a broker with a deployed cluster names itself so neither\n   * drives the other's runs; leave it unset on a deployed operator to drive every tenant). On a\n   * `tenant` role it is required and maps to `partition` (the worker's queue suffix).\n   *\n   * ```ts\n   * // Control plane: owns the store, dispatches over the transport, prunes old runs. The optional\n   * // `tenant` (e.g. from an env var) scopes this operator to its own runs — undefined on a\n   * // deployed operator (drives everything), set on a local stack sharing the broker.\n   * DurableModule.forRoot({\n   *   topology: { role: 'control-plane', tenant: process.env.DURABLE_TENANT },\n   *   store,\n   *   transport,\n   *   retention: { policies: [{ statuses: ['completed', 'cancelled'], maxAge: '14d' }] },\n   * });\n   *\n   * // Tenant: store-less worker scoped to its own partition — `tenant` maps to `partition` for you.\n   * DurableModule.forRoot({\n   *   topology: { role: 'tenant', tenant: 'acme-corp' },\n   *   connection: process.env.REDIS_URL,\n   * });\n   * ```\n   *\n   * Validated at `forRoot`/`forRootAsync` resolution time — see {@link DurableModule} for the exact\n   * error messages, which double as the axis primer above.\n   */\n  topology?: { role: 'control-plane'; tenant?: string } | { role: 'tenant'; tenant: string };\n}\n\nexport interface DurableModuleAsyncOptions {\n  // `never[]` (not `object[]`) is intentional: a contravariant bottom-type rest param is what lets a\n  // consumer supply a factory with concrete injected args, e.g. `(config: ConfigService) => …`.\n  useFactory: (...args: never[]) => DurableModuleOptions | Promise<DurableModuleOptions>;\n  inject?: InjectionToken[];\n}\n\n/** Throws the exact role-inference contract errors documented on {@link DurableModuleOptions}. */\nfunction assertValidRole(options: DurableModuleOptions): void {\n  const hasStore = options.store !== undefined;\n  const hasConnection = options.connection !== undefined;\n  if (!hasStore && !hasConnection) {\n    throw new Error(\n      'a durable module needs either a `store` (operator) or a `connection` (worker)',\n    );\n  }\n  if (hasStore && options.transport === undefined && options.transports === undefined) {\n    throw new Error('an operator (`store`) needs a `transport` (or `transports`)');\n  }\n}\n\n/**\n * Validates the {@link DurableModuleOptions.topology} preset (a no-op when it's unset — existing\n * inference is untouched). Each thrown message NAMES the role, the offending option, and a one-line\n * explanation of the axis it protects — these messages are the feature: they teach `namespace` (the\n * operator's poll-scoping axis) apart from `partition`/`tenant` (the worker's queue-routing axis),\n * which today are only reconciled in consumer-app comments.\n */\nfunction assertValidTopology(options: DurableModuleOptions): void {\n  const topology = options.topology;\n  if (topology === undefined) return;\n\n  if (topology.role === 'control-plane') {\n    if (\n      options.store === undefined ||\n      (options.transport === undefined && options.transports === undefined)\n    ) {\n      throw new Error(\n        \"topology: { role: 'control-plane' } needs `store` AND (`transport` or `transports`).\\n\" +\n          'A control-plane node is the durable operator: it owns the state store and dispatches ' +\n          'runs to workers over a transport — without both, there is nothing for it to operate.',\n      );\n    }\n    if (options.partition !== undefined) {\n      throw new Error(\n        \"topology: { role: 'control-plane' } forbids `partition`.\\n\" +\n          'partition is the WORKER queue-routing suffix — on a control-plane node, runs are ' +\n          \"dispatched to tenant partitions via each run's namespace, not via this option. Set \" +\n          \"`partition` (or `topology: { role: 'tenant', tenant }`) on the WORKER node instead.\",\n      );\n    }\n    if (\n      topology.tenant !== undefined &&\n      options.namespace !== undefined &&\n      options.namespace !== topology.tenant\n    ) {\n      throw new Error(\n        `topology: { role: 'control-plane', tenant: '${topology.tenant}' } conflicts with \\`namespace: '${options.namespace}'\\`.\\ntenant maps 1:1 onto namespace (the operator's poll-scoping axis) — set only \\`tenant\\`, or set \\`namespace\\` to the same value.`,\n      );\n    }\n    return;\n  }\n\n  if (options.connection === undefined) {\n    throw new Error(\n      `topology: { role: 'tenant', tenant: '${topology.tenant}' } needs \\`connection\\`.\\nA tenant is a store-less worker: it connects to the broker directly to pull its own partition of tasks instead of polling a store it does not have.`,\n    );\n  }\n  if (options.store !== undefined) {\n    throw new Error(\n      \"topology: { role: 'tenant' } forbids `store`.\\n\" +\n        'A tenant is store-less BY DEFINITION — a `store` present means you actually wanted ' +\n        \"`topology: { role: 'control-plane' }` (the store-owning role).\",\n    );\n  }\n  if (options.namespace !== undefined) {\n    throw new Error(\n      \"topology: { role: 'tenant' } forbids `namespace`.\\n\" +\n        \"namespace is the OPERATOR's poll-scoping axis (which runs a control-plane instance \" +\n        'drives/recovers/resumes) — a tenant worker has no poll loop for it to scope. Use this ' +\n        \"preset's `tenant` field for the worker's own routing axis instead.\",\n    );\n  }\n  if (options.partition !== undefined && options.partition !== topology.tenant) {\n    throw new Error(\n      `topology: { role: 'tenant', tenant: '${topology.tenant}' } conflicts with \\`partition: '${options.partition}'\\`.\\ntenant maps 1:1 onto partition (the worker queue-routing suffix) — set only \\`tenant\\`, or set \\`partition\\` to the same value.`,\n    );\n  }\n}\n\n/**\n * Applies the {@link DurableModuleOptions.topology} preset's `tenant` mapping — per role: onto\n * `partition` (worker queue-routing) for `{ role: 'tenant' }`, onto `namespace` (operator\n * poll-scoping) for `{ role: 'control-plane' }`. Validated already by {@link assertValidTopology}\n * (an equal explicit value is a no-op; a mismatched one throws before this runs). A no-op for an\n * unset `topology` (existing behavior, zero change).\n */\nfunction resolveTopology(options: DurableModuleOptions): DurableModuleOptions {\n  return servingPartition(resolveTopologyPreset(options));\n}\n\n/**\n * Fill the WORKER-side `partition` from this node's own `namespace` when it wasn't declared. The two\n * are one axis on the wire: the engine dispatches a run's steps to `<name>@<run.namespace>` (core's\n * `stepGroup`), so a node that stamps its runs with a namespace must SERVE the matching token or it\n * enqueues into queues nobody consumes. This is what keeps the documented local-dev recipe\n * (`docs/namespaces.md` — a namespaced engine on a private Redis) working with no extra wiring.\n *\n * An explicit `partition` always wins, so a node deliberately serving a different pool stays put.\n */\nfunction servingPartition(options: DurableModuleOptions): DurableModuleOptions {\n  if (options.partition !== undefined || options.namespace === undefined) return options;\n  return { ...options, partition: options.namespace };\n}\n\nfunction resolveTopologyPreset(options: DurableModuleOptions): DurableModuleOptions {\n  const topology = options.topology;\n  if (topology === undefined) return options;\n  if (topology.role === 'control-plane') {\n    // An optional tenant scopes the operator to its own runs — maps onto `namespace` (undefined =\n    // global operator, drives every tenant; validated against an explicit mismatched `namespace`).\n    // The namespace stays a STORE-side axis: the engine stamps runs with it and routes their work\n    // via `@<tenant>` GROUP suffixes on the transport's own prefix (the cross-SDK convention every\n    // tenant worker speaks natively) — it never re-scopes the transport keyspace.\n    if (topology.tenant === undefined) return options;\n    return {\n      ...options,\n      // An explicit `namespace` is already validated equal to `tenant` (assertValidTopology), so\n      // keeping it is a no-op rather than a conflict.\n      namespace: options.namespace ?? topology.tenant,\n      // The tenant is ALSO this node's worker-routing suffix: its engine now dispatches a run's steps\n      // to `<name>@<tenant>` (core's `stepGroup`), so its own co-located worker must SUBSCRIBE those\n      // same tokens. Without this the node would dispatch into queues it is not itself listening on.\n      // `partition` is user-forbidden on this role (assertValidTopology), so we own it here.\n      partition: topology.tenant,\n    };\n  }\n  if (options.partition !== undefined) return options;\n  return { ...options, partition: topology.tenant };\n}\n\n/**\n * Boot-time wiring for the tenant run gateway's OPERATOR side, gated by {@link isDrivingOperator} (an\n * operator instance drives by default; `drive: false` — or a thin worker with no `store` at all —\n * does not). Two independent capabilities, each wired only when the transport carries it:\n *\n * 1. **`RunRequestResponder`** — answers a tenant's {@link RunRequest} against the bound\n *    {@link RUN_GATEWAY}, tenant-scoped. Wired when the transport implements both\n *    `onRunRequest`/`publishRunReply` (only broker transports carry the protocol).\n * 2. **Tenant-event re-publisher** ({@link TenantEventRepublisher}) — mirrors every engine\n *    lifecycle event onto its run's per-tenant channel via `publishTenantEvent`, so a store-less\n *    tenant worker can live-tail its OWN runs. See that class for the namespace-resolution and\n *    cache-eviction details.\n */\n@Injectable()\nclass RunGatewayBootstrap implements OnApplicationBootstrap, OnModuleDestroy {\n  private unsubscribe?: () => void;\n\n  constructor(\n    private readonly engine: WorkflowEngine,\n    @Inject(TRANSPORT_CANONICAL) private readonly transport: Transport | null,\n    private readonly gateway: RunGateway,\n    @Inject(STATE_STORE_CANONICAL) private readonly store: StateStore | null,\n    @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n  ) {}\n\n  onApplicationBootstrap(): void {\n    if (!isDrivingOperator(this.options) || !this.transport) return;\n    const store = this.store;\n    if (!store) return; // unreachable when isDrivingOperator is true — keeps types honest\n\n    const { onRunRequest, publishRunReply, publishTenantEvent } = this.transport;\n    if (typeof onRunRequest === 'function' && typeof publishRunReply === 'function') {\n      // Bind to the transport: these are real methods that touch the transport's private fields\n      // (e.g. BullMQTransport's `this.#runRequestName()`), so they must keep their receiver — passing\n      // them destructured/unbound makes `this` the literal below and throws \"Receiver must be an\n      // instance of class\" on first use. (Mirrors the `publishTenantEvent.bind` just below.)\n      const runRequestTransport: RunRequestTransport = {\n        onRunRequest: onRunRequest.bind(this.transport),\n        publishRunReply: publishRunReply.bind(this.transport),\n      };\n      new RunRequestResponder(runRequestTransport, this.gateway).start();\n    }\n\n    if (typeof publishTenantEvent === 'function') {\n      const republisher = new TenantEventRepublisher(\n        store,\n        publishTenantEvent.bind(this.transport),\n      );\n      this.unsubscribe = this.engine.subscribe((event) => {\n        void republisher.handle(event);\n      });\n    }\n  }\n\n  onModuleDestroy(): void {\n    this.unsubscribe?.();\n  }\n}\n\n@Module({})\nexport class DurableModule {\n  static forRoot(options: DurableModuleOptions): DynamicModule {\n    DurableModule.assertValid(options);\n    return DurableModule.build({\n      provide: DURABLE_OPTIONS_CANONICAL,\n      useValue: resolveTopology(options),\n    });\n  }\n\n  static forRootAsync(options: DurableModuleAsyncOptions): DynamicModule {\n    return DurableModule.build({\n      provide: DURABLE_OPTIONS_CANONICAL,\n      useFactory: async (...args: Parameters<DurableModuleAsyncOptions['useFactory']>) => {\n        const resolved = await options.useFactory(...args);\n        DurableModule.assertValid(resolved);\n        return resolveTopology(resolved);\n      },\n      inject: options.inject ?? [],\n    });\n  }\n\n  /**\n   * Validates role/axis constraints, in resolution order. When {@link DurableModuleOptions.topology}\n   * is set, it OWNS validation — {@link assertValidTopology}'s own store/transport/connection checks\n   * are strictly stronger than {@link assertValidRole}'s, so the topology-specific, axis-teaching\n   * message is what a `topology`-opted-in consumer sees (not the older generic one). Falls back to\n   * {@link assertValidRole} unchanged when `topology` is absent — zero behavior change.\n   */\n  private static assertValid(options: DurableModuleOptions): void {\n    if (options.topology !== undefined) {\n      assertValidTopology(options);\n      return;\n    }\n    assertValidRole(options);\n  }\n\n  private static build(optionsProvider: Provider): DynamicModule {\n    return {\n      module: DurableModule,\n      global: true,\n      imports: [DiscoveryModule],\n      providers: [\n        optionsProvider,\n        {\n          provide: STATE_STORE_CANONICAL,\n          useFactory: (options: DurableModuleOptions): StateStore | null =>\n            options.store !== undefined ? scopedStore(options.store, options) : null,\n          inject: [DURABLE_OPTIONS_CANONICAL],\n        },\n        {\n          provide: TRANSPORT_CANONICAL,\n          // With a POOL (`transports`) and no singular `transport`, the canonical transport is the\n          // pool's PRIMARY — the same one the engine dispatches on first. Leaving it null starved\n          // the step registrar / in-app worker of a transport, so a pool-configured operator\n          // registered NO step handlers and its own steps parked in `wait` with no consumer.\n          useFactory: (options: DurableModuleOptions) =>\n            options.transport ?? options.transports?.[0]?.transport ?? null,\n          inject: [DURABLE_OPTIONS_CANONICAL],\n        },\n        // Legacy back-compat aliases (deprecated tokens still resolve to the same instances):\n        { provide: STATE_STORE, useExisting: STATE_STORE_CANONICAL },\n        { provide: TRANSPORT, useExisting: TRANSPORT_CANONICAL },\n        { provide: DURABLE_OPTIONS, useExisting: DURABLE_OPTIONS_CANONICAL },\n        {\n          // Shared token, bound EXACTLY once: a real engine for the operator role, or a store-less\n          // `DurableStartClient` facade for a thin worker (no `store`) — either way, `WorkflowService`\n          // and app code call `engine.start(...)` unchanged.\n          provide: WorkflowEngine,\n          useFactory: async (\n            options: DurableModuleOptions,\n            store: StateStore | null,\n            transport: Transport | null,\n            // The accessor from `@dudousxd/nestjs-context` is resolved at construction via ModuleRef\n            // (shared CONTEXT_ACCESSOR symbol — no hard import). Absent → unchanged behavior.\n            moduleRef: ModuleRef,\n          ) => {\n            if (options.store === undefined) {\n              return new DurableStartClient(options);\n            }\n            if (!store) {\n              throw new Error(\n                'unreachable: STATE_STORE_CANONICAL must resolve a store when options.store is set',\n              );\n            }\n            // The control-plane default is the primary task transport (single, or the pool's first)\n            // when it can broadcast.\n            const primary = transport ?? options.transports?.[0]?.transport;\n            // Auto-feed the carrier from nestjs-context when an accessor is present AND the app didn't\n            // pass its own `context` reader. The app's own reader always wins; with no accessor the\n            // carrier stays `undefined` (unchanged behavior — `traceparent` etc. still work).\n            const accessor = resolveAccessor(moduleRef);\n            const context =\n              options.context ?? (accessor ? () => carrierFromAccessor(accessor) : undefined);\n            // Consume side: when nestjs-context is present (an accessor is bound), re-hydrate the\n            // originating context AROUND each local step body, so a `@Step` reader sees the\n            // tenant/user/trace ids ambiently via nestjs-context's ALS — no consumer wrapping needed.\n            // The runtime `Context` is a module-level singleton (not the accessor token), resolved\n            // ONCE here via a guarded dynamic import (optional peer — failure leaves the default\n            // passthrough). Best-effort: an empty/undefined carrier just runs the handler normally.\n            const runtime = accessor ? await resolveContextRuntime() : undefined;\n            const rehydrate =\n              runtime &&\n              (<T>(carrier: Record<string, unknown> | undefined, fn: () => T): T =>\n                carrier && Object.keys(carrier).length > 0\n                  ? runtime.deserialize(carrier, fn)\n                  : fn());\n            const engine = new WorkflowEngine({\n              store,\n              transport: transport ?? undefined,\n              transports: options.transports,\n              controlPlane: options.controlPlane ?? (isControlPlane(primary) ? primary : undefined),\n              leaseMs: options.leaseMs,\n              admission: options.admission,\n              maxRecoveryAttempts: options.maxRecoveryAttempts,\n              remoteAdvanceSilenceMs: options.remoteAdvanceSilenceMs,\n              instanceId: options.instanceId,\n              namespace: options.namespace,\n              webhookUrl: options.webhookUrl,\n              traceparent: options.traceparent,\n              context,\n              rehydrate: rehydrate || undefined,\n              compensationRetries: options.compensationRetries,\n              // A non-driving (dashboard/API) operator must not run workflows: enqueue-only, leaving\n              // each `pending` run in the store for a DRIVING instance's poll. A driving operator gets\n              // the engine's default in-process dispatcher: for a body registered inline, it runs\n              // locally; for one registered GROUP-SERVED (co-located worker) or left unregistered, the\n              // SAME default dispatcher routes it out remotely (group-served executor, or convention\n              // dispatch — always on) instead.\n              runDispatcher: options.drive === false ? { dispatch: () => {} } : undefined,\n            });\n            for (const queue of options.queues ?? []) engine.registerQueue(queue);\n            // Dead-letter routing (per-workflow `@DeadLetter()` / `deadLetterWorkflow` + this global\n            // default) is wired by the WorkflowRegistrar, which owns the `@Workflow` metadata.\n            return engine;\n          },\n          inject: [\n            DURABLE_OPTIONS_CANONICAL,\n            STATE_STORE_CANONICAL,\n            TRANSPORT_CANONICAL,\n            ModuleRef,\n          ],\n        },\n        WorkflowService,\n        EntityService,\n        WorkflowRegistrar,\n        DurableStepRegistrar,\n        TimerPoller,\n        RetentionPoller,\n        // Tenant run gateway: bound EXACTLY once — the store-backed `StoreRunGateway` for an operator,\n        // or (for a thin worker, no `store`) a `ProxyRunGateway` over `transport` when given, else a\n        // gateway whose every method rejects with a clear tenant error.\n        {\n          provide: RunGateway,\n          useFactory: (\n            options: DurableModuleOptions,\n            store: StateStore | null,\n            engine: WorkflowEngine,\n          ): RunGateway => {\n            if (options.store !== undefined) {\n              if (!store) {\n                throw new Error(\n                  'unreachable: STATE_STORE_CANONICAL must resolve a store when options.store is set',\n                );\n              }\n              return new StoreRunGateway(store, engine);\n            }\n            return options.transport\n              ? new ProxyRunGateway(\n                  options.transport,\n                  options.partition ?? 'default',\n                  options.runGatewayTimeoutMs,\n                )\n              : unavailableRunGateway();\n          },\n          inject: [DURABLE_OPTIONS_CANONICAL, STATE_STORE_CANONICAL, WorkflowEngine],\n        },\n        RunGatewayBootstrap,\n        // Co-located in-app worker (uniform dispatch): inert unless BOTH `store` and `connection` are\n        // set — see `in-app-worker.ts`.\n        ...inAppWorkerProviders(),\n        // Pure thin-worker consumer: inert unless `connection` is set WITHOUT `store` — see\n        // `durable-worker.module.ts`.\n        ...thinWorkerProviders(),\n      ],\n      exports: [\n        WorkflowService,\n        EntityService,\n        WorkflowEngine,\n        STATE_STORE,\n        STATE_STORE_CANONICAL,\n        TRANSPORT,\n        TRANSPORT_CANONICAL,\n        DURABLE_OPTIONS_CANONICAL,\n        RunGateway,\n      ],\n    };\n  }\n}\n","import {\n  DURABLE_OPTIONS_CANONICAL,\n  type StepLogger,\n  TRANSPORT_CANONICAL,\n  type Transport,\n} from '@dudousxd/nestjs-durable-core';\nimport { Inject, Injectable, type OnModuleInit } from '@nestjs/common';\nimport { DiscoveryService, MetadataScanner } from '@nestjs/core';\nimport { scanSteps } from './discovery-helpers';\nimport type { DurableModuleOptions } from './durable.module';\nimport { isDrivingOperator } from './role';\n\n/** A transport that can run step handlers in-process (e.g. the event-emitter transport). */\ninterface LocalTaskHandling {\n  handle(\n    name: string,\n    fn: (input: unknown, log: StepLogger) => Promise<unknown> | unknown,\n    /** Isolation partition to SERVE — the queue token is `tenantGroup(sanitizeQueueToken(name),\n     *  partition)`. Omitted (or undefined) keeps the transport's own constructor partition. */\n    partition?: string | undefined,\n  ): void;\n}\n\nfunction supportsHandle(transport: unknown): transport is LocalTaskHandling {\n  return typeof (transport as LocalTaskHandling | null)?.handle === 'function';\n}\n\n/**\n * Discovers `@Step` methods and registers them — under their RESOLVED name (derived `Class.method`\n * for a bare `@Step()`, or the explicit override) — as step handlers on the configured transport,\n * when that transport runs handlers in-process. With a queue/remote transport the handlers live in\n * the worker process instead, so there is nothing to wire here.\n *\n * **Context re-hydration (consume side) is the consumer's responsibility.** DurableModule's\n * produce side auto-feeds an opaque carrier (`{ traceId, tenantId, userRef }`) from\n * `@dudousxd/nestjs-context` onto each dispatched `RemoteTask.context` (see DurableModule). A worker\n * that wants its `@Step` reads to SEE the originating context must re-establish it from\n * `task.context`. We do NOT do this here because: (a) re-hydration needs nestjs-context's module-level\n * `Context.deserialize(carrier, fn)` at runtime, and it is an OPTIONAL peer we must not hard-import;\n * and (b) the in-process `StepHandler` contract is `(input, log)` — the engine never surfaces\n * `task.context` to the handler closure, so wrapping it here would require widening the core\n * transport/handler signature (a non-additive cross-cutting change). Instead, a worker that consumes\n * the carrier should wrap its handler with `Context.deserialize(task.context, () => handler(...))`\n * from its own transport/worker bootstrap, where `task` is in scope.\n */\n@Injectable()\nexport class DurableStepRegistrar implements OnModuleInit {\n  constructor(\n    private readonly discovery: DiscoveryService,\n    private readonly metadataScanner: MetadataScanner,\n    @Inject(TRANSPORT_CANONICAL) private readonly transport: Transport | null,\n    @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n  ) {}\n\n  onModuleInit(): void {\n    // A non-driving operator (`drive: false`) or a thin worker (no `store`) must not consume the\n    // queue — receiving and running step tasks locally is exactly the driving-operator role we're\n    // switching off here (a thin worker registers its steps via `ThinStepRegistrar` instead).\n    if (!isDrivingOperator(this.options)) return;\n    if (!supportsHandle(this.transport)) return;\n    const transport = this.transport;\n\n    // Forward the step logger as a second arg; methods that only declare `(input)` ignore it.\n    // The partition is the node's own tenant axis (`topology: { role: 'control-plane', tenant }`\n    // resolves it, see `resolveTopology`): this engine DISPATCHES a tenant run's steps to\n    // `<name>@<tenant>`, so its in-process handlers must SERVE that same token. Undefined on a global\n    // operator — the bare token, byte-identical to before.\n    scanSteps(this.discovery, this.metadataScanner, (meta, handler) =>\n      transport.handle(meta.name, handler, this.options.partition),\n    );\n  }\n}\n","import type { DurableModuleOptions } from './durable.module';\n\n/**\n * True for the **operator** role — `store` is set (with or without a co-located `connection`). See\n * {@link DurableModuleOptions} for the full role matrix. Kept in its own module (rather than\n * `durable.module.ts`) so the poller/registrar classes can import it as a plain VALUE without a\n * circular value dependency on the module that also imports them as providers.\n */\nexport function isOperatorRole(options: DurableModuleOptions): boolean {\n  return options.store !== undefined;\n}\n\n/**\n * True when an operator instance actively DRIVES runs — polls pending, recovers crashed, resumes due\n * timers, sweeps timeouts, prunes retention, consumes local steps — as opposed to a read-only/\n * dashboard replica (`drive: false`) that mounts the store/dashboard but leaves driving to another\n * instance. Meaningless (`false`) outside the operator role. Defaults to `true` for an operator.\n */\nexport function isDrivingOperator(options: DurableModuleOptions): boolean {\n  return isOperatorRole(options) && options.drive !== false;\n}\n","import { type EntityHandler, WorkflowEngine } from '@dudousxd/nestjs-durable-core';\nimport { Injectable } from '@nestjs/common';\nimport 'reflect-metadata';\n\nexport const ENTITY_METADATA = Symbol('nestjs-durable:entity');\nexport const ENTITY_ON_METADATA = Symbol('nestjs-durable:entity-on');\n\n/**\n * Marks an `@Injectable()` class as a **durable entity** (a virtual object): its `@On(op)` methods run\n * serialized per key over the instance's fields as durable state. e.g.\n *\n * ```ts\n * @Entity({ name: 'cart' }) @Injectable()\n * class Cart { items: Item[] = []; @On('add') add(i: Item) { this.items.push(i); } @On('list') list() { return this.items; } }\n * ```\n *\n * The class must be **constructible with no arguments** (a fresh instance is the initial state per key)\n * — keep entities pure state, no DI. Drive them with `EntityService` or `ctx.signalEntity`/`callEntity`.\n */\nexport function Entity(options: { name: string }): ClassDecorator {\n  return (target) => {\n    Reflect.defineMetadata(ENTITY_METADATA, options, target);\n  };\n}\n\n/** Marks an entity method as the handler for operation `op`. */\nexport function On(op: string): MethodDecorator {\n  return (target, propertyKey) => {\n    // biome-ignore lint/complexity/noBannedTypes: reflect-metadata keys on the class constructor\n    const ctor = (target as { constructor: Function }).constructor;\n    const ops = (Reflect.getMetadata(ENTITY_ON_METADATA, ctor) as Map<string, string>) ?? new Map();\n    ops.set(op, propertyKey as string);\n    Reflect.defineMetadata(ENTITY_ON_METADATA, ops, ctor);\n  };\n}\n\n// biome-ignore lint/complexity/noBannedTypes: matches reflect-metadata's class target type\nexport function getEntityMeta(target: Function): { name: string } | undefined {\n  return Reflect.getMetadata(ENTITY_METADATA, target) as { name: string } | undefined;\n}\n\n/**\n * Build the engine `EntityConfig` for a discovered `@Entity` class: a fresh instance per key, and\n * handlers that rehydrate the class prototype onto the (serialized) state before dispatching the op,\n * so methods work after replay.\n */\n// biome-ignore lint/complexity/noBannedTypes: a class constructor\nexport function entityConfigFor(ctor: Function): {\n  initialState: () => object;\n  handlers: Record<string, EntityHandler>;\n} {\n  const ops = (Reflect.getMetadata(ENTITY_ON_METADATA, ctor) as Map<string, string>) ?? new Map();\n  const Cls = ctor as new () => Record<string, (arg: unknown) => unknown>;\n  const handlers: Record<string, EntityHandler> = {};\n  for (const [op, method] of ops) {\n    handlers[op] = (state, arg) => {\n      Object.setPrototypeOf(state as object, Cls.prototype); // re-attach methods to the plain state\n      const fn = (state as Record<string, ((a: unknown) => unknown) | undefined>)[method];\n      if (typeof fn !== 'function') throw new Error(`entity handler \"${method}\" is not a method`);\n      return fn.call(state, arg);\n    };\n  }\n  return { initialState: () => new Cls(), handlers };\n}\n\n/** Inject this to drive durable entities from outside a workflow. */\n@Injectable()\nexport class EntityService {\n  constructor(private readonly engine: WorkflowEngine) {}\n\n  /** Send an operation to an entity (fire-and-forget; ordered + exactly-once per key). */\n  signal(name: string, key: string, op: string, arg?: unknown): Promise<void> {\n    return this.engine.signalEntity(name, key, op, arg);\n  }\n\n  /** Read an entity's current durable state (or undefined if it has none yet). */\n  getState<S = unknown>(name: string, key: string): Promise<S | undefined> {\n    return this.engine.getEntityState<S>(name, key);\n  }\n}\n","import {\n  DurableWorkerRuntime,\n  type RunningWorker,\n  runRedisWorker as defaultRunRedisWorker,\n} from '@dudousxd/durable-worker';\nimport {\n  DURABLE_OPTIONS_CANONICAL,\n  TRANSPORT_CANONICAL,\n  type Transport,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n  Inject,\n  Injectable,\n  type OnApplicationBootstrap,\n  type OnApplicationShutdown,\n  type OnModuleInit,\n  type Provider,\n} from '@nestjs/common';\nimport { DiscoveryService, MetadataScanner } from '@nestjs/core';\nimport { scanSteps, scanWorkflows } from './discovery-helpers';\nimport type { RunRedisWorkerFn } from './durable-worker.module';\nimport type { DurableModuleOptions } from './durable.module';\n\n/**\n * The **co-located in-app worker** (uniform dispatch): active whenever an app supplies BOTH `store`\n * AND `connection` (`DurableModule.forRoot({ store, transport, connection, partition? })`) — the same\n * process runs the engine AND serves its own discovered `@Workflow`/`@Step`. The engine registers each\n * `@Workflow` GROUP-SERVED — its turns are dispatched, PER WORKFLOW NAME, to\n * `tenantGroup(sanitizeQueueToken(name), partition)` over the transport via a per-workflow\n * `RemoteWorkflowExecutor` instead of run inline — and a co-located {@link DurableWorkerRuntime}\n * subscribes one queue per discovered name (via `runRedisWorker`) and replays the very same TS bodies.\n * This is the uniform-dispatch \"one app, both roles\" shape — every turn pays a transport round-trip\n * even though the worker is the same process. Requires a transport that carries workflow tasks\n * (BullMQ); an in-process-only transport cannot dispatch a `WorkflowExecutor`.\n *\n * Distinct from the PURE thin-worker role (`connection` set, `store` unset — see\n * `durable-worker.module.ts`'s `ThinWorkflowRegistrar`/`ThinStepRegistrar`/`ThinWorkerBootstrap`),\n * which has no engine/store of its own at all.\n */\nexport const IN_APP_WORKER_BINDING = Symbol('nestjs-durable:in-app-worker-binding');\n\n/** The co-located worker's {@link DurableWorkerRuntime} (the consumer half). */\nexport const IN_APP_WORKER_RUNTIME = Symbol('nestjs-durable:in-app-worker-runtime');\n\n/** `runRedisWorker`, injected so tests can substitute a fake (no real Redis). Defaults to the real one. */\nexport const IN_APP_RUN_REDIS_WORKER = Symbol('nestjs-durable:in-app-run-redis-worker');\n\n/** Started {@link RunningWorker} handles for the in-app worker, closed on shutdown. */\nexport const IN_APP_WORKER_RUNNERS = Symbol('nestjs-durable:in-app-worker-runners');\n\n/** The group-served binding shape resolved behind {@link IN_APP_WORKER_BINDING}. */\nexport interface InAppWorkerBinding {\n  transport: Transport;\n  partition?: string;\n}\n\n/** True for the **co-located** role — both `store` AND `connection` set. */\nfunction isCoLocatedWorker(options: DurableModuleOptions): boolean {\n  return options.store !== undefined && options.connection !== undefined;\n}\n\n/**\n * Builds the {@link IN_APP_WORKER_BINDING}: when this app is co-located (`store` + `connection`), the\n * engine's transport plus the configured partition — the registrar builds a PER-WORKFLOW\n * `RemoteWorkflowExecutor` from these, one per discovered name; otherwise `null` (inline default).\n * Fails fast if co-located but the transport can't carry workflow tasks — a group-served run would\n * otherwise dead-end at dispatch.\n */\nfunction inAppWorkerBinding(\n  transport: Transport | null,\n  options: DurableModuleOptions,\n): InAppWorkerBinding | null {\n  if (!isCoLocatedWorker(options)) return null;\n  if (!transport?.dispatchWorkflowTask || !transport.onDecision) {\n    throw new Error(\n      'a co-located worker (store + connection) requires a transport that carries workflow tasks (dispatchWorkflowTask + onDecision), e.g. BullMQTransport. An in-process transport cannot serve a group-served workflow.',\n    );\n  }\n  return {\n    transport,\n    ...(options.partition !== undefined ? { partition: options.partition } : {}),\n  };\n}\n\n/**\n * The consumer half of the in-app worker: on init it registers every discovered `@Workflow`/`@Step`\n * on a {@link DurableWorkerRuntime} (the SAME bodies the engine registered group-served), and on\n * bootstrap it starts one `runRedisWorker` call that subscribes one queue per discovered name,\n * suffixed by the configured partition, closing it on shutdown. A no-op outside the co-located role\n * (see {@link isCoLocatedWorker}). Mirrors the thin {@link\n * import('./durable-worker.module').ThinWorkerBootstrap}, but co-located with a full engine.\n */\n@Injectable()\nexport class InAppWorkerBootstrap\n  implements OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown\n{\n  private readonly runners: RunningWorker[] = [];\n\n  constructor(\n    private readonly discovery: DiscoveryService,\n    private readonly metadataScanner: MetadataScanner,\n    @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n    @Inject(IN_APP_WORKER_RUNTIME) private readonly runtime: DurableWorkerRuntime,\n    @Inject(IN_APP_RUN_REDIS_WORKER) private readonly runRedisWorker: RunRedisWorkerFn,\n    @Inject(IN_APP_WORKER_RUNNERS) private readonly runnersSink: RunningWorker[],\n  ) {}\n\n  onModuleInit(): void {\n    if (!isCoLocatedWorker(this.options)) return;\n    // Register the same TS bodies the engine serves group-served, so the consumer can replay them.\n    scanWorkflows(this.discovery, (meta, instance) =>\n      this.runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input)),\n    );\n    scanSteps(this.discovery, this.metadataScanner, (meta, handler) =>\n      this.runtime.registerStep(meta.name, handler),\n    );\n  }\n\n  async onApplicationBootstrap(): Promise<void> {\n    const options = this.options;\n    if (!isCoLocatedWorker(options) || options.connection === undefined) return;\n    const handle = await this.runRedisWorker({\n      runtime: this.runtime,\n      connection: options.connection,\n      ...(options.partition !== undefined ? { partition: options.partition } : {}),\n      ...(options.prefix !== undefined ? { prefix: options.prefix } : {}),\n      ...(options.instanceId !== undefined ? { instanceId: options.instanceId } : {}),\n      ...(options.concurrency !== undefined ? { concurrency: options.concurrency } : {}),\n    });\n    this.runners.push(handle);\n    this.runnersSink.push(handle);\n  }\n\n  async onApplicationShutdown(): Promise<void> {\n    await Promise.allSettled(this.runners.map((handle) => handle.close()));\n  }\n}\n\n/**\n * The providers that stand up the co-located in-app worker, added to {@link\n * import('./durable.module').DurableModule}'s unified provider set. All are inert outside the\n * co-located role (`store` + `connection` both set — see {@link isCoLocatedWorker}), so a plain\n * operator (`store` only) or pure thin worker (`connection` only) is unaffected.\n */\nexport function inAppWorkerProviders(): Provider[] {\n  return [\n    {\n      provide: IN_APP_WORKER_BINDING,\n      useFactory: (transport: Transport | null, options: DurableModuleOptions) =>\n        inAppWorkerBinding(transport, options),\n      inject: [TRANSPORT_CANONICAL, DURABLE_OPTIONS_CANONICAL],\n    },\n    {\n      provide: IN_APP_WORKER_RUNTIME,\n      // The runtime's `WorkflowWorker` uses its `group` ctor param as the WORKFLOW's\n      // `workflowPartition` for any `ctx.step` call (see `workflow-context.ts`'s `resolveCallGroup`)\n      // — that fallback MUST equal this app's own `partition`, or a dispatched step's decision\n      // carries a mismatched token and the engine dispatches it to a queue nothing here subscribes\n      // to. Explicit `''` (never\n      // `undefined`) so it does NOT fall through to `WorkflowWorker`'s unrelated `'workflows'`\n      // default parameter.\n      useFactory: (options: DurableModuleOptions) =>\n        new DurableWorkerRuntime({ workflowGroup: options.partition ?? '' }),\n      inject: [DURABLE_OPTIONS_CANONICAL],\n    },\n    { provide: IN_APP_RUN_REDIS_WORKER, useValue: defaultRunRedisWorker },\n    { provide: IN_APP_WORKER_RUNNERS, useValue: [] as RunningWorker[] },\n    InAppWorkerBootstrap,\n  ];\n}\n","import type {\n  DurableTopology,\n  EngineEvent,\n  GroupHealth,\n  RunDetail,\n  RunGateway,\n  RunListItem,\n  RunQuery,\n  RunReply,\n  RunRequestKind,\n  RunResult,\n  RunWaiting,\n  Transport,\n} from '@dudousxd/nestjs-durable-core';\nimport { Injectable } from '@nestjs/common';\n\n/**\n * Method-shorthand signatures (not arrow-function-typed properties) are deliberate: TypeScript\n * checks method parameters bivariantly, so a `Promise<T>`'s own `resolve`/`reject` (typed for that\n * call's concrete `T`) can be stored here erased to `unknown` with no cast — the one place the\n * generic `T` from {@link ProxyRunGateway.request} crosses into the untyped bookkeeping map.\n */\ninterface PendingRequest {\n  resolve(data: unknown): void;\n  reject(err: Error): void;\n  timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Tenant-side `RunGateway` — round-trips every verb as a `RunRequest`/`RunReply` pair over the\n * transport, correlated by a minted `requestId`, and bridges `subscribe` onto the transport's\n * per-tenant event stream. Bound under `RUN_GATEWAY` by `DurableModule`'s thin-worker role\n * (`connection` set, no `store`) when the app supplies a `transport` (see `unavailableRunGateway`\n * for the no-transport fallback). The counterpart to the operator-side `StoreRunGateway`: a tenant\n * worker never touches a store/driver directly, only this proxy.\n */\n@Injectable()\nexport class ProxyRunGateway implements RunGateway {\n  private readonly pending = new Map<string, PendingRequest>();\n\n  constructor(\n    private readonly transport: Transport,\n    private readonly tenant: string,\n    private readonly timeoutMs = 10_000,\n  ) {\n    this.transport.onRunReply?.((reply: RunReply) => this.handleReply(reply));\n  }\n\n  private handleReply(reply: RunReply): void {\n    const pending = this.pending.get(reply.requestId);\n    if (!pending) return;\n    clearTimeout(pending.timer);\n    this.pending.delete(reply.requestId);\n    if (reply.result.ok) {\n      pending.resolve(reply.result.data);\n    } else {\n      pending.reject(new Error(reply.result.error.message));\n    }\n  }\n\n  private request<T>(body: RunRequestKind): Promise<T> {\n    return new Promise<T>((resolve, reject) => {\n      const requestId = globalThis.crypto.randomUUID();\n      const timer = setTimeout(() => {\n        this.pending.delete(requestId);\n        reject(\n          new Error(`control plane did not respond to ${body.kind} within ${this.timeoutMs}ms`),\n        );\n      }, this.timeoutMs);\n      this.pending.set(requestId, { resolve, reject, timer });\n      this.transport\n        .dispatchRunRequest?.({ requestId, tenant: this.tenant, body })\n        .catch((error: unknown) => {\n          const stillPending = this.pending.get(requestId);\n          if (!stillPending) return;\n          clearTimeout(stillPending.timer);\n          this.pending.delete(requestId);\n          stillPending.reject(error instanceof Error ? error : new Error(String(error)));\n        });\n    });\n  }\n\n  topology(): DurableTopology {\n    return { role: 'tenant', tenant: this.tenant };\n  }\n\n  getRunDetail(runId: string): Promise<RunDetail | null> {\n    return this.request<RunDetail | null>({ kind: 'getRunDetail', runId });\n  }\n\n  /** The control plane's `StoreRunGateway` resolves each run's `waiting` descriptor; the reply carries\n   *  it through as plain JSON, so a tenant's list rows name the wait too. */\n  listRuns(query: RunQuery): Promise<RunListItem[]> {\n    return this.request<RunListItem[]>({ kind: 'listRuns', query });\n  }\n\n  /** Round-trips to the operator, which scopes the result to this tenant's own `@<tenant>` groups. */\n  workerHealth(): Promise<GroupHealth[]> {\n    return this.request<GroupHealth[]>({ kind: 'workerHealth' });\n  }\n\n  /** Bulk, one request for the whole id list (like `listRuns`, not one request per id). The operator\n   *  filters the reply to runs this tenant actually owns (see `RunRequestResponder`). */\n  waitingFor(runIds: string[]): Promise<Record<string, RunWaiting>> {\n    return this.request<Record<string, RunWaiting>>({ kind: 'waitingFor', runIds });\n  }\n\n  cancel(runId: string, opts?: { compensate?: boolean }): Promise<RunResult | null> {\n    return this.request<RunResult | null>(\n      opts === undefined ? { kind: 'cancel', runId } : { kind: 'cancel', runId, opts },\n    );\n  }\n\n  retry(runId: string): Promise<RunResult | null> {\n    return this.request<RunResult | null>({ kind: 'retry', runId });\n  }\n\n  continue(runId: string): Promise<RunResult | null> {\n    return this.request<RunResult | null>({ kind: 'continue', runId });\n  }\n\n  retryWithInput(runId: string, input: unknown): Promise<{ runId: string } | null> {\n    return this.request<{ runId: string } | null>({ kind: 'retryWithInput', runId, input });\n  }\n\n  redispatchPending(runId: string): Promise<(RunResult & { redispatched: number }) | null> {\n    return this.request<(RunResult & { redispatched: number }) | null>({\n      kind: 'redispatch',\n      runId,\n    });\n  }\n\n  subscribe(runId: string, onEvent: (event: EngineEvent) => void): () => void {\n    const unsubscribe = this.transport.onTenantEvent?.(this.tenant, (evt) => {\n      if (evt.event.runId === runId) onEvent(evt.event);\n    });\n    return unsubscribe ?? (() => {});\n  }\n}\n","import {\n  DURABLE_OPTIONS_CANONICAL,\n  STATE_STORE_CANONICAL,\n  type StateStore,\n  TERMINAL_RUN_STATUSES,\n  parseDuration,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n  Inject,\n  Injectable,\n  type OnApplicationBootstrap,\n  type OnModuleDestroy,\n} from '@nestjs/common';\nimport type { DurableModuleOptions, DurableRetentionOptions } from './durable.module';\nimport { isDrivingOperator } from './role';\n\nconst DEFAULT_SWEEP_INTERVAL_MS = 60_000;\nconst DEFAULT_BATCH_SIZE = 1_000;\n// Backstop so a single sweep can never loop unbounded. At the default batch size this still drains up\n// to 100k runs per policy per sweep before yielding the event loop to the next tick.\nconst MAX_BATCHES_PER_POLICY = 100;\n\n/**\n * Reject a retention config that would silently misbehave: every policy must set at least one bound,\n * list only terminal statuses (pruning a live status would race the engine), and the status sets must\n * be disjoint (so \"most recent N\" is unambiguous per status group). Throws on the first violation.\n */\nexport function validateRetention(retention: DurableRetentionOptions): void {\n  const seen = new Set<string>();\n  for (const policy of retention.policies) {\n    if (policy.statuses.length === 0) {\n      throw new Error('durable retention: each policy must list at least one status');\n    }\n    if (policy.maxAge == null && policy.maxCount == null) {\n      throw new Error('durable retention: each policy must set maxAge and/or maxCount');\n    }\n    // Fail fast on a typo'd duration string ('7days', '1 month') at boot rather than silently never pruning.\n    if (policy.maxAge != null) parseDuration(policy.maxAge);\n    for (const status of policy.statuses) {\n      if (!TERMINAL_RUN_STATUSES.includes(status)) {\n        throw new Error(\n          `durable retention: status \"${status}\" is not terminal; only ${TERMINAL_RUN_STATUSES.join(\n            ', ',\n          )} can be pruned`,\n        );\n      }\n      if (seen.has(status)) {\n        throw new Error(\n          `durable retention: status \"${status}\" appears in more than one policy; status sets must be disjoint`,\n        );\n      }\n      seen.add(status);\n    }\n  }\n}\n\n/**\n * Hard-prunes terminal run history on an interval per the configured `retention` policies, keeping the\n * `durable_workflow_runs` table (and its children) bounded so the poller's per-tick scans stay cheap.\n *\n * Worker-only (a dashboard-only instance never prunes), separate from the 1s timer poll (defaults to a\n * 60s sweep), and self-draining: each policy is swept in `batchSize` chunks until a batch comes back\n * short. No-ops with a warning if the store adapter doesn't implement `pruneTerminalRuns`.\n */\n@Injectable()\nexport class RetentionPoller implements OnApplicationBootstrap, OnModuleDestroy {\n  private timer?: ReturnType<typeof setInterval>;\n  private sweeping = false;\n\n  constructor(\n    @Inject(STATE_STORE_CANONICAL) private readonly store: StateStore | null,\n    @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n  ) {}\n\n  /** Non-null once past the `isDrivingOperator` guard (which requires `options.store` to be set). */\n  private requireStore(): StateStore {\n    if (!this.store) {\n      throw new Error(\n        'unreachable: STATE_STORE_CANONICAL must resolve a store when options.store is set',\n      );\n    }\n    return this.store;\n  }\n\n  async onApplicationBootstrap(): Promise<void> {\n    // Only a DRIVING operator prunes — a dashboard/dispatch-only instance, or a thin worker with no\n    // `store` at all, must not delete history.\n    if (!isDrivingOperator(this.options)) return;\n    const retention = this.options.retention;\n    if (!retention || retention.policies.length === 0) return;\n    validateRetention(retention);\n    if (typeof this.requireStore().pruneTerminalRuns !== 'function') {\n      console.warn(\n        '[nestjs-durable] `retention` is configured but the store adapter does not implement pruneTerminalRuns; retention is disabled.',\n      );\n      return;\n    }\n    await this.sweep();\n    const intervalMs =\n      retention.sweepInterval != null\n        ? parseDuration(retention.sweepInterval)\n        : DEFAULT_SWEEP_INTERVAL_MS;\n    if (intervalMs > 0) {\n      this.timer = setInterval(() => void this.sweep(), intervalMs);\n      this.timer.unref?.();\n    }\n  }\n\n  onModuleDestroy(): void {\n    if (this.timer) clearInterval(this.timer);\n  }\n\n  private async sweep(): Promise<void> {\n    if (this.sweeping) return; // never overlap two sweeps on this instance\n    const retention = this.options.retention;\n    const store = this.requireStore();\n    const prune = store.pruneTerminalRuns;\n    if (!retention || typeof prune !== 'function') return;\n    this.sweeping = true;\n    try {\n      const batchSize = retention.batchSize ?? DEFAULT_BATCH_SIZE;\n      const now = Date.now();\n      for (const policy of retention.policies) {\n        for (let batch = 0; batch < MAX_BATCHES_PER_POLICY; batch++) {\n          const deleted = await prune.call(store, policy, now, batchSize);\n          if (deleted < batchSize) break; // backlog drained for this policy\n        }\n      }\n    } finally {\n      this.sweeping = false;\n    }\n  }\n}\n","import type { RunGateway, RunReply, RunRequest, RunWaiting } from '@dudousxd/nestjs-durable-core';\n\n/** The narrow slice of `Transport` the responder needs — a tenant's read/control request in, a\n *  correlated reply out. Both are OPTIONAL on the full `Transport` interface (only broker\n *  transports carry the run-request/reply protocol); the caller capability-checks before wiring\n *  this up (see `durable.module.ts`). */\nexport interface RunRequestTransport {\n  onRunRequest(handler: (msg: RunRequest) => Promise<void>): void;\n  publishRunReply(reply: RunReply): Promise<void>;\n}\n\n/**\n * Operator-side consumer of a tenant's {@link RunRequest}s: answers each one against a\n * `RunGateway`, enforcing the tenant boundary before touching the run. For every runId-bearing\n * verb it loads the run via `getRunDetail` FIRST and compares `run.namespace` to the requesting\n * `msg.tenant` — a mismatch short-circuits into a `cross-tenant` error reply WITHOUT calling the\n * verb, so a tenant can never read or act on another tenant's run. `listRuns` is scoped by\n * overwriting the query's `namespace` with the requester's tenant, ignoring whatever the client\n * sent. This is the security boundary of the tenant run gateway — do not weaken it.\n */\nexport class RunRequestResponder {\n  constructor(\n    private readonly transport: RunRequestTransport,\n    private readonly gateway: RunGateway,\n  ) {}\n\n  /** Register the consumer on the transport. Each request is answered independently; a handler\n   *  failure never throws back into the transport (errors are captured into an error reply). */\n  start(): void {\n    this.transport.onRunRequest(async (msg) => {\n      const reply = await this.handle(msg);\n      await this.transport.publishRunReply(reply);\n    });\n  }\n\n  private async handle(msg: RunRequest): Promise<RunReply> {\n    const { body } = msg;\n    if (body.kind === 'listRuns') {\n      // Force the namespace to the requester's tenant — the client-supplied value is discarded,\n      // never merely validated, so a tenant can't widen its own query into another's namespace.\n      const data = await this.gateway.listRuns({ ...body.query, namespace: msg.tenant });\n      return { requestId: msg.requestId, result: { ok: true, data } };\n    }\n\n    if (body.kind === 'workerHealth') {\n      // Not runId-bearing, so it can't ride the getRunDetail namespace check below. Scope by the\n      // group-name convention instead: a tenant's queues are suffixed `<name>@<tenant>`, so keep only\n      // groups ending in the requester's `@<tenant>` — the operator's own bare groups and every other\n      // tenant's are dropped, so a tenant's Workers panel only ever sees ITS OWN queues.\n      const all = await this.gateway.workerHealth();\n      const data = all.filter((h) => h.group.endsWith(`@${msg.tenant}`));\n      return { requestId: msg.requestId, result: { ok: true, data } };\n    }\n\n    if (body.kind === 'waitingFor') {\n      // Bulk, like `listRuns` — but unlike `listRuns` (which forces the query's namespace and so\n      // scopes itself) the caller supplies arbitrary ids, which could probe another tenant's runs. The\n      // gateway's own `waitingFor` has no namespace to filter by, so verify ownership per MATCHED entry\n      // (bounded by how many of the requested ids are actually waiting, never by `runIds.length`) the\n      // same way every other runId-bearing verb does below: `getRunDetail` + a `namespace` check.\n      const all = await this.gateway.waitingFor(body.runIds);\n      const owned = await Promise.all(\n        Object.entries(all).map(async ([runId, waiting]) => {\n          const runDetail = await this.gateway.getRunDetail(runId);\n          return runDetail && runDetail.run.namespace === msg.tenant\n            ? ([runId, waiting] as const)\n            : undefined;\n        }),\n      );\n      const data: Record<string, RunWaiting> = {};\n      for (const entry of owned) {\n        if (entry) data[entry[0]] = entry[1];\n      }\n      return { requestId: msg.requestId, result: { ok: true, data } };\n    }\n\n    // Every remaining verb is runId-bearing. Load the run FIRST — before calling the verb — so a\n    // cross-tenant request never reaches the gateway's mutating methods (cancel/retry/continue/redispatch).\n    const detail = await this.gateway.getRunDetail(body.runId);\n    if (detail && detail.run.namespace !== msg.tenant) {\n      return {\n        requestId: msg.requestId,\n        result: {\n          ok: false,\n          error: { message: 'run belongs to another tenant', code: 'cross-tenant' },\n        },\n      };\n    }\n\n    if (body.kind === 'getRunDetail') {\n      return { requestId: msg.requestId, result: { ok: true, data: detail } };\n    }\n\n    try {\n      const data = await this.callVerb(body);\n      return { requestId: msg.requestId, result: { ok: true, data } };\n    } catch (err) {\n      return {\n        requestId: msg.requestId,\n        result: { ok: false, error: { message: err instanceof Error ? err.message : String(err) } },\n      };\n    }\n  }\n\n  private callVerb(\n    body: Exclude<\n      RunRequest['body'],\n      | { kind: 'listRuns' }\n      | { kind: 'getRunDetail' }\n      | { kind: 'workerHealth' }\n      | { kind: 'waitingFor' }\n    >,\n  ): Promise<unknown> {\n    switch (body.kind) {\n      case 'cancel':\n        return this.gateway.cancel(body.runId, body.opts);\n      case 'retry':\n        return this.gateway.retry(body.runId);\n      case 'continue':\n        return this.gateway.continue(body.runId);\n      case 'retryWithInput':\n        return this.gateway.retryWithInput(body.runId, body.input);\n      case 'redispatch':\n        return this.gateway.redispatchPending(body.runId);\n    }\n  }\n}\n","import {\n  type DurableTopology,\n  type EngineEvent,\n  type GroupHealth,\n  type RunDetail,\n  type RunGateway,\n  type RunListItem,\n  type RunQuery,\n  type RunResult,\n  type RunWaiting,\n  STATE_STORE_CANONICAL,\n  type StateStore,\n  WorkflowEngine,\n  indexWaitersByRun,\n  resolveRunWaiting,\n} from '@dudousxd/nestjs-durable-core';\nimport { Inject, Injectable } from '@nestjs/common';\n\n/**\n * Store-backed `RunGateway` — the operator-side implementation, bound to {@link RUN_GATEWAY} on\n * worker/drive instances. Reuses `DashboardService`'s six read/control method bodies verbatim\n * (`dashboard.service.ts:76-172`), so a consumer that only needs the bounded `RunGateway` surface\n * (the `RunRequestResponder`, a thin controller) doesn't have to depend on the dashboard package.\n */\n@Injectable()\nexport class StoreRunGateway implements RunGateway {\n  constructor(\n    @Inject(STATE_STORE_CANONICAL) private readonly store: StateStore,\n    private readonly engine: WorkflowEngine,\n  ) {}\n\n  topology(): DurableTopology {\n    return { role: 'control-plane' };\n  }\n\n  async getRunDetail(runId: string): Promise<RunDetail | null> {\n    const run = await this.store.getRun(runId);\n    if (!run) return null;\n    const [timeline, children] = await Promise.all([\n      this.store.listCheckpoints(runId),\n      this.engine.getRunChildren(runId), // canonical parent→children edge (shared with cancel cascade)\n    ]);\n    return { run, timeline, children };\n  }\n\n  async listRuns(query: RunQuery): Promise<RunListItem[]> {\n    const runs = await this.store.listRuns(query);\n    // ONE bulk scan of the signal-waiter table (indexed by runId) resolves what each suspended run is\n    // parked on — signal / webhook / child — with no per-run timeline fetch. A timer wait falls back\n    // to the run's own `wakeAt`. Non-suspended (and remote-step-in-flight) runs carry no `waiting`.\n    const waiterByRun = indexWaitersByRun(await this.store.listSignalWaiters(''));\n    return runs.map((run) => {\n      const waiting = resolveRunWaiting(run, waiterByRun);\n      return waiting ? { ...run, waiting } : run;\n    });\n  }\n\n  /**\n   * Bulk-resolve what each of `runIds` is currently parked on — for a consumer with its own filtered/\n   * paginated run listing (e.g. \"which of MY suspended runs are stuck at a breakpoint\") without\n   * re-deriving `listRuns`' waiter scan or querying `durable_step_checkpoints` directly. Mirrors\n   * `listRuns`' waiting computation (the SAME bulk signal-waiter scan + `resolveRunWaiting`), but ALSO\n   * bulk-fetches the currently-suspended runs to check real status: `engine.cancel` (the non-compensate\n   * path) doesn't clear a run's signal waiter row, so a cancelled run can leave an ORPHANED waiter\n   * behind — trusting waiter presence alone would wrongly report a terminal run as still waiting.\n   * Two bulk scans total (never one query per requested id), same as `listRuns`.\n   */\n  async waitingFor(runIds: string[]): Promise<Record<string, RunWaiting>> {\n    if (runIds.length === 0) return {};\n    const idSet = new Set(runIds);\n    const [suspended, waiters] = await Promise.all([\n      this.store.listRuns({ statuses: ['suspended'] }),\n      this.store.listSignalWaiters(''),\n    ]);\n    const waiterByRun = indexWaitersByRun(waiters);\n    const result: Record<string, RunWaiting> = {};\n    for (const run of suspended) {\n      if (!idSet.has(run.id)) continue;\n      const waiting = resolveRunWaiting(run, waiterByRun);\n      if (waiting) result[run.id] = waiting;\n    }\n    return result;\n  }\n\n  /** Every group the engine knows about — unscoped. A tenant proxy's request is scoped by the\n   *  `RunRequestResponder` (to the requester's `@<tenant>` groups); the operator's own UI sees all. */\n  workerHealth(): Promise<GroupHealth[]> {\n    return this.engine.workerHealth();\n  }\n\n  cancel(runId: string, opts?: { compensate?: boolean }): Promise<RunResult | null> {\n    return this.engine.cancel(runId, opts);\n  }\n\n  /** Re-enqueue (dispatch model) instead of resuming inline — a worker picks the run up and replays it. */\n  retry(runId: string): Promise<RunResult | null> {\n    return this.engine.requeue(runId);\n  }\n\n  continue(runId: string): Promise<RunResult | null> {\n    return this.engine.continue(runId);\n  }\n\n  retryWithInput(runId: string, input: unknown): Promise<{ runId: string } | null> {\n    return this.engine.retryWithInput(runId, input);\n  }\n\n  redispatchPending(runId: string): Promise<(RunResult & { redispatched: number }) | null> {\n    return this.engine.redispatchPending(runId);\n  }\n\n  subscribe(runId: string, onEvent: (event: EngineEvent) => void): () => void {\n    return this.engine.subscribe((event) => {\n      if (event.runId === runId) onEvent(event);\n    });\n  }\n}\n","import type { EngineEvent, StateStore, TenantEvent } from '@dudousxd/nestjs-durable-core';\n\n/** The narrow slice of `StateStore` the republisher needs — the `step.*`-event namespace fallback\n *  (see {@link TenantEventRepublisher.namespaceFor}). Mirrors the `RunRequestTransport` narrowing\n *  convention (`run-request-responder.ts`): depend on the smallest surface, not the whole store. */\nexport type RunLookupStore = Pick<StateStore, 'getRun'>;\n\n/** `EngineEvent` types that end a run's lifecycle — no further event for that `runId` will ever\n *  follow one of these, so it is the safe point to drop the run from `TenantEventRepublisher`'s\n *  namespace cache. Matches the terminal members of `EngineEventType` (`packages/core/src/interfaces.ts`)\n *  exactly: `run.suspended` is NOT terminal (a suspended run resumes and emits more events), and\n *  there is no separate `run.cancelled`/`run.dead` event type — both cancellation and dead-lettering\n *  are published as `run.failed`. */\nfunction isTerminalRunEvent(event: EngineEvent): boolean {\n  return event.type === 'run.completed' || event.type === 'run.failed';\n}\n\n/**\n * Re-publishes engine lifecycle events onto a run's per-tenant channel via `publish` (bound from\n * `Transport.publishTenantEvent`), so a store-less tenant worker can live-tail its OWN runs.\n *\n * The run's namespace is read straight off `event.namespace` (stamped by `engine.ts`'s `emit()` on\n * every `run.*` lifecycle event, where the run is already in hand); a `step.*` event doesn't carry\n * it, so those fall back to `store.getRun`. EVERY run's resolved namespace is memoized in\n * `runNamespaces` — including a bare/`default` run — so the store is read at most ONCE per run id\n * regardless of how many `step.*` events that run emits; a `null` cache entry means \"resolved, not\n * a tenant\" (distinct from \"not yet resolved\", i.e. absent from the map). A run's entry is deleted\n * the moment its terminal event is handled, so the cache stays bounded to in-flight runs (tenant and\n * default alike) — the same order of magnitude it was already paying for tenant runs alone, in\n * exchange for turning a per-event store read into a per-run one.\n */\nexport class TenantEventRepublisher {\n  private readonly runNamespaces = new Map<string, string | null>();\n\n  constructor(\n    private readonly store: RunLookupStore,\n    private readonly publish: (event: TenantEvent) => Promise<void>,\n  ) {}\n\n  async handle(event: EngineEvent): Promise<void> {\n    const namespace = await this.namespaceFor(event);\n    // A terminal run emits no further events — drop its cache entry now regardless of whether this\n    // event was itself re-published, so the slot is reclaimed even for a bare/default run.\n    if (isTerminalRunEvent(event)) this.runNamespaces.delete(event.runId);\n    // Skip bare/default runs — no real tenant to re-publish to, and it would just be noise on an\n    // unpartitioned operator.\n    if (!namespace || namespace === 'default') return;\n    await this.publish({ tenant: namespace, event }).catch(() => undefined);\n  }\n\n  private async namespaceFor(event: EngineEvent): Promise<string | undefined> {\n    if (this.runNamespaces.has(event.runId)) {\n      const cached = this.runNamespaces.get(event.runId);\n      return cached === null ? undefined : cached;\n    }\n    if (event.namespace !== undefined) {\n      this.runNamespaces.set(event.runId, event.namespace === 'default' ? null : event.namespace);\n      return event.namespace;\n    }\n    const run = await this.store.getRun(event.runId);\n    const tenantNamespace =\n      run?.namespace !== undefined && run.namespace !== 'default' ? run.namespace : null;\n    this.runNamespaces.set(event.runId, tenantNamespace);\n    return tenantNamespace === null ? undefined : tenantNamespace;\n  }\n}\n","import {\n  DURABLE_OPTIONS_CANONICAL,\n  WorkflowEngine,\n  runSchedules,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n  Inject,\n  Injectable,\n  type OnApplicationBootstrap,\n  type OnModuleDestroy,\n} from '@nestjs/common';\nimport type { DurableModuleOptions } from './durable.module';\nimport { isDrivingOperator } from './role';\n\n/**\n * Resumes suspended runs whose durable timer (`ctx.sleep`) is due, and fires any configured\n * recurring `schedules` — once on boot, then on an interval. Set `timerPollMs` to `0` to disable\n * the interval (e.g. when an external scheduler drives `WorkflowEngine.resumeDueTimers`).\n */\n@Injectable()\nexport class TimerPoller implements OnApplicationBootstrap, OnModuleDestroy {\n  private timer?: ReturnType<typeof setInterval>;\n  private polling = false;\n  private unsubscribeEnqueued?: () => void;\n\n  constructor(\n    private readonly engine: WorkflowEngine,\n    @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n  ) {}\n\n  async onApplicationBootstrap(): Promise<void> {\n    // Drive suspended runs forward when this operator instance is DRIVING (defaults to true) — it\n    // may execute locally or dispatch remotely (group-served / convention), either way it drives. A\n    // non-driving (dashboard-only) operator, or a thin worker with no `store` at all, must not\n    // resume timers — leave that to a driving instance.\n    if (!isDrivingOperator(this.options)) return;\n    // Low-latency dispatch: when a run is enqueued elsewhere (e.g. an API pod), pick it up at once\n    // over the control plane instead of waiting for the next poll tick. Leasing dedups across workers.\n    this.unsubscribeEnqueued = this.engine.onEnqueued((runId) => void this.engine.runOne(runId));\n    await this.poll();\n    const intervalMs = this.options.timerPollMs ?? 1_000;\n    if (intervalMs > 0) {\n      this.timer = setInterval(() => void this.poll(), intervalMs);\n      this.timer.unref?.();\n    }\n  }\n\n  onModuleDestroy(): void {\n    if (this.timer) clearInterval(this.timer);\n    this.unsubscribeEnqueued?.();\n  }\n\n  private async poll(): Promise<void> {\n    if (this.polling) return; // never overlap two sweeps\n    this.polling = true;\n    try {\n      // Pick up runs enqueued elsewhere (an API pod's `start`, or another worker) still `pending`,\n      // reclaim runs orphaned by a crashed worker (lease expired — a live worker renews its lease so\n      // only dead ones are reclaimed), then resume due timers and sweep execution timeouts.\n      await this.engine.runPending();\n      await this.engine.recoverIncomplete();\n      await this.engine.resumeDueTimers();\n      await this.engine.sweepTimeouts();\n      const schedules = this.options.schedules;\n      if (schedules && schedules.length > 0) {\n        await runSchedules(this.engine, schedules, Date.now());\n      }\n    } finally {\n      this.polling = false;\n    }\n  }\n}\n","import { RunGateway } from '@dudousxd/nestjs-durable-core';\n\n/**\n * Cross-lib injection token for the current-request context accessor, owned by\n * `@dudousxd/nestjs-context`. We do NOT import nestjs-context (it is an OPTIONAL\n * peer dependency) — instead we share its well-known token by value so DI\n * resolves the same provider when nestjs-context is installed and present.\n *\n * `Symbol.for(key)` uses the global symbol registry, so this resolves to the\n * SAME symbol instance as nestjs-context's `tokens.ts` (and the identical token\n * declared by `@dudousxd/nestjs-authz`) without any import. The key MUST stay\n * byte-identical with nestjs-context's export.\n */\nexport const CONTEXT_ACCESSOR = Symbol.for('@dudousxd/nestjs-context:accessor');\n\n/**\n * @deprecated Inject the `RunGateway` abstract class directly (it is its own DI token now):\n * `constructor(private readonly gateway: RunGateway)`, provider `{ provide: RunGateway, useClass }`.\n * This symbol is kept as a back-compat alias — it points at the `RunGateway` class, so existing\n * `@Inject(RUN_GATEWAY)` sites resolve the very same token — and will be removed in a future major.\n *\n * `RunGateway` is owned by `@dudousxd/nestjs-durable-core` (a required peer dep of both this package\n * and the dashboard), so the abstract class is a single shared token across packages without the\n * previous `Symbol.for` value-sharing hack.\n */\nexport const RUN_GATEWAY = RunGateway;\n","import {\n  DURABLE_OPTIONS_CANONICAL,\n  RemoteWorkflowExecutor,\n  STATE_STORE_CANONICAL,\n  type StateStore,\n  type WorkflowCtx,\n  WorkflowEngine,\n  type WorkflowRun,\n  bindWorkflowClass,\n  parseDuration,\n  sanitizeQueueToken,\n  tenantGroup,\n  workflowName,\n} from '@dudousxd/nestjs-durable-core';\nimport {\n  Inject,\n  Injectable,\n  type OnApplicationBootstrap,\n  type OnApplicationShutdown,\n  type OnModuleInit,\n} from '@nestjs/common';\nimport { DiscoveryService, MetadataScanner } from '@nestjs/core';\nimport { getOnEvents, isDeadLetterHandler } from './decorators';\nimport { scanWorkflows } from './discovery-helpers';\nimport type { DurableModuleOptions } from './durable.module';\nimport { entityConfigFor, getEntityMeta } from './entity';\nimport { IN_APP_WORKER_BINDING, type InAppWorkerBinding } from './in-app-worker';\nimport { classValidatorInput } from './input-validation';\nimport { isDrivingOperator, isOperatorRole } from './role';\nimport { type DurableStepInterceptor, isStepInterceptor } from './step-interceptor';\n\ntype WorkflowFn = (ctx: WorkflowCtx, input: unknown) => Promise<unknown>;\n\n/**\n * Ensures the schema (auto-schema) and registers `@Workflow` providers on init, resumes runs left\n * incomplete by a previous process once booted, and drains in-flight runs on shutdown.\n *\n * For the shutdown drain to fire, enable Nest's shutdown hooks: `app.enableShutdownHooks()`.\n */\n@Injectable()\nexport class WorkflowRegistrar\n  implements OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown\n{\n  constructor(\n    private readonly discovery: DiscoveryService,\n    private readonly metadataScanner: MetadataScanner,\n    private readonly engine: WorkflowEngine,\n    @Inject(STATE_STORE_CANONICAL) private readonly store: StateStore | null,\n    @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n    // Group-served binding when the app opted into an in-app worker (uniform dispatch); null = inline.\n    @Inject(IN_APP_WORKER_BINDING) private readonly inAppWorker: InAppWorkerBinding | null,\n  ) {}\n\n  async onApplicationBootstrap(): Promise<void> {\n    // Recover runs left incomplete by a crash/deploy when this operator instance is DRIVING\n    // (defaults to true). A non-driving (dashboard-only) operator, or a thin worker with no `store`\n    // at all, must not pick up and re-run workflows — leave that to a driving instance.\n    if (!isDrivingOperator(this.options)) return;\n    await this.engine.recoverIncomplete();\n  }\n\n  /** On deploy/shutdown: stop picking up new runs and wait for in-flight ones to settle, then close\n   *  the transport(s) so the broker workers stop consuming and connections are released. Closing\n   *  AFTER the drain keeps the transport alive while in-flight runs dispatch/await their remote steps.\n   *  Operator only — a thin worker (no `store`) holds no engine to drain. */\n  async onApplicationShutdown(): Promise<void> {\n    if (!isOperatorRole(this.options)) return;\n    await this.engine.drain(this.options.shutdownTimeoutMs);\n    const transports = [\n      this.options.transport,\n      ...(this.options.transports ?? []).map((t) => t.transport),\n    ];\n    await Promise.allSettled(transports.map((t) => t?.close?.()));\n  }\n\n  async onModuleInit(): Promise<void> {\n    // Operator only — a thin worker (no `store`) registers its bodies on a `DurableWorkerRuntime`\n    // instead (see `durable-worker.module.ts`'s `ThinWorkflowRegistrar`), never on this engine.\n    if (!isOperatorRole(this.options)) return;\n    const store = this.store;\n    if (!store) {\n      throw new Error(\n        'unreachable: STATE_STORE_CANONICAL must resolve a store when options.store is set',\n      );\n    }\n    if (this.options.autoSchema !== false) {\n      await store.ensureSchema?.();\n    }\n    // Maps a workflow name to the workflow its dead runs route to. Built from each `@Workflow`'s\n    // inline `@DeadLetter()` method (preferred) or its `deadLetterWorkflow` reference; the\n    // module-level `deadLetterWorkflow` is the fallback applied in the onDead listener below.\n    const deadLetterByWorkflow = new Map<string, string>();\n\n    // Pre-pass: wire interceptors and register @Entity providers (engine-only concerns).\n    for (const wrapper of this.discovery.getProviders()) {\n      const { instance } = wrapper;\n      if (!instance || typeof instance !== 'object') continue;\n      if (isStepInterceptor(instance.constructor)) {\n        const interceptor = instance as DurableStepInterceptor;\n        this.engine.use((invocation, next) => interceptor.intercept(invocation, next));\n      }\n      const entityMeta = getEntityMeta(instance.constructor);\n      if (entityMeta) {\n        this.engine.registerEntity(entityMeta.name, entityConfigFor(instance.constructor));\n      }\n    }\n\n    // Workflow registration pass — shared scan, engine-specific registration callback.\n    scanWorkflows(this.discovery, (meta, workflow) => {\n      // Input validation: a custom `validateInput` wins; otherwise build one from the class-validator\n      // `inputSchema` DTO (lazy — class-validator is only required if a workflow uses inputSchema).\n      const validateInput =\n        meta.validateInput ??\n        (meta.inputSchema ? classValidatorInput(meta.inputSchema) : undefined);\n      const eventBatch = meta.debounce\n        ? ({ mode: 'debounce', windowMs: parseDuration(meta.debounce) } as const)\n        : meta.batch\n          ? ({\n              mode: 'batch',\n              maxSize: meta.batch.maxSize,\n              windowMs: parseDuration(meta.batch.within),\n            } as const)\n          : undefined;\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      const workflowCtor: object = (workflow as any).constructor as object;\n      this.engine.register(meta.name, meta.version, (ctx, input) => workflow.run(ctx, input), {\n        tags: meta.tags,\n        singleton: meta.singleton,\n        executionTimeout: meta.executionTimeout,\n        requires: meta.requires,\n        validateInput,\n        searchAttributesSchema: meta.searchAttributes,\n        onEvent: getOnEvents(meta, workflowCtor),\n        eventBatch,\n        // Uniform dispatch (opt-in): when an in-app worker is configured, register the body GROUP-SERVED\n        // so the engine dispatches its turns over the transport instead of running it inline; the\n        // co-located worker consumer (Task 5: one queue PER REGISTERED NAME) replays the same body.\n        // The routing token — and the executor that dispatches under it — MUST be keyed by THIS\n        // workflow's own name (`tenantGroup(sanitizeQueueToken(meta.name), partition)`), not a single\n        // group shared across every discovered `@Workflow`: a fixed shared token would dispatch every\n        // workflow's turns to one queue while the co-located worker subscribes one queue per name,\n        // so a turn dispatched under the wrong token would never be consumed. Absent → the inline fast\n        // path (unchanged).\n        ...(this.inAppWorker\n          ? {\n              group: tenantGroup(sanitizeQueueToken(meta.name), this.inAppWorker.partition),\n              executor: new RemoteWorkflowExecutor(\n                this.inAppWorker.transport,\n                meta.name,\n                this.inAppWorker.partition,\n              ),\n            }\n          : {}),\n      });\n\n      // Class-first statics (`MyWorkflow.start()` / `.execute()`): bind the class to THIS engine,\n      // so the statics resolve the engine that registered it — no global engine singleton.\n      bindWorkflowClass(workflowCtor as new () => object, {\n        start: (name, input, runId, opts) => this.engine.start(name, input, runId, opts),\n        waitForRun: (runId, opts) => this.engine.waitForRun(runId, opts),\n      });\n\n      const inline = this.findDeadLetterHandler(workflow as unknown as object);\n      if (inline && meta.deadLetterWorkflow) {\n        // Two dead-letter targets for one workflow is ambiguous config, not a precedence question —\n        // fail fast at boot rather than silently picking one.\n        throw new Error(\n          `@Workflow ${meta.name} declares both an inline @DeadLetter() method and a deadLetterWorkflow option. Use one: the inline handler, or the reference.`,\n        );\n      }\n      if (inline) {\n        // The inline handler is itself a durable workflow, registered as `<name>.dlq`, so it gets\n        // checkpointing and a dashboard run linked to the dead run via the `dlq:<runId>` id.\n        const dlqName = `${meta.name}.dlq`;\n        this.engine.register(dlqName, meta.version, inline);\n        deadLetterByWorkflow.set(meta.name, dlqName);\n      } else if (meta.deadLetterWorkflow) {\n        deadLetterByWorkflow.set(meta.name, workflowName(meta.deadLetterWorkflow));\n      }\n    });\n\n    this.installDeadLetterRouting(deadLetterByWorkflow);\n  }\n\n  /** Returns the instance's `@DeadLetter()` method bound to the instance, or undefined if none. */\n  private findDeadLetterHandler(instance: object): WorkflowFn | undefined {\n    const prototype = Object.getPrototypeOf(instance);\n    for (const methodName of this.metadataScanner.getAllMethodNames(prototype)) {\n      const method = (instance as Record<string, unknown>)[methodName];\n      if (typeof method === 'function' && isDeadLetterHandler(method)) {\n        return (ctx, input) => method.call(instance, ctx, input);\n      }\n    }\n    return undefined;\n  }\n\n  /**\n   * Installs a single onDead listener that routes a dead run to its workflow's handler (from the\n   * map) or the module-level `deadLetterWorkflow` default. The handler is started idempotently with\n   * a `dlq:<runId>` id, so re-recovery never double-dispatches.\n   */\n  private installDeadLetterRouting(byWorkflow: Map<string, string>): void {\n    const fallback = this.options.deadLetterWorkflow\n      ? workflowName(this.options.deadLetterWorkflow)\n      : undefined;\n    if (byWorkflow.size === 0 && !fallback) return;\n    this.engine.onDead((run: WorkflowRun) => {\n      const target = byWorkflow.get(run.workflow) ?? fallback;\n      if (!target) return;\n      void this.engine\n        .start(\n          target,\n          { deadRunId: run.id, workflow: run.workflow, input: run.input, error: run.error },\n          `dlq:${run.id}`,\n          // Route the dead-letter handler to the dead run's OWN tenant so an operator dispatches it to\n          // that tenant's worker group (`target@<tenant>`), not the bare group.\n          { namespace: run.namespace },\n        )\n        .catch(() => undefined);\n    });\n  }\n}\n","type ClassCtor = new (...args: any[]) => object;\n\n/**\n * Build a `validateInput` from a class-validator DTO class — the same `plainToInstance` + `validate`\n * NestJS runs in controllers. `class-validator` and `class-transformer` are lazy-required optional\n * peers (only needed if you use `@Workflow({ inputSchema })`), so they stay out of the type graph.\n */\nexport function classValidatorInput(cls: ClassCtor): (input: unknown) => Promise<void> {\n  let cv: any;\n  let ct: any;\n  try {\n    cv = require('class-validator');\n    ct = require('class-transformer');\n  } catch {\n    throw new Error(\n      '@Workflow({ inputSchema }) needs the optional peers \"class-validator\" and \"class-transformer\" — install them, or pass a `validateInput` function instead.',\n    );\n  }\n  return async (input: unknown) => {\n    const instance = ct.plainToInstance(cls, input);\n    const errors = await cv.validate(instance, { whitelist: true });\n    if (errors.length > 0) {\n      const message = errors\n        .map((e: any) => Object.values(e.constraints ?? { _: e.property }).join(', '))\n        .join('; ');\n      throw new Error(`invalid input for workflow: ${message}`);\n    }\n  };\n}\n","import type { StepInvocation } from '@dudousxd/nestjs-durable-core';\nimport 'reflect-metadata';\n\nexport const STEP_INTERCEPTOR_METADATA = Symbol('nestjs-durable:step-interceptor');\n\n/**\n * The shape a `@StepInterceptor()` provider must implement: `intercept(invocation, next)` wraps the\n * real execution of every local `ctx.step` (call `next()` to run the step body / next interceptor,\n * and return — or transform — its result). The engine-level {@link StepInterceptor} primitive, with\n * NestJS dependency injection.\n */\nexport interface DurableStepInterceptor {\n  intercept(invocation: StepInvocation, next: () => Promise<unknown>): Promise<unknown>;\n}\n\n/**\n * Marks an `@Injectable()` class as a durable step interceptor. The module discovers it on boot and\n * registers its `intercept` method with the engine (so it can inject loggers/tracers/etc.). First\n * declared is outermost. Interceptors fire only when a step actually executes, never on replay.\n */\nexport function StepInterceptor(): ClassDecorator {\n  return (target) => {\n    Reflect.defineMetadata(STEP_INTERCEPTOR_METADATA, true, target);\n  };\n}\n\n// biome-ignore lint/complexity/noBannedTypes: matches reflect-metadata's class target type\nexport function isStepInterceptor(target: Function): boolean {\n  return Reflect.getMetadata(STEP_INTERCEPTOR_METADATA, target) === true;\n}\n","import { randomUUID } from 'node:crypto';\nimport {\n  type RunResult,\n  type StartOptions,\n  type WorkflowClass,\n  WorkflowEngine,\n  type WorkflowInputOf,\n} from '@dudousxd/nestjs-durable-core';\nimport { Injectable } from '@nestjs/common';\n\n/** Public entry point for starting and resuming workflow runs. */\n@Injectable()\nexport class WorkflowService {\n  constructor(private readonly engine: WorkflowEngine) {}\n\n  /**\n   * Enqueue a workflow run — it creates the run (`pending`) and returns `{ runId, status: 'pending' }`\n   * immediately; a worker executes the body (so the caller never blocks on workflow logic). Use\n   * {@link waitForRun} when you need the outcome. Pass the workflow's **class**\n   * (`start(CheckoutWorkflow, input)`) for a typed input + refactor-safety, or a **name** string for a\n   * cross-runtime workflow. `runId` defaults to a random id; pass your own to make the start idempotent\n   * (a redelivery returns the existing run). `opts.tags` are merged with the workflow's static\n   * `@Workflow({ tags })`; `opts.searchAttributes` stamp typed, queryable run data.\n   */\n  start<C extends WorkflowClass>(\n    workflow: C,\n    input: WorkflowInputOf<C>,\n    runId?: string,\n    opts?: StartOptions,\n  ): Promise<RunResult>;\n  start(workflow: string, input: unknown, runId?: string, opts?: StartOptions): Promise<RunResult>;\n  start(\n    workflow: string,\n    input: unknown,\n    runId: string = randomUUID(),\n    opts?: StartOptions,\n  ): Promise<RunResult> {\n    return this.engine.start(workflow, input, runId, opts);\n  }\n\n  resume(runId: string): Promise<RunResult> {\n    return this.engine.resume(runId);\n  }\n\n  /**\n   * Resolve once a run settles — terminal (completed/failed/cancelled/dead) or suspended. `start`\n   * only enqueues (a worker runs the body), so pair them when a request needs the outcome:\n   * `const { runId } = await svc.start(...); const result = await svc.waitForRun(runId)`.\n   */\n  waitForRun(runId: string, opts?: { timeoutMs?: number }): Promise<RunResult> {\n    return this.engine.waitForRun(runId, opts);\n  }\n\n  /** Deliver an external signal (e.g. from a webhook) to the run waiting on `token`. */\n  signal(token: string, payload: unknown): Promise<RunResult | null> {\n    return this.engine.signal(token, payload);\n  }\n\n  /**\n   * Ensure a run exists for `runId`, then deliver a signal to it — race-free (the signal is buffered\n   * until the run reaches its `waitForSignal`). The durable-entity / accumulator pattern: one\n   * long-lived run per key fed events by many calls. See {@link WorkflowEngine.signalWithStart}.\n   */\n  signalWithStart<C extends WorkflowClass>(\n    workflow: C,\n    input: WorkflowInputOf<C>,\n    runId: string,\n    signal: { token: string; payload?: unknown },\n    opts?: StartOptions,\n  ): Promise<{ runId: string }>;\n  signalWithStart(\n    workflow: string,\n    input: unknown,\n    runId: string,\n    signal: { token: string; payload?: unknown },\n    opts?: StartOptions,\n  ): Promise<{ runId: string }>;\n  signalWithStart(\n    workflow: string,\n    input: unknown,\n    runId: string,\n    signal: { token: string; payload?: unknown },\n    opts?: StartOptions,\n  ): Promise<{ runId: string }> {\n    return this.engine.signalWithStart(workflow, input, runId, signal, opts);\n  }\n\n  /**\n   * Publish a named event. Resumes runs waiting on it via `ctx.waitForEvent(name, { match })` and\n   * starts a fresh run of every workflow subscribed via `@Workflow({ onEvent })` / `@OnDurableEvent` (the\n   * payload becomes its input). Pass `opts.id` to dedupe redeliveries. Returns how many runs it\n   * touched (resumed + started).\n   *\n   * Reliable by default: a publish that touches NOBODY (no live waiter, no subscriber) buffers ONE\n   * copy so a LATER `waitForEvent(name, { match })` still consumes it instead of it being dropped —\n   * see {@link WorkflowEngine.publishEvent}'s full semantics doc. Pass `opts.buffer: false` to opt out.\n   */\n  publishEvent(\n    name: string,\n    payload: unknown,\n    opts?: { id?: string; buffer?: boolean },\n  ): Promise<number> {\n    return this.engine.publishEvent(name, payload, opts);\n  }\n}\n"],"mappings":"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","names":["import_nestjs_durable_core","WORKFLOW_METADATA","Symbol","Workflow","options","target","meta","name","version","deadLetterWorkflow","tags","singleton","executionTimeout","inputSchema","validateInput","searchAttributes","onEvent","debounce","batch","requires","Reflect","defineMetadata","Object","defineProperty","WORKFLOW_NAME_KEY","value","configurable","getWorkflowMeta","getMetadata","DURABLE_STEP_METADATA","stepConfigFrom","config","retries","backoff","backoffMs","backoffMaxMs","jitter","timeoutMs","hasAnyField","values","some","undefined","Step","nameOrOptions","propertyKey","descriptor","derivedName","String","input","output","DURABLE_STEP_NAME","DURABLE_STEP_CONFIG","DurableStep","getDurableStepMeta","method","DEAD_LETTER_METADATA","DeadLetter","_target","_propertyKey","isDeadLetterHandler","ON_EVENT_METADATA","OnDurableEvent","events","existing","OnEvent","getOnEvents","fromDecorator","Set","attributesOf","workflow","run","meta","getWorkflowMeta","Error","name","searchAttributes","readSearchAttributes","import_nestjs_durable_core","DurableStartClient","tenant","options","deps","partition","start","workflow","input","runId","globalThis","crypto","randomUUID","opts","name","workflowName","startRun","connection","prefix","undefined","tags","searchAttributes","status","cancel","_runId","tenantUnsupported","deleteRun","resume","waitForRun","_opts","signal","_token","_payload","signalWithStart","_workflow","_input","_signal","publishEvent","_name","method","Promise","reject","Error","import_durable_worker","import_nestjs_durable_core","import_common","scanWorkflows","discovery","register","wrapper","getProviders","instance","meta","getWorkflowMeta","workflow","run","Error","name","scanSteps","scanner","prototype","Object","getPrototypeOf","methodName","getAllMethodNames","method","getDurableStepMeta","boundMethod","handler","input","log","validInput","parse","output","call","RUN_REDIS_WORKER","Symbol","DURABLE_WORKER_RUNNERS","isPureThinWorker","options","store","undefined","connection","ThinWorkflowRegistrar","discovery","runtime","onModuleInit","scanWorkflows","meta","instance","registerWorkflow","name","ctx","input","run","ThinStepRegistrar","metadataScanner","scanSteps","handler","registerStep","ThinWorkerBootstrap","runners","runRedisWorker","runnersSink","onApplicationBootstrap","handle","partition","prefix","instanceId","concurrency","push","onApplicationShutdown","Promise","allSettled","map","h","close","thinWorkerProviders","provide","DurableWorkerRuntime","useFactory","workflowGroup","inject","DURABLE_OPTIONS_CANONICAL","useValue","defaultRunRedisWorker","tenantGatewayUnavailable","method","reject","Error","unavailableRunGateway","topology","role","getRunDetail","_runId","listRuns","_query","waitingFor","_runIds","workerHealth","cancel","retry","continue","retryWithInput","_input","redispatchPending","subscribe","_onEvent","import_nestjs_durable_core","import_common","import_core","import_nestjs_durable_core","import_common","import_core","isOperatorRole","options","store","undefined","isDrivingOperator","drive","supportsHandle","transport","handle","DurableStepRegistrar","discovery","metadataScanner","options","onModuleInit","isDrivingOperator","scanSteps","meta","handler","name","partition","import_nestjs_durable_core","import_common","import_reflect_metadata","ENTITY_METADATA","Symbol","ENTITY_ON_METADATA","Entity","options","target","Reflect","defineMetadata","On","op","propertyKey","ctor","ops","getMetadata","Map","set","getEntityMeta","entityConfigFor","Cls","handlers","method","state","arg","Object","setPrototypeOf","prototype","fn","Error","call","initialState","EntityService","engine","signal","name","key","signalEntity","getState","getEntityState","import_durable_worker","import_nestjs_durable_core","import_common","import_core","IN_APP_WORKER_BINDING","Symbol","IN_APP_WORKER_RUNTIME","IN_APP_RUN_REDIS_WORKER","IN_APP_WORKER_RUNNERS","isCoLocatedWorker","options","store","undefined","connection","inAppWorkerBinding","transport","dispatchWorkflowTask","onDecision","Error","partition","InAppWorkerBootstrap","runners","discovery","metadataScanner","runtime","runRedisWorker","runnersSink","onModuleInit","scanWorkflows","meta","instance","registerWorkflow","name","ctx","input","run","scanSteps","handler","registerStep","onApplicationBootstrap","handle","prefix","instanceId","concurrency","push","onApplicationShutdown","Promise","allSettled","map","close","inAppWorkerProviders","provide","useFactory","inject","TRANSPORT_CANONICAL","DURABLE_OPTIONS_CANONICAL","DurableWorkerRuntime","workflowGroup","useValue","defaultRunRedisWorker","import_common","ProxyRunGateway","pending","Map","transport","tenant","timeoutMs","onRunReply","reply","handleReply","get","requestId","clearTimeout","timer","delete","result","ok","resolve","data","reject","Error","error","message","request","body","Promise","globalThis","crypto","randomUUID","setTimeout","kind","set","dispatchRunRequest","catch","stillPending","String","topology","role","getRunDetail","runId","listRuns","query","workerHealth","waitingFor","runIds","cancel","opts","undefined","retry","continue","retryWithInput","input","redispatchPending","subscribe","onEvent","unsubscribe","onTenantEvent","evt","event","import_nestjs_durable_core","import_common","DEFAULT_SWEEP_INTERVAL_MS","DEFAULT_BATCH_SIZE","MAX_BATCHES_PER_POLICY","validateRetention","retention","seen","Set","policy","policies","statuses","length","Error","maxAge","maxCount","parseDuration","status","TERMINAL_RUN_STATUSES","includes","join","has","add","RetentionPoller","timer","sweeping","store","options","requireStore","onApplicationBootstrap","isDrivingOperator","pruneTerminalRuns","console","warn","sweep","intervalMs","sweepInterval","setInterval","unref","onModuleDestroy","clearInterval","prune","batchSize","now","Date","batch","deleted","call","RunRequestResponder","transport","gateway","start","onRunRequest","msg","reply","handle","publishRunReply","body","kind","data","listRuns","query","namespace","tenant","requestId","result","ok","all","workerHealth","filter","h","group","endsWith","waitingFor","runIds","owned","Promise","Object","entries","map","runId","waiting","runDetail","getRunDetail","run","undefined","entry","detail","error","message","code","callVerb","err","Error","String","cancel","opts","retry","continue","retryWithInput","input","redispatchPending","import_nestjs_durable_core","import_common","StoreRunGateway","store","engine","topology","role","getRunDetail","runId","run","getRun","timeline","children","Promise","all","listCheckpoints","getRunChildren","listRuns","query","runs","waiterByRun","indexWaitersByRun","listSignalWaiters","map","waiting","resolveRunWaiting","waitingFor","runIds","length","idSet","Set","suspended","waiters","statuses","result","has","id","workerHealth","cancel","opts","retry","requeue","continue","retryWithInput","input","redispatchPending","subscribe","onEvent","event","isTerminalRunEvent","event","type","TenantEventRepublisher","runNamespaces","Map","store","publish","handle","namespace","namespaceFor","delete","runId","tenant","catch","undefined","has","cached","get","set","run","getRun","tenantNamespace","import_nestjs_durable_core","import_common","TimerPoller","timer","polling","unsubscribeEnqueued","engine","options","onApplicationBootstrap","isDrivingOperator","onEnqueued","runId","runOne","poll","intervalMs","timerPollMs","setInterval","unref","onModuleDestroy","clearInterval","runPending","recoverIncomplete","resumeDueTimers","sweepTimeouts","schedules","length","runSchedules","Date","now","import_nestjs_durable_core","CONTEXT_ACCESSOR","Symbol","for","RUN_GATEWAY","RunGateway","import_nestjs_durable_core","import_common","import_core","classValidatorInput","cls","cv","ct","require","Error","input","instance","plainToInstance","errors","validate","whitelist","length","message","map","e","Object","values","constraints","_","property","join","import_reflect_metadata","STEP_INTERCEPTOR_METADATA","Symbol","StepInterceptor","target","Reflect","defineMetadata","isStepInterceptor","getMetadata","WorkflowRegistrar","discovery","metadataScanner","engine","store","options","inAppWorker","onApplicationBootstrap","isDrivingOperator","recoverIncomplete","onApplicationShutdown","isOperatorRole","drain","shutdownTimeoutMs","transports","transport","map","t","Promise","allSettled","close","onModuleInit","Error","autoSchema","ensureSchema","deadLetterByWorkflow","Map","wrapper","getProviders","instance","isStepInterceptor","interceptor","use","invocation","next","intercept","entityMeta","getEntityMeta","registerEntity","name","entityConfigFor","scanWorkflows","meta","workflow","validateInput","inputSchema","classValidatorInput","undefined","eventBatch","debounce","mode","windowMs","parseDuration","batch","maxSize","within","workflowCtor","register","version","ctx","input","run","tags","singleton","executionTimeout","requires","searchAttributesSchema","searchAttributes","onEvent","getOnEvents","group","tenantGroup","sanitizeQueueToken","partition","executor","RemoteWorkflowExecutor","bindWorkflowClass","start","runId","opts","waitForRun","inline","findDeadLetterHandler","deadLetterWorkflow","dlqName","set","workflowName","installDeadLetterRouting","prototype","Object","getPrototypeOf","methodName","getAllMethodNames","method","isDeadLetterHandler","call","byWorkflow","fallback","size","onDead","target","get","deadRunId","id","error","namespace","catch","import_nestjs_durable_core","import_common","WorkflowService","engine","start","workflow","input","runId","randomUUID","opts","resume","waitForRun","signal","token","payload","signalWithStart","publishEvent","name","resolveAccessor","moduleRef","get","CONTEXT_ACCESSOR","strict","undefined","carrierFromAccessor","accessor","carrier","traceId","tenantId","userRef","isContextRuntime","x","deserialize","resolveContextRuntime","specifier","mod","Context","isControlPlane","publishControl","onControl","isScopeableStore","store","withScope","scopedStore","options","scopeReads","namespace","assertValidRole","hasStore","hasConnection","connection","Error","transport","transports","assertValidTopology","topology","role","partition","tenant","resolveTopology","servingPartition","resolveTopologyPreset","RunGatewayBootstrap","unsubscribe","engine","gateway","onApplicationBootstrap","isDrivingOperator","onRunRequest","publishRunReply","publishTenantEvent","runRequestTransport","bind","RunRequestResponder","start","republisher","TenantEventRepublisher","subscribe","event","handle","onModuleDestroy","DurableModule","forRoot","assertValid","build","provide","DURABLE_OPTIONS_CANONICAL","useValue","forRootAsync","useFactory","args","resolved","inject","optionsProvider","module","global","imports","DiscoveryModule","providers","STATE_STORE_CANONICAL","TRANSPORT_CANONICAL","STATE_STORE","useExisting","TRANSPORT","DURABLE_OPTIONS","WorkflowEngine","DurableStartClient","primary","context","runtime","rehydrate","fn","Object","keys","length","controlPlane","leaseMs","admission","maxRecoveryAttempts","remoteAdvanceSilenceMs","instanceId","webhookUrl","traceparent","compensationRetries","runDispatcher","drive","dispatch","queue","queues","registerQueue","ModuleRef","WorkflowService","EntityService","WorkflowRegistrar","DurableStepRegistrar","TimerPoller","RetentionPoller","RunGateway","StoreRunGateway","ProxyRunGateway","runGatewayTimeoutMs","unavailableRunGateway","inAppWorkerProviders","thinWorkerProviders","exports","import_nestjs_durable_core"]}