@dudousxd/nestjs-durable 0.45.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/attributes-of.ts","../src/decorators.ts","../src/origin.ts","../src/durable-start-client.ts","../src/worker-sdk.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 DynamicWorkflowCtx,\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 type ConcurrencyConfig,\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';\nimport { declaringFile, rememberDeclaringFile } from './origin';\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 /** Start-time concurrency quota (rejecting). See `WorkflowOptions`. */\n concurrency?: ConcurrencyConfig | 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 * A start-time concurrency QUOTA: at most `limit` runs sharing a key in flight, and a start over it\n * is REJECTED (`ConcurrencyLimitError`, nothing created) rather than queued. e.g.\n * `concurrency: { key: (input) => `tenant:${input.tenantId}`, limit: 8 }` caps what one tenant can\n * have executing at once. The key is shared across workflows; `limit` may be a (async) function of\n * the key for per-plan limits; `countStatuses` narrows what occupies a slot (default: every\n * non-terminal status). Use `singleton` instead when excess runs should wait their turn.\n */\n concurrency?: ConcurrencyConfig;\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 // Captured HERE, in the factory, because this is the one moment we are provably executing inside\n // the module that declares the workflow — by the time a registrar scans providers, the only thing\n // left of that module is a class object with no file on it. The path is resolved to the owning\n // package lazily, at registration (see `origin.ts`); a runtime that yields no usable frame simply\n // leaves the workflow unattributed, which the registrar reports at boot.\n const declaredIn = declaringFile(Workflow);\n return (target) => {\n rememberDeclaringFile(target, declaredIn);\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 concurrency: options.concurrency,\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 { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Derives a workflow's **origin** — the npm package that declared it (`@dudousxd/nestjs-agent`, the\n * host app's own `package.json` `name`, …) — so every run can say which lib produced it (see\n * {@link import('@dudousxd/nestjs-durable-core').WorkflowRun.origin}).\n *\n * WHY IT IS DERIVED AND NOT DECLARED: anything a lib has to opt into (a `@Workflow({ tags })`\n * convention, a registry of known packages) is already wrong in the field the day a lib forgets, and\n * a facet that lists two of the five libs actually running is worse than no facet at all. So the only\n * acceptable input is one the lib cannot fail to provide: the file it declared its `@Workflow` class\n * in. That file is captured by {@link declaringFile} at decoration time — the one moment we are\n * guaranteed to be executing inside the declaring module — and mapped here to the nearest enclosing\n * `package.json` `name`.\n *\n * WHY NOT THE NEST MODULE: the `InstanceWrapper` a `DiscoveryService` scan hands back does name the\n * module a provider came from (`wrapper.host.metatype`), but a module class is just a class — it\n * carries no file location, and the app's own module is a perfectly normal place to register a lib's\n * workflow, which would then be attributed to the app. The declaring file of the `@Workflow` class is\n * both available and strictly more precise, so the wrapper is used only to reach that class.\n *\n * EVERY step here can fail, and each failure yields `undefined` (= unknown) rather than a guess:\n * - a runtime without V8's `Error.captureStackTrace`, or an app that set `Error.stackTraceLimit = 0`\n * - a declaring frame that is not a real file (`node:` internals, `eval`, `<anonymous>`) — notably a\n * single-file bundle can also collapse several packages into one file, and where the file IS real\n * but shared, the origin will be the bundle's package: that is the one shape this cannot detect\n * - no `package.json` carrying a `name` anywhere above the file\n * `WorkflowRegistrar` reports the workflows that came back unattributed once, at boot.\n */\n\n/** The file each decorated class was declared in, keyed by the class itself. */\nconst declaringFileByClass = new WeakMap<object, string>();\n\n/** Memoized directory → owning package name (or `undefined` when the walk found none). */\nconst packageNameByDir = new Map<string, string | undefined>();\n\ntype AnyFunction = (...args: never[]) => unknown;\n\n/**\n * The absolute path of the file that is calling `boundary` right now — i.e. the file whose top level\n * is applying a decorator, when called from inside that decorator's factory. `boundary` is clipped\n * off the captured stack (`Error.captureStackTrace`'s second argument), so the first remaining frame\n * is the caller's, with no frame counting to go stale when this file is refactored.\n *\n * Returns `undefined` on any runtime that does not give us a usable frame.\n */\nexport function declaringFile(boundary: AnyFunction): string | undefined {\n const capture = Error.captureStackTrace;\n if (typeof capture !== 'function') return undefined;\n const holder: { stack?: string } = {};\n capture(holder, boundary);\n return topFrameFile(holder.stack);\n}\n\n/** Records the file `target` was declared in, so {@link originOfClass} can resolve it later. */\nexport function rememberDeclaringFile(target: object, file: string | undefined): void {\n if (file) declaringFileByClass.set(target, file);\n}\n\n/**\n * The package that declared `target`, or `undefined` if it cannot be resolved with confidence.\n *\n * Walks the prototype chain, matching `Reflect.getMetadata`'s inheritance: a subclass that inherits\n * its `@Workflow` metadata from a decorated base also inherits that base's origin, because the base\n * is where the decorated body was declared.\n */\nexport function originOfClass(target: unknown): string | undefined {\n for (\n let ctor: unknown = target;\n typeof ctor === 'function' || (typeof ctor === 'object' && ctor !== null);\n ctor = Object.getPrototypeOf(ctor)\n ) {\n const file = declaringFileByClass.get(ctor);\n if (file) return packageNameFor(file);\n }\n return undefined;\n}\n\n/**\n * The package that declared the workflow behind a discovered provider. Structurally typed against\n * Nest's `InstanceWrapper` (the shape `DiscoveryService.getProviders()` returns) so this package does\n * not deep-import an internal Nest path: `metatype` is the registered class, and `instance.constructor`\n * covers a provider whose metatype is absent (a `useValue`/factory-built instance of a decorated class).\n */\nexport function originOfProvider(wrapper: {\n metatype?: unknown;\n instance?: unknown;\n}): string | undefined {\n const fromMetatype = originOfClass(wrapper.metatype);\n if (fromMetatype) return fromMetatype;\n const instance = wrapper.instance;\n if (typeof instance !== 'object' || instance === null) return undefined;\n return originOfClass(instance.constructor);\n}\n\n/**\n * The `name` of the nearest `package.json` at or above `file`'s directory. For a lib this is the\n * installed package (`…/node_modules/@scope/lib/dist/index.cjs` → `@scope/lib`, pnpm's nested layout\n * included); for a workflow written in the app it is the app's own name. Memoized per directory —\n * the walk hits the filesystem once per distinct directory, not once per workflow.\n */\nfunction packageNameFor(file: string): string | undefined {\n const start = dirname(file);\n const visited: string[] = [];\n let dir = start;\n for (;;) {\n if (packageNameByDir.has(dir)) {\n const cached = packageNameByDir.get(dir);\n for (const seen of visited) packageNameByDir.set(seen, cached);\n return cached;\n }\n visited.push(dir);\n const name = readPackageName(join(dir, 'package.json'));\n if (name !== undefined) {\n for (const seen of visited) packageNameByDir.set(seen, name);\n return name;\n }\n const parent = dirname(dir);\n // `dirname('/')` is `'/'` — the filesystem root is the end of the walk.\n if (parent === dir) {\n for (const seen of visited) packageNameByDir.set(seen, undefined);\n return undefined;\n }\n dir = parent;\n }\n}\n\n/** The `name` field of a `package.json`, or `undefined` if it is missing, unreadable or unnamed. */\nfunction readPackageName(path: string): string | undefined {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // A malformed package.json is not ours to fail on — keep walking up.\n return undefined;\n }\n if (!isRecord(parsed)) return undefined;\n const name = parsed.name;\n return typeof name === 'string' && name.length > 0 ? name : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * The file named by the FIRST frame of a captured stack. Deliberately only the first: with the\n * capture boundary clipped, frame 0 IS the declaring site, and falling through to a deeper frame\n * when it is unparseable would attribute the workflow to whatever happened to be further up the\n * stack (the test runner, Nest's loader) — a confident-looking wrong answer.\n */\nfunction topFrameFile(stack: string | undefined): string | undefined {\n if (typeof stack !== 'string') return undefined;\n for (const line of stack.split('\\n')) {\n const frame = line.trim();\n if (!frame.startsWith('at ')) continue;\n return fileOfFrame(frame.slice(3));\n }\n return undefined;\n}\n\n/** `foo (/a/b.ts:1:2)` / `/a/b.ts:1:2` / `file:///a/b.js:1:2` → an absolute path, else `undefined`. */\nfunction fileOfFrame(frame: string): string | undefined {\n const match = /\\((.+):\\d+:\\d+\\)$/.exec(frame) ?? /^(.+):\\d+:\\d+$/.exec(frame);\n const location = match?.[1];\n if (location === undefined) return undefined;\n if (location.startsWith('file://')) {\n try {\n return fileURLToPath(location);\n } catch {\n return undefined;\n }\n }\n // Anything that is not an absolute path is not a file we can resolve a package from: `node:internal/…`,\n // `<anonymous>`, `eval at …`, a bundler's virtual id.\n const absolute = location.startsWith('/') || /^[A-Za-z]:[\\\\/]/.test(location);\n return absolute ? location : undefined;\n}\n","import type { StartRunDeps } 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';\nimport { startRun } from './worker-sdk';\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 type { DurableWorkerRuntime } from '@dudousxd/durable-worker';\n\n/** The `@dudousxd/durable-worker` module shape. `typeof import()` is erased, so this costs nothing. */\ntype WorkerSdk = typeof import('@dudousxd/durable-worker');\n\n/**\n * The single gate to `@dudousxd/durable-worker`. It is an OPTIONAL peer because only the roles that\n * EXECUTE workflow bodies need it — a pure operator (`store` + `transport`, no `connection`) never\n * runs a worker and is entitled to install without it. A static import would resolve it for every\n * consumer at `import '@dudousxd/nestjs-durable'`, so such an operator would die at boot with\n * `ERR_MODULE_NOT_FOUND` on a package it was told was optional.\n *\n * Every value from the SDK therefore crosses this module and nothing here is reachable from an\n * import of the package entry. Node caches the resolved module, so each call after the first is a\n * cache hit; the call sites below are per-`start` or per-bootstrap, never per-turn. Mirrors how\n * `@dudousxd/durable-worker` itself reaches its own optional `bullmq`/`ioredis` peers.\n */\nasync function workerSdk(): Promise<WorkerSdk> {\n try {\n return await import('@dudousxd/durable-worker');\n } catch (cause) {\n throw new Error(\n '@dudousxd/durable-worker is not installed. It is an optional peer needed only by the roles that run workflow bodies (`connection` set on DurableModule.forRoot) — install it, or drop `connection`.',\n { cause },\n );\n }\n}\n\n/** Publish a `StartRunMessage` on the shared start-run queue. One import per `start`, then cached. */\nexport const startRun: WorkerSdk['startRun'] = async (connection, options) =>\n (await workerSdk()).startRun(connection, options);\n\n/** Start a BullMQ consumer for a runtime's registered names. Called once, on app bootstrap. */\nexport const runRedisWorker: WorkerSdk['runRedisWorker'] = async (options) =>\n (await workerSdk()).runRedisWorker(options);\n\n/** Construct the store-less runtime a worker registers its bodies on. Called once, per role gate. */\nexport async function createWorkerRuntime(\n options: ConstructorParameters<WorkerSdk['DurableWorkerRuntime']>[0],\n): Promise<DurableWorkerRuntime> {\n const { DurableWorkerRuntime: Runtime } = await workerSdk();\n return new Runtime(options);\n}\n","import type {\n DurableWorkerRuntime,\n RunRedisWorkerOptions,\n RunningWorker,\n} from '@dudousxd/durable-worker';\nimport {\n DURABLE_OPTIONS_CANONICAL,\n type DurableTopology,\n type EngineEvent,\n type GroupHealth,\n type RunDetail,\n type RunFacetQuery,\n type RunFacetRow,\n type RunGateway,\n type RunListItem,\n type RunQuery,\n type RunResult,\n type RunValueAxis,\n type RunValueFacetOptions,\n type RunValueFacetRow,\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';\nimport { createWorkerRuntime, runRedisWorker as defaultRunRedisWorker } from './worker-sdk';\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/**\n * The store-less {@link DurableWorkerRuntime} a pure thin worker registers its bodies on — `null` in\n * every other role. A Symbol rather than the class itself: the class lives in an optional peer, and\n * using it as a DI token would drag that peer into the package's static import graph.\n */\nexport const DURABLE_WORKER_RUNTIME = Symbol('nestjs-durable:worker-runtime');\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 @Inject(DURABLE_WORKER_RUNTIME) private readonly runtime: DurableWorkerRuntime | null,\n @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n ) {}\n\n onModuleInit(): void {\n const runtime = this.runtime;\n if (runtime === null || !isPureThinWorker(this.options)) return;\n // The `@Workflow` metadata is also what this worker ANNOUNCES about each body it serves (design\n // §7.9) — version, capability demand, and the declaring package `scanWorkflows` derived. It is\n // passed straight through: the registry states what the decorator states, and an option the\n // decorator left off stays un-stated rather than becoming a default.\n scanWorkflows(this.discovery, (meta, instance, origin) =>\n runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {\n version: meta.version,\n ...(meta.requires !== undefined ? { requires: meta.requires } : {}),\n ...(origin !== undefined ? { origin } : {}),\n }),\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 @Inject(DURABLE_WORKER_RUNTIME) private readonly runtime: DurableWorkerRuntime | null,\n @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n ) {}\n\n onModuleInit(): void {\n const runtime = this.runtime;\n if (runtime === null || !isPureThinWorker(this.options)) return;\n scanSteps(this.discovery, this.metadataScanner, (meta, handler) =>\n 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 @Inject(DURABLE_WORKER_RUNTIME) private readonly runtime: DurableWorkerRuntime | null,\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 const runtime = this.runtime;\n if (runtime === null || !isPureThinWorker(options) || options.connection === undefined) return;\n // ONE call: the runner subscribes one queue per name registered on `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,\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: DURABLE_WORKER_RUNTIME,\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 //\n // The role gate lives HERE, not just in the consumers: constructing the runtime resolves the\n // optional `@dudousxd/durable-worker` peer, so an operator that never runs a worker must not\n // reach this branch at all.\n useFactory: (options: DurableModuleOptions) =>\n isPureThinWorker(options)\n ? createWorkerRuntime({ workflowGroup: options.partition ?? '' })\n : null,\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 runFacets(_query: RunFacetQuery): Promise<RunFacetRow[]> {\n return tenantGatewayUnavailable<RunFacetRow[]>('runFacets');\n },\n runValueFacets(\n _axis: RunValueAxis,\n _query: RunFacetQuery,\n _opts?: RunValueFacetOptions,\n ): Promise<RunValueFacetRow[]> {\n return tenantGatewayUnavailable<RunValueFacetRow[]>('runValueFacets');\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';\nimport { originOfProvider } from './origin';\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\n * `register(meta, instance, origin)`.\n *\n * `origin` is the package that declared the workflow class ({@link originOfProvider}), derived from\n * the provider wrapper — this is the ONE path every `@Workflow` in the app reaches, whichever role\n * the app runs in, so a lib that has never heard of the feature is attributed anyway. It is\n * `undefined` when the derivation could not answer confidently; the engine-side registrar\n * (`WorkflowRegistrar`) reports those at boot, and their runs carry no origin rather than a guess.\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, origin: string | undefined) => 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, originOfProvider(wrapper));\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 per scope — 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 * // Scoped: chat turns age out after a day, whatever the defaults above say.\n * { statuses: ['completed', 'failed', 'cancelled'], maxAge: '1d', scope: { tags: ['chat'] } },\n * ],\n * }\n * ```\n *\n * Policies are applied one after another, so a run matched by several scopes is pruned by the\n * strictest of them.\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 * Opt-in self-heal window (ms) for a dispatched remote step whose job was LOST — the worker was\n * killed mid-step (an OOM kill leaves no result, and a non-TS consumer has no way to bridge the\n * job's terminal failure back into one), or the broker dropped the job. The `pending` checkpoint\n * then has nothing left to settle it, so the run waits forever. When set, the pending checkpoint\n * carries a LEASE: a result — or a step-scoped heartbeat, which renews it, so a worker still\n * holding the step is never double-dispatched — must land within the window, else the engine\n * re-dispatches the same step (bumping `attempts`, bounded by {@link remoteRedispatchMax}).\n * Covers both `ctx.step` and a polyglot workflow's `call` fan-out (`ctx.gather_calls`).\n *\n * Off by default: a re-dispatch double-runs a step whose original job was merely SLOW, so the\n * window MUST exceed your longest remote step and the step MUST be idempotent. Operator only.\n */\n remoteRedispatchMs?: number;\n /**\n * Max times {@link remoteRedispatchMs} re-dispatches one lost step before failing it\n * (`remote_step_lost`, surfaced on the run) instead of re-dispatching forever. Defaults to 10.\n * Ignored when `remoteRedispatchMs` is unset. Operator only.\n */\n remoteRedispatchMax?: 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 * Fire the PERSISTED schedules managed at runtime through `engine.schedules` (create/upsert/pause/\n * resume/delete/list) on every timer-poll tick of a driving instance — one indexed \"due by now\"\n * query per tick. Off by default, so a deployment that never uses them doesn't need the\n * `durable_schedules` table. Safe with any number of driving instances: each window's run id is\n * deterministic and advancing a schedule is a compare-and-set. Operator only.\n */\n persistedSchedules?: boolean;\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` (every\n * first-party adapter does); a custom store without it no-ops with a warning. See\n * {@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 remoteRedispatchMs: options.remoteRedispatchMs,\n remoteRedispatchMax: options.remoteRedispatchMax,\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 type { DurableWorkerRuntime, RunningWorker } 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';\nimport { createWorkerRuntime, runRedisWorker as defaultRunRedisWorker } from './worker-sdk';\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 | null,\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 const runtime = this.runtime;\n if (runtime === null || !isCoLocatedWorker(this.options)) return;\n // Register the same TS bodies the engine serves group-served, so the consumer can replay them.\n // The metadata rides along as this worker's ANNOUNCEMENT of what it can execute (design §7.9):\n // co-located or not, the process that CONSUMES the queue is the one entitled to announce the\n // workflow, and here that is this runtime — not the engine sharing its process.\n scanWorkflows(this.discovery, (meta, instance, origin) =>\n runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {\n version: meta.version,\n ...(meta.requires !== undefined ? { requires: meta.requires } : {}),\n ...(origin !== undefined ? { origin } : {}),\n }),\n );\n scanSteps(this.discovery, this.metadataScanner, (meta, handler) =>\n runtime.registerStep(meta.name, handler),\n );\n }\n\n async onApplicationBootstrap(): Promise<void> {\n const options = this.options;\n const runtime = this.runtime;\n if (runtime === null || !isCoLocatedWorker(options) || options.connection === undefined) return;\n const handle = await this.runRedisWorker({\n 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 //\n // The role gate lives HERE, not just in the consumer: constructing the runtime resolves the\n // optional `@dudousxd/durable-worker` peer, so an operator that never runs a worker must not\n // reach this branch at all.\n useFactory: (options: DurableModuleOptions) =>\n isCoLocatedWorker(options)\n ? createWorkerRuntime({ workflowGroup: options.partition ?? '' })\n : null,\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 RunFacetQuery,\n RunFacetRow,\n RunGateway,\n RunListItem,\n RunQuery,\n RunReply,\n RunRequestKind,\n RunResult,\n RunValueAxis,\n RunValueFacetOptions,\n RunValueFacetRow,\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 /** Counted on the operator, over the same tenant-scoped predicates its `listRuns` reply obeys. */\n runFacets(query: RunFacetQuery): Promise<RunFacetRow[]> {\n return this.request<RunFacetRow[]>({ kind: 'runFacets', query });\n }\n\n /** Enumerated on the operator, over the same tenant-scoped predicates its `listRuns` reply obeys —\n * so a tenant's pickers offer its own tenants/tags/attributes and never another's. */\n runValueFacets(\n axis: RunValueAxis,\n query: RunFacetQuery,\n opts?: RunValueFacetOptions,\n ): Promise<RunValueFacetRow[]> {\n return this.request<RunValueFacetRow[]>({ kind: 'runValueFacets', axis, query, opts });\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 // Disjointness is per SCOPE: two policies over the same runs must not both claim a status (\"keep 100\"\n // vs \"keep 10\" of the same set is ambiguous), but `{ namespace: 'a' }` and `{ namespace: 'b' }` — or a\n // tenant-scoped policy next to an unscoped default — each rule their own selection.\n const seen = new Set<string>();\n for (const policy of retention.policies) {\n const scopeKey = policy.scope ? stableKey(policy.scope) : '';\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 const key = `${scopeKey}|${status}`;\n if (seen.has(key)) {\n throw new Error(\n `durable retention: status \"${status}\" appears in more than one policy with the same scope; status sets must be disjoint per scope`,\n );\n }\n seen.add(key);\n }\n }\n}\n\n/** A key-order-independent JSON key for a scope, so `{a,b}` and `{b,a}` compare equal. */\nfunction stableKey(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableKey).join(',')}]`;\n if (value && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => a.localeCompare(b));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableKey(v)}`).join(',')}}`;\n }\n return JSON.stringify(value);\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 /**\n * Prune one round.\n *\n * Never rejects, for the same reason {@link TimerPoller.poll} does not: it is awaited inside\n * `onApplicationBootstrap` (so a rejection propagates into the host's `bootstrap()` and kills the\n * process) and fired as `void` on the interval (so a rejection is an unhandled one, also fatal).\n * Pruning history is the least urgent thing this package does — a `pruneTerminalRuns` that fails\n * on a locked table must cost this sweep and nothing more.\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 } catch (error) {\n console.error('[nestjs-durable] retention sweep failed; retrying next interval:', error);\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 === 'runFacets') {\n // Same forced scope as `listRuns` above, for the same reason: the counts label a tenant's own\n // page, so they must be taken over exactly that tenant's runs and no one else's.\n const data = await this.gateway.runFacets({ ...body.query, namespace: msg.tenant });\n return { requestId: msg.requestId, result: { ok: true, data } };\n }\n\n if (body.kind === 'runValueFacets') {\n // Same forced scope as `listRuns`/`runFacets`: the values fill a picker for the tenant's own\n // list, so enumerating them over anyone else's runs would both leak other tenants' tag and\n // attribute vocabulary and offer choices this tenant's list can never match.\n const data = await this.gateway.runValueFacets(\n body.axis,\n { ...body.query, namespace: msg.tenant },\n body.opts,\n );\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: 'runFacets' }\n | { kind: 'runValueFacets' }\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 RunFacetQuery,\n type RunFacetRow,\n type RunGateway,\n type RunListItem,\n type RunQuery,\n type RunResult,\n type RunValueAxis,\n type RunValueFacetOptions,\n type RunValueFacetRow,\n type RunWaiting,\n STATE_STORE_CANONICAL,\n type StateStore,\n WorkflowEngine,\n indexWaitersByRun,\n mergeRunFacetRows,\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 * The `(status, origin)` counts behind a console's chips, straight off the store's aggregate. The\n * console needs this precisely BECAUSE its list is paginated: the page bounds what is rendered,\n * these counts stay whole-set exact. A store with no aggregate falls back to counting a full\n * listing — correct, but the unbounded read this exists to avoid, so it is the last resort.\n */\n async runFacets(query: RunFacetQuery): Promise<RunFacetRow[]> {\n if (this.store.runFacets) return this.store.runFacets(query);\n const runs = await this.store.listRuns(query);\n return mergeRunFacetRows(runs.map((r) => ({ status: r.status, origin: r.origin, count: 1 })));\n }\n\n /**\n * The distinct values behind a console's pickers, straight off the store's enumeration. A store\n * without one answers `[]` rather than falling back to a listing: unlike the facet counts above —\n * where a full listing is at least the RIGHT answer expensively — a picker built from an unbounded\n * scan would be the same unbounded read on every keystroke, for a control whose free-text entry\n * still works without it.\n */\n async runValueFacets(\n axis: RunValueAxis,\n query: RunFacetQuery,\n opts?: RunValueFacetOptions,\n ): Promise<RunValueFacetRow[]> {\n return this.store.runValueFacets?.(axis, query, opts) ?? [];\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` (plus the persisted `engine.schedules` when `persistedSchedules` is on) —\n * 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 // The `.catch` is load-bearing, not decorative: this is fire-and-forget, so an unhandled\n // rejection here terminates the process under Node's default policy — and the run it could not\n // drive is still waiting for the next poll tick either way.\n this.unsubscribeEnqueued = this.engine.onEnqueued((runId) => {\n void this.engine.runOne(runId).catch((error) => {\n console.error(`[nestjs-durable] could not run enqueued run ${runId}:`, error);\n });\n });\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 /**\n * One sweep of the engine's recovery work.\n *\n * **This method never rejects, and that is a hard requirement rather than defensiveness.** It has\n * exactly two callers and a rejection out of either one kills the process: on boot it is awaited\n * inside `onApplicationBootstrap`, so the rejection propagates through `NestApplication.init()`\n * into the host's `bootstrap()`; on the interval it is a fire-and-forget `void`, so it surfaces as\n * an unhandled rejection. Both are fatal, and both re-occur on the next boot with the same state,\n * so a single unrecoverable run crash-loops every worker in the deployment — taking down every\n * other workflow with it. A poll that goes wrong must cost this tick, not the pod.\n *\n * Each sweep is isolated separately because they are independent: a store that fails\n * `recoverIncomplete` says nothing about whether durable timers should still fire, and skipping\n * the rest of the tick would silently widen one subsystem's outage into all of them.\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.sweep('runPending', () => this.engine.runPending());\n await this.sweep('recoverIncomplete', () => this.engine.recoverIncomplete());\n await this.sweep('resumeDueTimers', () => this.engine.resumeDueTimers());\n await this.sweep('sweepTimeouts', () => this.engine.sweepTimeouts());\n const schedules = this.options.schedules;\n if (schedules && schedules.length > 0) {\n await this.sweep('schedules', () => runSchedules(this.engine, schedules, Date.now()));\n }\n if (this.options.persistedSchedules) {\n await this.sweep('persistedSchedules', () => this.engine.schedules.tick());\n }\n } finally {\n this.polling = false;\n }\n }\n\n /** Run one sweep, absorbing and reporting its failure — see {@link poll}. */\n private async sweep(name: string, run: () => Promise<unknown>): Promise<void> {\n try {\n await run();\n } catch (error) {\n console.error(`[nestjs-durable] timer poll: ${name} failed; continuing:`, error);\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 // Workflows whose declaring package could not be derived — reported once, after the scan.\n const unattributed: string[] = [];\n\n // Workflow registration pass — shared scan, engine-specific registration callback.\n scanWorkflows(this.discovery, (meta, workflow, origin) => {\n if (origin === undefined) unattributed.push(meta.name);\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 // Stamped onto every run this workflow starts (see `WorkflowRun.origin`). Derived from the\n // class's declaring file, so a lib gets attributed without opting in; `undefined` when it\n // could not be derived, which the engine records as unknown rather than defaulting.\n origin,\n tags: meta.tags,\n singleton: meta.singleton,\n concurrency: meta.concurrency,\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 // Declared on the very same class, so it carries the same origin.\n this.engine.register(dlqName, meta.version, inline, { origin });\n deadLetterByWorkflow.set(meta.name, dlqName);\n } else if (meta.deadLetterWorkflow) {\n deadLetterByWorkflow.set(meta.name, workflowName(meta.deadLetterWorkflow));\n }\n });\n\n this.reportUnattributed(unattributed);\n this.installDeadLetterRouting(deadLetterByWorkflow);\n }\n\n /**\n * Says, once at boot, which workflows could not be attributed to a package — their runs are stamped\n * with no `origin` at all, so an operator filtering the dashboard by origin will not see them. That\n * is the deliberate trade (an absent origin over an invented one), but it has to be visible: a\n * silently-dropped run is indistinguishable from a run that never happened.\n */\n private reportUnattributed(names: string[]): void {\n if (names.length === 0) return;\n console.warn(\n `[nestjs-durable] could not derive an origin (declaring package) for ${names.length} workflow(s): ${names.join(', ')}. Their runs carry no origin and will not match an origin filter.`,\n );\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 optional peers (only\n * needed if you use `@Workflow({ inputSchema })`), so they are reached through a dynamic `import()`\n * inside the validator: a static import would resolve them for every consumer of the package, and a\n * `require()` cannot work at all in the ESM build. That defers the \"peer missing\" error from\n * registration to the first `start` of a workflow that declares an `inputSchema` — the only moment\n * the peers are actually needed.\n */\nexport function classValidatorInput(cls: ClassCtor): (input: unknown) => Promise<void> {\n return async (input: unknown) => {\n let cv: any;\n let ct: any;\n try {\n cv = await import('class-validator');\n ct = await import('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 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","declaringFileByClass","WeakMap","packageNameByDir","Map","declaringFile","boundary","capture","Error","captureStackTrace","undefined","holder","topFrameFile","stack","rememberDeclaringFile","target","file","set","originOfClass","ctor","Object","getPrototypeOf","get","packageNameFor","originOfProvider","wrapper","fromMetatype","metatype","instance","start","dirname","visited","dir","has","cached","seen","push","name","readPackageName","join","parent","path","raw","readFileSync","parsed","JSON","parse","isRecord","length","value","line","split","frame","trim","startsWith","fileOfFrame","slice","match","exec","location","fileURLToPath","absolute","test","WORKFLOW_METADATA","Symbol","Workflow","options","declaredIn","declaringFile","target","rememberDeclaringFile","meta","name","version","deadLetterWorkflow","tags","singleton","concurrency","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","workerSdk","cause","Error","startRun","connection","options","runRedisWorker","createWorkerRuntime","DurableWorkerRuntime","Runtime","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_nestjs_durable_core","import_common","scanWorkflows","discovery","register","wrapper","getProviders","instance","meta","getWorkflowMeta","workflow","run","Error","name","originOfProvider","scanSteps","scanner","prototype","Object","getPrototypeOf","methodName","getAllMethodNames","method","getDurableStepMeta","boundMethod","handler","input","log","validInput","parse","output","call","RUN_REDIS_WORKER","Symbol","DURABLE_WORKER_RUNTIME","DURABLE_WORKER_RUNNERS","isPureThinWorker","options","store","undefined","connection","ThinWorkflowRegistrar","discovery","runtime","onModuleInit","scanWorkflows","meta","instance","origin","registerWorkflow","name","ctx","input","run","version","requires","ThinStepRegistrar","metadataScanner","scanSteps","handler","registerStep","ThinWorkerBootstrap","runners","runRedisWorker","runnersSink","onApplicationBootstrap","handle","partition","prefix","instanceId","concurrency","push","onApplicationShutdown","Promise","allSettled","map","h","close","thinWorkerProviders","provide","useFactory","createWorkerRuntime","workflowGroup","inject","DURABLE_OPTIONS_CANONICAL","useValue","defaultRunRedisWorker","tenantGatewayUnavailable","method","reject","Error","unavailableRunGateway","topology","role","getRunDetail","_runId","listRuns","_query","runFacets","runValueFacets","_axis","_opts","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_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","origin","registerWorkflow","name","ctx","input","run","version","requires","scanSteps","handler","registerStep","onApplicationBootstrap","handle","prefix","instanceId","concurrency","push","onApplicationShutdown","Promise","allSettled","map","close","inAppWorkerProviders","provide","useFactory","inject","TRANSPORT_CANONICAL","DURABLE_OPTIONS_CANONICAL","createWorkerRuntime","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","runFacets","runValueFacets","axis","opts","workerHealth","waitingFor","runIds","cancel","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","scopeKey","scope","stableKey","statuses","length","Error","maxAge","maxCount","parseDuration","status","TERMINAL_RUN_STATUSES","includes","join","key","has","add","value","Array","isArray","map","entries","Object","filter","v","undefined","sort","a","b","localeCompare","k","JSON","stringify","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","error","RunRequestResponder","transport","gateway","start","onRunRequest","msg","reply","handle","publishRunReply","body","kind","data","listRuns","query","namespace","tenant","requestId","result","ok","runFacets","runValueFacets","axis","opts","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","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","runFacets","mergeRunFacetRows","r","status","origin","count","runValueFacets","axis","opts","waitingFor","runIds","length","idSet","Set","suspended","waiters","statuses","result","has","id","workerHealth","cancel","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","catch","error","console","poll","intervalMs","timerPollMs","setInterval","unref","onModuleDestroy","clearInterval","sweep","runPending","recoverIncomplete","resumeDueTimers","sweepTimeouts","schedules","length","runSchedules","Date","now","persistedSchedules","tick","name","run","import_nestjs_durable_core","CONTEXT_ACCESSOR","Symbol","for","RUN_GATEWAY","RunGateway","import_nestjs_durable_core","import_common","import_core","classValidatorInput","cls","input","cv","ct","Error","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","unattributed","scanWorkflows","meta","workflow","origin","undefined","push","validateInput","inputSchema","classValidatorInput","eventBatch","debounce","mode","windowMs","parseDuration","batch","maxSize","within","workflowCtor","register","version","ctx","input","run","tags","singleton","concurrency","executionTimeout","requires","searchAttributesSchema","searchAttributes","onEvent","getOnEvents","group","tenantGroup","sanitizeQueueToken","partition","executor","RemoteWorkflowExecutor","bindWorkflowClass","start","runId","opts","waitForRun","inline","findDeadLetterHandler","deadLetterWorkflow","dlqName","set","workflowName","reportUnattributed","installDeadLetterRouting","names","length","console","warn","join","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","remoteRedispatchMs","remoteRedispatchMax","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"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/attributes-of.ts","../src/decorators.ts","../src/origin.ts","../src/durable-start-client.ts","../src/worker-sdk.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 DynamicWorkflowCtx,\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 type ConcurrencyConfig,\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';\nimport { declaringFile, rememberDeclaringFile } from './origin';\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 /** Start-time concurrency quota (rejecting). See `WorkflowOptions`. */\n concurrency?: ConcurrencyConfig | 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 * A start-time concurrency QUOTA: at most `limit` runs sharing a key in flight, and a start over it\n * is REJECTED (`ConcurrencyLimitError`, nothing created) rather than queued. e.g.\n * `concurrency: { key: (input) => `tenant:${input.tenantId}`, limit: 8 }` caps what one tenant can\n * have executing at once. The key is shared across workflows; `limit` may be a (async) function of\n * the key for per-plan limits; `countStatuses` narrows what occupies a slot (default: every\n * non-terminal status). Use `singleton` instead when excess runs should wait their turn.\n */\n concurrency?: ConcurrencyConfig;\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 // Captured HERE, in the factory, because this is the one moment we are provably executing inside\n // the module that declares the workflow — by the time a registrar scans providers, the only thing\n // left of that module is a class object with no file on it. The path is resolved to the owning\n // package lazily, at registration (see `origin.ts`); a runtime that yields no usable frame simply\n // leaves the workflow unattributed, which the registrar reports at boot.\n const declaredIn = declaringFile(Workflow);\n return (target) => {\n rememberDeclaringFile(target, declaredIn);\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 concurrency: options.concurrency,\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 { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Derives a workflow's **origin** — the npm package that declared it (`@dudousxd/nestjs-agent`, the\n * host app's own `package.json` `name`, …) — so every run can say which lib produced it (see\n * {@link import('@dudousxd/nestjs-durable-core').WorkflowRun.origin}).\n *\n * WHY IT IS DERIVED AND NOT DECLARED: anything a lib has to opt into (a `@Workflow({ tags })`\n * convention, a registry of known packages) is already wrong in the field the day a lib forgets, and\n * a facet that lists two of the five libs actually running is worse than no facet at all. So the only\n * acceptable input is one the lib cannot fail to provide: the file it declared its `@Workflow` class\n * in. That file is captured by {@link declaringFile} at decoration time — the one moment we are\n * guaranteed to be executing inside the declaring module — and mapped here to the nearest enclosing\n * `package.json` `name`.\n *\n * WHY NOT THE NEST MODULE: the `InstanceWrapper` a `DiscoveryService` scan hands back does name the\n * module a provider came from (`wrapper.host.metatype`), but a module class is just a class — it\n * carries no file location, and the app's own module is a perfectly normal place to register a lib's\n * workflow, which would then be attributed to the app. The declaring file of the `@Workflow` class is\n * both available and strictly more precise, so the wrapper is used only to reach that class.\n *\n * EVERY step here can fail, and each failure yields `undefined` (= unknown) rather than a guess:\n * - a runtime without V8's `Error.captureStackTrace`, or an app that set `Error.stackTraceLimit = 0`\n * - a declaring frame that is not a real file (`node:` internals, `eval`, `<anonymous>`) — notably a\n * single-file bundle can also collapse several packages into one file, and where the file IS real\n * but shared, the origin will be the bundle's package: that is the one shape this cannot detect\n * - no `package.json` carrying a `name` anywhere above the file\n * `WorkflowRegistrar` reports the workflows that came back unattributed once, at boot.\n */\n\n/** The file each decorated class was declared in, keyed by the class itself. */\nconst declaringFileByClass = new WeakMap<object, string>();\n\n/** Memoized directory → owning package name (or `undefined` when the walk found none). */\nconst packageNameByDir = new Map<string, string | undefined>();\n\ntype AnyFunction = (...args: never[]) => unknown;\n\n/**\n * The absolute path of the file that is calling `boundary` right now — i.e. the file whose top level\n * is applying a decorator, when called from inside that decorator's factory. `boundary` is clipped\n * off the captured stack (`Error.captureStackTrace`'s second argument), so the first remaining frame\n * is the caller's, with no frame counting to go stale when this file is refactored.\n *\n * Returns `undefined` on any runtime that does not give us a usable frame.\n */\nexport function declaringFile(boundary: AnyFunction): string | undefined {\n const capture = Error.captureStackTrace;\n if (typeof capture !== 'function') return undefined;\n const holder: { stack?: string } = {};\n capture(holder, boundary);\n return topFrameFile(holder.stack);\n}\n\n/** Records the file `target` was declared in, so {@link originOfClass} can resolve it later. */\nexport function rememberDeclaringFile(target: object, file: string | undefined): void {\n if (file) declaringFileByClass.set(target, file);\n}\n\n/**\n * The package that declared `target`, or `undefined` if it cannot be resolved with confidence.\n *\n * Walks the prototype chain, matching `Reflect.getMetadata`'s inheritance: a subclass that inherits\n * its `@Workflow` metadata from a decorated base also inherits that base's origin, because the base\n * is where the decorated body was declared.\n */\nexport function originOfClass(target: unknown): string | undefined {\n for (\n let ctor: unknown = target;\n typeof ctor === 'function' || (typeof ctor === 'object' && ctor !== null);\n ctor = Object.getPrototypeOf(ctor)\n ) {\n const file = declaringFileByClass.get(ctor);\n if (file) return packageNameFor(file);\n }\n return undefined;\n}\n\n/**\n * The package that declared the workflow behind a discovered provider. Structurally typed against\n * Nest's `InstanceWrapper` (the shape `DiscoveryService.getProviders()` returns) so this package does\n * not deep-import an internal Nest path: `metatype` is the registered class, and `instance.constructor`\n * covers a provider whose metatype is absent (a `useValue`/factory-built instance of a decorated class).\n */\nexport function originOfProvider(wrapper: {\n metatype?: unknown;\n instance?: unknown;\n}): string | undefined {\n const fromMetatype = originOfClass(wrapper.metatype);\n if (fromMetatype) return fromMetatype;\n const instance = wrapper.instance;\n if (typeof instance !== 'object' || instance === null) return undefined;\n return originOfClass(instance.constructor);\n}\n\n/**\n * The `name` of the nearest `package.json` at or above `file`'s directory. For a lib this is the\n * installed package (`…/node_modules/@scope/lib/dist/index.cjs` → `@scope/lib`, pnpm's nested layout\n * included); for a workflow written in the app it is the app's own name. Memoized per directory —\n * the walk hits the filesystem once per distinct directory, not once per workflow.\n */\nfunction packageNameFor(file: string): string | undefined {\n const start = dirname(file);\n const visited: string[] = [];\n let dir = start;\n for (;;) {\n if (packageNameByDir.has(dir)) {\n const cached = packageNameByDir.get(dir);\n for (const seen of visited) packageNameByDir.set(seen, cached);\n return cached;\n }\n visited.push(dir);\n const name = readPackageName(join(dir, 'package.json'));\n if (name !== undefined) {\n for (const seen of visited) packageNameByDir.set(seen, name);\n return name;\n }\n const parent = dirname(dir);\n // `dirname('/')` is `'/'` — the filesystem root is the end of the walk.\n if (parent === dir) {\n for (const seen of visited) packageNameByDir.set(seen, undefined);\n return undefined;\n }\n dir = parent;\n }\n}\n\n/** The `name` field of a `package.json`, or `undefined` if it is missing, unreadable or unnamed. */\nfunction readPackageName(path: string): string | undefined {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // A malformed package.json is not ours to fail on — keep walking up.\n return undefined;\n }\n if (!isRecord(parsed)) return undefined;\n const name = parsed.name;\n return typeof name === 'string' && name.length > 0 ? name : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * The file named by the FIRST frame of a captured stack. Deliberately only the first: with the\n * capture boundary clipped, frame 0 IS the declaring site, and falling through to a deeper frame\n * when it is unparseable would attribute the workflow to whatever happened to be further up the\n * stack (the test runner, Nest's loader) — a confident-looking wrong answer.\n */\nfunction topFrameFile(stack: string | undefined): string | undefined {\n if (typeof stack !== 'string') return undefined;\n for (const line of stack.split('\\n')) {\n const frame = line.trim();\n if (!frame.startsWith('at ')) continue;\n return fileOfFrame(frame.slice(3));\n }\n return undefined;\n}\n\n/** `foo (/a/b.ts:1:2)` / `/a/b.ts:1:2` / `file:///a/b.js:1:2` → an absolute path, else `undefined`. */\nfunction fileOfFrame(frame: string): string | undefined {\n const match = /\\((.+):\\d+:\\d+\\)$/.exec(frame) ?? /^(.+):\\d+:\\d+$/.exec(frame);\n const location = match?.[1];\n if (location === undefined) return undefined;\n if (location.startsWith('file://')) {\n try {\n return fileURLToPath(location);\n } catch {\n return undefined;\n }\n }\n // Anything that is not an absolute path is not a file we can resolve a package from: `node:internal/…`,\n // `<anonymous>`, `eval at …`, a bundler's virtual id.\n const absolute = location.startsWith('/') || /^[A-Za-z]:[\\\\/]/.test(location);\n return absolute ? location : undefined;\n}\n","import type { StartRunDeps } 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';\nimport { startRun } from './worker-sdk';\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 type { DurableWorkerRuntime } from '@dudousxd/durable-worker';\n\n/** The `@dudousxd/durable-worker` module shape. `typeof import()` is erased, so this costs nothing. */\ntype WorkerSdk = typeof import('@dudousxd/durable-worker');\n\n/**\n * The single gate to `@dudousxd/durable-worker`. It is an OPTIONAL peer because only the roles that\n * EXECUTE workflow bodies need it — a pure operator (`store` + `transport`, no `connection`) never\n * runs a worker and is entitled to install without it. A static import would resolve it for every\n * consumer at `import '@dudousxd/nestjs-durable'`, so such an operator would die at boot with\n * `ERR_MODULE_NOT_FOUND` on a package it was told was optional.\n *\n * Every value from the SDK therefore crosses this module and nothing here is reachable from an\n * import of the package entry. Node caches the resolved module, so each call after the first is a\n * cache hit; the call sites below are per-`start` or per-bootstrap, never per-turn. Mirrors how\n * `@dudousxd/durable-worker` itself reaches its own optional `bullmq`/`ioredis` peers.\n */\nasync function workerSdk(): Promise<WorkerSdk> {\n try {\n return await import('@dudousxd/durable-worker');\n } catch (cause) {\n throw new Error(\n '@dudousxd/durable-worker is not installed. It is an optional peer needed only by the roles that run workflow bodies (`connection` set on DurableModule.forRoot) — install it, or drop `connection`.',\n { cause },\n );\n }\n}\n\n/** Publish a `StartRunMessage` on the shared start-run queue. One import per `start`, then cached. */\nexport const startRun: WorkerSdk['startRun'] = async (connection, options) =>\n (await workerSdk()).startRun(connection, options);\n\n/** Start a BullMQ consumer for a runtime's registered names. Called once, on app bootstrap. */\nexport const runRedisWorker: WorkerSdk['runRedisWorker'] = async (options) =>\n (await workerSdk()).runRedisWorker(options);\n\n/** Construct the store-less runtime a worker registers its bodies on. Called once, per role gate. */\nexport async function createWorkerRuntime(\n options: ConstructorParameters<WorkerSdk['DurableWorkerRuntime']>[0],\n): Promise<DurableWorkerRuntime> {\n const { DurableWorkerRuntime: Runtime } = await workerSdk();\n return new Runtime(options);\n}\n","import type {\n DurableWorkerRuntime,\n RunRedisWorkerOptions,\n RunningWorker,\n} from '@dudousxd/durable-worker';\nimport {\n DURABLE_OPTIONS_CANONICAL,\n type DurableTopology,\n type EngineEvent,\n type GroupHealth,\n type RunDetail,\n type RunFacetQuery,\n type RunFacetRow,\n type RunGateway,\n type RunListItem,\n type RunQuery,\n type RunResult,\n type RunValueAxis,\n type RunValueFacetOptions,\n type RunValueFacetRow,\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';\nimport { createWorkerRuntime, runRedisWorker as defaultRunRedisWorker } from './worker-sdk';\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/**\n * The store-less {@link DurableWorkerRuntime} a pure thin worker registers its bodies on — `null` in\n * every other role. A Symbol rather than the class itself: the class lives in an optional peer, and\n * using it as a DI token would drag that peer into the package's static import graph.\n */\nexport const DURABLE_WORKER_RUNTIME = Symbol('nestjs-durable:worker-runtime');\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 @Inject(DURABLE_WORKER_RUNTIME) private readonly runtime: DurableWorkerRuntime | null,\n @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n ) {}\n\n onModuleInit(): void {\n const runtime = this.runtime;\n if (runtime === null || !isPureThinWorker(this.options)) return;\n // The `@Workflow` metadata is also what this worker ANNOUNCES about each body it serves (design\n // §7.9) — version, capability demand, and the declaring package `scanWorkflows` derived. It is\n // passed straight through: the registry states what the decorator states, and an option the\n // decorator left off stays un-stated rather than becoming a default.\n scanWorkflows(this.discovery, (meta, instance, origin) =>\n runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {\n version: meta.version,\n ...(meta.requires !== undefined ? { requires: meta.requires } : {}),\n ...(origin !== undefined ? { origin } : {}),\n }),\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 @Inject(DURABLE_WORKER_RUNTIME) private readonly runtime: DurableWorkerRuntime | null,\n @Inject(DURABLE_OPTIONS_CANONICAL) private readonly options: DurableModuleOptions,\n ) {}\n\n onModuleInit(): void {\n const runtime = this.runtime;\n if (runtime === null || !isPureThinWorker(this.options)) return;\n scanSteps(this.discovery, this.metadataScanner, (meta, handler) =>\n 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 @Inject(DURABLE_WORKER_RUNTIME) private readonly runtime: DurableWorkerRuntime | null,\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 const runtime = this.runtime;\n if (runtime === null || !isPureThinWorker(options) || options.connection === undefined) return;\n // ONE call: the runner subscribes one queue per name registered on `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,\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: DURABLE_WORKER_RUNTIME,\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 //\n // The role gate lives HERE, not just in the consumers: constructing the runtime resolves the\n // optional `@dudousxd/durable-worker` peer, so an operator that never runs a worker must not\n // reach this branch at all.\n useFactory: (options: DurableModuleOptions) =>\n isPureThinWorker(options)\n ? createWorkerRuntime({ workflowGroup: options.partition ?? '' })\n : null,\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 runFacets(_query: RunFacetQuery): Promise<RunFacetRow[]> {\n return tenantGatewayUnavailable<RunFacetRow[]>('runFacets');\n },\n runValueFacets(\n _axis: RunValueAxis,\n _query: RunFacetQuery,\n _opts?: RunValueFacetOptions,\n ): Promise<RunValueFacetRow[]> {\n return tenantGatewayUnavailable<RunValueFacetRow[]>('runValueFacets');\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';\nimport { originOfProvider } from './origin';\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\n * `register(meta, instance, origin)`.\n *\n * `origin` is the package that declared the workflow class ({@link originOfProvider}), derived from\n * the provider wrapper — this is the ONE path every `@Workflow` in the app reaches, whichever role\n * the app runs in, so a lib that has never heard of the feature is attributed anyway. It is\n * `undefined` when the derivation could not answer confidently; the engine-side registrar\n * (`WorkflowRegistrar`) reports those at boot, and their runs carry no origin rather than a guess.\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, origin: string | undefined) => 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, originOfProvider(wrapper));\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 per scope — 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 * // Scoped: chat turns age out after a day, whatever the defaults above say.\n * { statuses: ['completed', 'failed', 'cancelled'], maxAge: '1d', scope: { tags: ['chat'] } },\n * ],\n * }\n * ```\n *\n * Policies are applied one after another, so a run matched by several scopes is pruned by the\n * strictest of them.\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 * Opt-in self-heal window (ms) for a dispatched remote step whose job was LOST — the worker was\n * killed mid-step (an OOM kill leaves no result, and a non-TS consumer has no way to bridge the\n * job's terminal failure back into one), or the broker dropped the job. The `pending` checkpoint\n * then has nothing left to settle it, so the run waits forever. When set, the pending checkpoint\n * carries a LEASE: a result — or a step-scoped heartbeat, which renews it, so a worker still\n * holding the step is never double-dispatched — must land within the window, else the engine\n * re-dispatches the same step (bumping `attempts`, bounded by {@link remoteRedispatchMax}).\n * Covers both `ctx.step` and a polyglot workflow's `call` fan-out (`ctx.gather_calls`).\n *\n * Off by default: a re-dispatch double-runs a step whose original job was merely SLOW, so the\n * window MUST exceed your longest remote step and the step MUST be idempotent. Operator only.\n */\n remoteRedispatchMs?: number;\n /**\n * Max times {@link remoteRedispatchMs} re-dispatches one lost step before failing it\n * (`remote_step_lost`, surfaced on the run) instead of re-dispatching forever. Defaults to 10.\n * Ignored when `remoteRedispatchMs` is unset. Operator only.\n */\n remoteRedispatchMax?: 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 * Fire the PERSISTED schedules managed at runtime through `engine.schedules` (create/upsert/pause/\n * resume/delete/list) on every timer-poll tick of a driving instance — one indexed \"due by now\"\n * query per tick. Off by default, so a deployment that never uses them doesn't need the\n * `durable_schedules` table. Safe with any number of driving instances: each window's run id is\n * deterministic and advancing a schedule is a compare-and-set. Operator only.\n */\n persistedSchedules?: boolean;\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` (every\n * first-party adapter does); a custom store without it no-ops with a warning. See\n * {@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 // Tenant event mirroring is best-effort; a namespace lookup outage must not kill the worker.\n void republisher.handle(event).catch(() => undefined);\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 remoteRedispatchMs: options.remoteRedispatchMs,\n remoteRedispatchMax: options.remoteRedispatchMax,\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 type { DurableWorkerRuntime, RunningWorker } 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';\nimport { createWorkerRuntime, runRedisWorker as defaultRunRedisWorker } from './worker-sdk';\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 | null,\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 const runtime = this.runtime;\n if (runtime === null || !isCoLocatedWorker(this.options)) return;\n // Register the same TS bodies the engine serves group-served, so the consumer can replay them.\n // The metadata rides along as this worker's ANNOUNCEMENT of what it can execute (design §7.9):\n // co-located or not, the process that CONSUMES the queue is the one entitled to announce the\n // workflow, and here that is this runtime — not the engine sharing its process.\n scanWorkflows(this.discovery, (meta, instance, origin) =>\n runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {\n version: meta.version,\n ...(meta.requires !== undefined ? { requires: meta.requires } : {}),\n ...(origin !== undefined ? { origin } : {}),\n }),\n );\n scanSteps(this.discovery, this.metadataScanner, (meta, handler) =>\n runtime.registerStep(meta.name, handler),\n );\n }\n\n async onApplicationBootstrap(): Promise<void> {\n const options = this.options;\n const runtime = this.runtime;\n if (runtime === null || !isCoLocatedWorker(options) || options.connection === undefined) return;\n const handle = await this.runRedisWorker({\n 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 //\n // The role gate lives HERE, not just in the consumer: constructing the runtime resolves the\n // optional `@dudousxd/durable-worker` peer, so an operator that never runs a worker must not\n // reach this branch at all.\n useFactory: (options: DurableModuleOptions) =>\n isCoLocatedWorker(options)\n ? createWorkerRuntime({ workflowGroup: options.partition ?? '' })\n : null,\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 RunFacetQuery,\n RunFacetRow,\n RunGateway,\n RunListItem,\n RunQuery,\n RunReply,\n RunRequestKind,\n RunResult,\n RunValueAxis,\n RunValueFacetOptions,\n RunValueFacetRow,\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 /** Counted on the operator, over the same tenant-scoped predicates its `listRuns` reply obeys. */\n runFacets(query: RunFacetQuery): Promise<RunFacetRow[]> {\n return this.request<RunFacetRow[]>({ kind: 'runFacets', query });\n }\n\n /** Enumerated on the operator, over the same tenant-scoped predicates its `listRuns` reply obeys —\n * so a tenant's pickers offer its own tenants/tags/attributes and never another's. */\n runValueFacets(\n axis: RunValueAxis,\n query: RunFacetQuery,\n opts?: RunValueFacetOptions,\n ): Promise<RunValueFacetRow[]> {\n return this.request<RunValueFacetRow[]>({ kind: 'runValueFacets', axis, query, opts });\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 // Disjointness is per SCOPE: two policies over the same runs must not both claim a status (\"keep 100\"\n // vs \"keep 10\" of the same set is ambiguous), but `{ namespace: 'a' }` and `{ namespace: 'b' }` — or a\n // tenant-scoped policy next to an unscoped default — each rule their own selection.\n const seen = new Set<string>();\n for (const policy of retention.policies) {\n const scopeKey = policy.scope ? stableKey(policy.scope) : '';\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 const key = `${scopeKey}|${status}`;\n if (seen.has(key)) {\n throw new Error(\n `durable retention: status \"${status}\" appears in more than one policy with the same scope; status sets must be disjoint per scope`,\n );\n }\n seen.add(key);\n }\n }\n}\n\n/** A key-order-independent JSON key for a scope, so `{a,b}` and `{b,a}` compare equal. */\nfunction stableKey(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableKey).join(',')}]`;\n if (value && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => a.localeCompare(b));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableKey(v)}`).join(',')}}`;\n }\n return JSON.stringify(value);\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 /**\n * Prune one round.\n *\n * Never rejects, for the same reason {@link TimerPoller.poll} does not: it is awaited inside\n * `onApplicationBootstrap` (so a rejection propagates into the host's `bootstrap()` and kills the\n * process) and fired as `void` on the interval (so a rejection is an unhandled one, also fatal).\n * Pruning history is the least urgent thing this package does — a `pruneTerminalRuns` that fails\n * on a locked table must cost this sweep and nothing more.\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 } catch (error) {\n console.error('[nestjs-durable] retention sweep failed; retrying next interval:', error);\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 === 'runFacets') {\n // Same forced scope as `listRuns` above, for the same reason: the counts label a tenant's own\n // page, so they must be taken over exactly that tenant's runs and no one else's.\n const data = await this.gateway.runFacets({ ...body.query, namespace: msg.tenant });\n return { requestId: msg.requestId, result: { ok: true, data } };\n }\n\n if (body.kind === 'runValueFacets') {\n // Same forced scope as `listRuns`/`runFacets`: the values fill a picker for the tenant's own\n // list, so enumerating them over anyone else's runs would both leak other tenants' tag and\n // attribute vocabulary and offer choices this tenant's list can never match.\n const data = await this.gateway.runValueFacets(\n body.axis,\n { ...body.query, namespace: msg.tenant },\n body.opts,\n );\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: 'runFacets' }\n | { kind: 'runValueFacets' }\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 RunFacetQuery,\n type RunFacetRow,\n type RunGateway,\n type RunListItem,\n type RunQuery,\n type RunResult,\n type RunValueAxis,\n type RunValueFacetOptions,\n type RunValueFacetRow,\n type RunWaiting,\n STATE_STORE_CANONICAL,\n type StateStore,\n WorkflowEngine,\n indexWaitersByRun,\n listSignalWaitersOfRuns,\n mergeRunFacetRows,\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 read of the listed suspended runs' signal waiters resolves what each is parked on — signal\n // / webhook / child — with no per-run timeline fetch. A timer wait falls back to the run's own\n // `wakeAt`. Non-suspended (and remote-step-in-flight) runs carry no `waiting`.\n const waiterByRun = indexWaitersByRun(\n await listSignalWaitersOfRuns(\n this.store,\n runs.filter((run) => run.status === 'suspended').map((run) => run.id),\n ),\n );\n return runs.map((run) => {\n const waiting = resolveRunWaiting(run, waiterByRun);\n return waiting ? { ...run, waiting } : run;\n });\n }\n\n /**\n * The `(status, origin)` counts behind a console's chips, straight off the store's aggregate. The\n * console needs this precisely BECAUSE its list is paginated: the page bounds what is rendered,\n * these counts stay whole-set exact. A store with no aggregate falls back to counting a full\n * listing — correct, but the unbounded read this exists to avoid, so it is the last resort.\n */\n async runFacets(query: RunFacetQuery): Promise<RunFacetRow[]> {\n if (this.store.runFacets) return this.store.runFacets(query);\n const runs = await this.store.listRuns(query);\n return mergeRunFacetRows(runs.map((r) => ({ status: r.status, origin: r.origin, count: 1 })));\n }\n\n /**\n * The distinct values behind a console's pickers, straight off the store's enumeration. A store\n * without one answers `[]` rather than falling back to a listing: unlike the facet counts above —\n * where a full listing is at least the RIGHT answer expensively — a picker built from an unbounded\n * scan would be the same unbounded read on every keystroke, for a control whose free-text entry\n * still works without it.\n */\n async runValueFacets(\n axis: RunValueAxis,\n query: RunFacetQuery,\n opts?: RunValueFacetOptions,\n ): Promise<RunValueFacetRow[]> {\n return this.store.runValueFacets?.(axis, query, opts) ?? [];\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 read or querying `durable_step_checkpoints` directly. Mirrors\n * `listRuns`' waiting computation (the requested runs' signal waiters + `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 reads 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 listSignalWaitersOfRuns(this.store, runIds),\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` (plus the persisted `engine.schedules` when `persistedSchedules` is on) —\n * 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 // The `.catch` is load-bearing, not decorative: this is fire-and-forget, so an unhandled\n // rejection here terminates the process under Node's default policy — and the run it could not\n // drive is still waiting for the next poll tick either way.\n this.unsubscribeEnqueued = this.engine.onEnqueued((runId) => {\n void this.engine.runOne(runId).catch((error) => {\n console.error(`[nestjs-durable] could not run enqueued run ${runId}:`, error);\n });\n });\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 /**\n * One sweep of the engine's recovery work.\n *\n * **This method never rejects, and that is a hard requirement rather than defensiveness.** It has\n * exactly two callers and a rejection out of either one kills the process: on boot it is awaited\n * inside `onApplicationBootstrap`, so the rejection propagates through `NestApplication.init()`\n * into the host's `bootstrap()`; on the interval it is a fire-and-forget `void`, so it surfaces as\n * an unhandled rejection. Both are fatal, and both re-occur on the next boot with the same state,\n * so a single unrecoverable run crash-loops every worker in the deployment — taking down every\n * other workflow with it. A poll that goes wrong must cost this tick, not the pod.\n *\n * Each sweep is isolated separately because they are independent: a store that fails\n * `recoverIncomplete` says nothing about whether durable timers should still fire, and skipping\n * the rest of the tick would silently widen one subsystem's outage into all of them.\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.sweep('runPending', () => this.engine.runPending());\n await this.sweep('recoverIncomplete', () => this.engine.recoverIncomplete());\n await this.sweep('resumeDueTimers', () => this.engine.resumeDueTimers());\n await this.sweep('sweepTimeouts', () => this.engine.sweepTimeouts());\n const schedules = this.options.schedules;\n if (schedules && schedules.length > 0) {\n await this.sweep('schedules', () => runSchedules(this.engine, schedules, Date.now()));\n }\n if (this.options.persistedSchedules) {\n await this.sweep('persistedSchedules', () => this.engine.schedules.tick());\n }\n } finally {\n this.polling = false;\n }\n }\n\n /** Run one sweep, absorbing and reporting its failure — see {@link poll}. */\n private async sweep(name: string, run: () => Promise<unknown>): Promise<void> {\n try {\n await run();\n } catch (error) {\n console.error(`[nestjs-durable] timer poll: ${name} failed; continuing:`, error);\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 // Workflows whose declaring package could not be derived — reported once, after the scan.\n const unattributed: string[] = [];\n\n // Workflow registration pass — shared scan, engine-specific registration callback.\n scanWorkflows(this.discovery, (meta, workflow, origin) => {\n if (origin === undefined) unattributed.push(meta.name);\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 // Stamped onto every run this workflow starts (see `WorkflowRun.origin`). Derived from the\n // class's declaring file, so a lib gets attributed without opting in; `undefined` when it\n // could not be derived, which the engine records as unknown rather than defaulting.\n origin,\n tags: meta.tags,\n singleton: meta.singleton,\n concurrency: meta.concurrency,\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 // Declared on the very same class, so it carries the same origin.\n this.engine.register(dlqName, meta.version, inline, { origin });\n deadLetterByWorkflow.set(meta.name, dlqName);\n } else if (meta.deadLetterWorkflow) {\n deadLetterByWorkflow.set(meta.name, workflowName(meta.deadLetterWorkflow));\n }\n });\n\n this.reportUnattributed(unattributed);\n this.installDeadLetterRouting(deadLetterByWorkflow);\n }\n\n /**\n * Says, once at boot, which workflows could not be attributed to a package — their runs are stamped\n * with no `origin` at all, so an operator filtering the dashboard by origin will not see them. That\n * is the deliberate trade (an absent origin over an invented one), but it has to be visible: a\n * silently-dropped run is indistinguishable from a run that never happened.\n */\n private reportUnattributed(names: string[]): void {\n if (names.length === 0) return;\n console.warn(\n `[nestjs-durable] could not derive an origin (declaring package) for ${names.length} workflow(s): ${names.join(', ')}. Their runs carry no origin and will not match an origin filter.`,\n );\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 optional peers (only\n * needed if you use `@Workflow({ inputSchema })`), so they are reached through a dynamic `import()`\n * inside the validator: a static import would resolve them for every consumer of the package, and a\n * `require()` cannot work at all in the ESM build. That defers the \"peer missing\" error from\n * registration to the first `start` of a workflow that declares an `inputSchema` — the only moment\n * the peers are actually needed.\n */\nexport function classValidatorInput(cls: ClassCtor): (input: unknown) => Promise<void> {\n return async (input: unknown) => {\n let cv: any;\n let ct: any;\n try {\n cv = await import('class-validator');\n ct = await import('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 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","declaringFileByClass","WeakMap","packageNameByDir","Map","declaringFile","boundary","capture","Error","captureStackTrace","undefined","holder","topFrameFile","stack","rememberDeclaringFile","target","file","set","originOfClass","ctor","Object","getPrototypeOf","get","packageNameFor","originOfProvider","wrapper","fromMetatype","metatype","instance","start","dirname","visited","dir","has","cached","seen","push","name","readPackageName","join","parent","path","raw","readFileSync","parsed","JSON","parse","isRecord","length","value","line","split","frame","trim","startsWith","fileOfFrame","slice","match","exec","location","fileURLToPath","absolute","test","WORKFLOW_METADATA","Symbol","Workflow","options","declaredIn","declaringFile","target","rememberDeclaringFile","meta","name","version","deadLetterWorkflow","tags","singleton","concurrency","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","workerSdk","cause","Error","startRun","connection","options","runRedisWorker","createWorkerRuntime","DurableWorkerRuntime","Runtime","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_nestjs_durable_core","import_common","scanWorkflows","discovery","register","wrapper","getProviders","instance","meta","getWorkflowMeta","workflow","run","Error","name","originOfProvider","scanSteps","scanner","prototype","Object","getPrototypeOf","methodName","getAllMethodNames","method","getDurableStepMeta","boundMethod","handler","input","log","validInput","parse","output","call","RUN_REDIS_WORKER","Symbol","DURABLE_WORKER_RUNTIME","DURABLE_WORKER_RUNNERS","isPureThinWorker","options","store","undefined","connection","ThinWorkflowRegistrar","discovery","runtime","onModuleInit","scanWorkflows","meta","instance","origin","registerWorkflow","name","ctx","input","run","version","requires","ThinStepRegistrar","metadataScanner","scanSteps","handler","registerStep","ThinWorkerBootstrap","runners","runRedisWorker","runnersSink","onApplicationBootstrap","handle","partition","prefix","instanceId","concurrency","push","onApplicationShutdown","Promise","allSettled","map","h","close","thinWorkerProviders","provide","useFactory","createWorkerRuntime","workflowGroup","inject","DURABLE_OPTIONS_CANONICAL","useValue","defaultRunRedisWorker","tenantGatewayUnavailable","method","reject","Error","unavailableRunGateway","topology","role","getRunDetail","_runId","listRuns","_query","runFacets","runValueFacets","_axis","_opts","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_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","origin","registerWorkflow","name","ctx","input","run","version","requires","scanSteps","handler","registerStep","onApplicationBootstrap","handle","prefix","instanceId","concurrency","push","onApplicationShutdown","Promise","allSettled","map","close","inAppWorkerProviders","provide","useFactory","inject","TRANSPORT_CANONICAL","DURABLE_OPTIONS_CANONICAL","createWorkerRuntime","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","runFacets","runValueFacets","axis","opts","workerHealth","waitingFor","runIds","cancel","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","scopeKey","scope","stableKey","statuses","length","Error","maxAge","maxCount","parseDuration","status","TERMINAL_RUN_STATUSES","includes","join","key","has","add","value","Array","isArray","map","entries","Object","filter","v","undefined","sort","a","b","localeCompare","k","JSON","stringify","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","error","RunRequestResponder","transport","gateway","start","onRunRequest","msg","reply","handle","publishRunReply","body","kind","data","listRuns","query","namespace","tenant","requestId","result","ok","runFacets","runValueFacets","axis","opts","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","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","listSignalWaitersOfRuns","filter","status","map","id","waiting","resolveRunWaiting","runFacets","mergeRunFacetRows","r","origin","count","runValueFacets","axis","opts","waitingFor","runIds","length","idSet","Set","suspended","waiters","statuses","result","has","workerHealth","cancel","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","catch","error","console","poll","intervalMs","timerPollMs","setInterval","unref","onModuleDestroy","clearInterval","sweep","runPending","recoverIncomplete","resumeDueTimers","sweepTimeouts","schedules","length","runSchedules","Date","now","persistedSchedules","tick","name","run","import_nestjs_durable_core","CONTEXT_ACCESSOR","Symbol","for","RUN_GATEWAY","RunGateway","import_nestjs_durable_core","import_common","import_core","classValidatorInput","cls","input","cv","ct","Error","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","unattributed","scanWorkflows","meta","workflow","origin","undefined","push","validateInput","inputSchema","classValidatorInput","eventBatch","debounce","mode","windowMs","parseDuration","batch","maxSize","within","workflowCtor","register","version","ctx","input","run","tags","singleton","concurrency","executionTimeout","requires","searchAttributesSchema","searchAttributes","onEvent","getOnEvents","group","tenantGroup","sanitizeQueueToken","partition","executor","RemoteWorkflowExecutor","bindWorkflowClass","start","runId","opts","waitForRun","inline","findDeadLetterHandler","deadLetterWorkflow","dlqName","set","workflowName","reportUnattributed","installDeadLetterRouting","names","length","console","warn","join","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","catch","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","remoteRedispatchMs","remoteRedispatchMax","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"]}