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