@dudousxd/nestjs-durable 0.0.0-beta-20260701163515
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.cjs +1345 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +718 -0
- package/dist/index.d.ts +718 -0
- package/dist/index.js +1286 -0
- package/dist/index.js.map +1 -0
- package/package.json +82 -0
- package/skills/durable-determinism/SKILL.md +144 -0
- package/skills/durable-setup/SKILL.md +189 -0
- package/skills/durable-signals-and-timers/SKILL.md +181 -0
- package/skills/durable-testing/SKILL.md +146 -0
- package/skills/durable-workflows/SKILL.md +183 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
import { StepError, WorkflowRef, SingletonConfig, WorkflowExecutor, StateStore, Transport, NamedTransport, ControlPlane, ScheduledWorkflow, RetentionPolicy, QueueConfig, AdmissionBackend, WorkflowEngine, EntityHandler, StepInvocation, WorkflowClass, WorkflowInputOf, StartOptions, RunResult } from '@dudousxd/nestjs-durable-core';
|
|
2
|
+
import { ConcurrencyOption, RunRedisWorkerOptions, RunningWorker, DurableWorkerRuntime } from '@dudousxd/durable-worker';
|
|
3
|
+
import { DynamicModule, InjectionToken, OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown, Provider } from '@nestjs/common';
|
|
4
|
+
import { DiscoveryService, MetadataScanner } from '@nestjs/core';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Local, structural mirror of `@dudousxd/nestjs-context`'s public accessor
|
|
8
|
+
* (`packages/core/src/accessor.ts`).
|
|
9
|
+
*
|
|
10
|
+
* We deliberately do NOT import nestjs-context (it is an OPTIONAL peer). Instead
|
|
11
|
+
* we declare the same shape here and inject it via the shared
|
|
12
|
+
* {@link CONTEXT_ACCESSOR} token with `@Optional()`. Any object that structurally
|
|
13
|
+
* satisfies this interface — including nestjs-context's real accessor — works.
|
|
14
|
+
*
|
|
15
|
+
* Kept byte-aligned with nestjs-context's `ContextAccessor` (and the identical
|
|
16
|
+
* mirror in `@dudousxd/nestjs-authz`): `traceId()` / `tenantId()` are REQUIRED
|
|
17
|
+
* (the real accessor always provides them) and `get()` is included, so the
|
|
18
|
+
* structural match stays exact when DurableModule auto-feeds the carrier.
|
|
19
|
+
*/
|
|
20
|
+
interface UserRef {
|
|
21
|
+
type: string;
|
|
22
|
+
id: string | number;
|
|
23
|
+
}
|
|
24
|
+
/** Opaque shape of the context store. durable never reads it; mirrors the upstream surface. */
|
|
25
|
+
type ContextStore = Record<string, unknown>;
|
|
26
|
+
interface ContextAccessor {
|
|
27
|
+
/** Trace id for the current request, or `undefined` when unavailable. */
|
|
28
|
+
traceId(): string | undefined;
|
|
29
|
+
/** Current tenant id, or `undefined` when no multi-tenant context is populated. */
|
|
30
|
+
tenantId(): string | undefined;
|
|
31
|
+
/** Reference to the current user, or `undefined` when unauthenticated. */
|
|
32
|
+
userRef(): UserRef | undefined;
|
|
33
|
+
/** The raw context store for the current request, or `undefined`. */
|
|
34
|
+
get(): ContextStore | undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
declare const WORKFLOW_METADATA: unique symbol;
|
|
38
|
+
interface WorkflowMeta {
|
|
39
|
+
name: string;
|
|
40
|
+
version: string;
|
|
41
|
+
/** The workflow this workflow's dead runs route to (a name or a class). See `WorkflowOptions`. */
|
|
42
|
+
deadLetterWorkflow?: WorkflowRef | undefined;
|
|
43
|
+
/** Static searchable labels stamped on every run of this workflow. See `WorkflowOptions`. */
|
|
44
|
+
tags?: string[] | undefined;
|
|
45
|
+
/** Per-key serialization (a durable mutex). See `WorkflowOptions`. */
|
|
46
|
+
singleton?: SingletonConfig | undefined;
|
|
47
|
+
/** Max wall-clock lifetime before a run is cancelled (e.g. `'2h'`). See `WorkflowOptions`. */
|
|
48
|
+
executionTimeout?: string | number | undefined;
|
|
49
|
+
/** class-validator DTO validated at start. See `WorkflowOptions`. */
|
|
50
|
+
inputSchema?: (new (...args: any[]) => object) | undefined;
|
|
51
|
+
/** Custom input validator (throws on invalid). See `WorkflowOptions`. */
|
|
52
|
+
validateInput?: ((input: unknown) => void | Promise<void>) | undefined;
|
|
53
|
+
/** Event names that start a fresh run of this workflow. See `WorkflowOptions`. */
|
|
54
|
+
onEvent?: string[] | undefined;
|
|
55
|
+
/** Debounce `onEvent` triggers — fire once it's quiet for this long. See `WorkflowOptions`. */
|
|
56
|
+
debounce?: string | number | undefined;
|
|
57
|
+
/** Batch `onEvent` triggers — fire on size or window. See `WorkflowOptions`. */
|
|
58
|
+
batch?: {
|
|
59
|
+
maxSize: number;
|
|
60
|
+
within: string | number;
|
|
61
|
+
} | undefined;
|
|
62
|
+
}
|
|
63
|
+
interface WorkflowOptions {
|
|
64
|
+
name: string;
|
|
65
|
+
version?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Route this workflow's dead-lettered runs to another **registered** workflow — by class
|
|
68
|
+
* (`deadLetterWorkflow: CheckoutDlqWorkflow`, refactor-safe) or by name for a cross-runtime
|
|
69
|
+
* handler. For a handler co-located on the same class, prefer an inline `@DeadLetter()` method
|
|
70
|
+
* instead — it takes precedence, and declaring both is a boot-time error. The handler receives a
|
|
71
|
+
* {@link DeadLetter} payload, idempotent by a `dlq:<runId>` id.
|
|
72
|
+
*/
|
|
73
|
+
deadLetterWorkflow?: WorkflowRef;
|
|
74
|
+
/**
|
|
75
|
+
* Static labels stamped on **every run** of this workflow (e.g. `tags: ['etl', 'critical']`) and
|
|
76
|
+
* merged with any per-run tags passed to `start`. Searchable/filterable in the dashboard.
|
|
77
|
+
*/
|
|
78
|
+
tags?: string[];
|
|
79
|
+
/**
|
|
80
|
+
* Serialize runs of this workflow that share a key — a durable FIFO mutex. e.g.
|
|
81
|
+
* `singleton: { key: (input) => `base:${input.baseId}` }` runs at most one pipeline per base at a
|
|
82
|
+
* time; same-key runs queue (suspended) and admit in creation order as slots free. `limit` (default
|
|
83
|
+
* 1) raises the concurrency.
|
|
84
|
+
*/
|
|
85
|
+
singleton?: SingletonConfig;
|
|
86
|
+
/**
|
|
87
|
+
* Max wall-clock lifetime for a run of this workflow (e.g. `'2h'`, `'7 days'`, or ms). A run that
|
|
88
|
+
* outlives it is moved to `cancelled` (`execution_timeout`) by the timer poller — a backstop for
|
|
89
|
+
* runs that get stuck or loop forever. Omit for no limit.
|
|
90
|
+
*/
|
|
91
|
+
executionTimeout?: string | number;
|
|
92
|
+
/**
|
|
93
|
+
* Validate the workflow input at `start` against a **class-validator DTO** (the same
|
|
94
|
+
* `plainToInstance` + `validate` NestJS runs in controllers) — invalid input is rejected before any
|
|
95
|
+
* run is created. Needs the optional peers `class-validator` + `class-transformer`.
|
|
96
|
+
* `@Workflow({ inputSchema: CheckoutInput })`.
|
|
97
|
+
*/
|
|
98
|
+
inputSchema?: new (...args: any[]) => object;
|
|
99
|
+
/**
|
|
100
|
+
* Custom input validator (throws on invalid) — an escape hatch for zod/yup/etc. instead of
|
|
101
|
+
* `inputSchema`. Takes precedence over `inputSchema` if both are set.
|
|
102
|
+
*/
|
|
103
|
+
validateInput?: (input: unknown) => void | Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Start a fresh run of this workflow whenever any of these events is published via
|
|
106
|
+
* `publishEvent(name, payload)` — the payload becomes the run's input. e.g.
|
|
107
|
+
* `onEvent: ['user.registered', 'user.invited']`. The same subscription can also be declared with
|
|
108
|
+
* the `@OnEvent(...)` class decorator; the two are merged.
|
|
109
|
+
*/
|
|
110
|
+
onEvent?: string[];
|
|
111
|
+
/**
|
|
112
|
+
* Coalesce `onEvent` triggers by **debouncing** — start one run only once events have been quiet
|
|
113
|
+
* for this long (resets on each event), with the LAST payload. e.g. `debounce: '30s'`.
|
|
114
|
+
*/
|
|
115
|
+
debounce?: string | number;
|
|
116
|
+
/**
|
|
117
|
+
* Coalesce `onEvent` triggers by **batching** — start one run with all payloads (`{ events: [...] }`)
|
|
118
|
+
* once `maxSize` is reached or `within` elapses from the first event. e.g.
|
|
119
|
+
* `batch: { maxSize: 100, within: '10s' }`.
|
|
120
|
+
*/
|
|
121
|
+
batch?: {
|
|
122
|
+
maxSize: number;
|
|
123
|
+
within: string | number;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Marks a provider class as a durable workflow. Its `run(ctx, input)` method becomes the
|
|
128
|
+
* workflow function the engine executes and replays.
|
|
129
|
+
*/
|
|
130
|
+
declare function Workflow(options: WorkflowOptions): ClassDecorator;
|
|
131
|
+
declare function getWorkflowMeta(target: Function): WorkflowMeta | undefined;
|
|
132
|
+
declare const DURABLE_STEP_METADATA: unique symbol;
|
|
133
|
+
interface DurableStepMeta {
|
|
134
|
+
name: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Marks a provider method as the handler for a remote step `name`. An in-process transport
|
|
138
|
+
* (e.g. the event-emitter transport) routes dispatched tasks to it. The method's single
|
|
139
|
+
* argument is the step input; its return value is the step output.
|
|
140
|
+
*/
|
|
141
|
+
declare function Step(name: string): MethodDecorator;
|
|
142
|
+
/**
|
|
143
|
+
* @deprecated Use `@Step` instead. `@DurableStep` is a back-compat alias of {@link Step} and
|
|
144
|
+
* writes the same `DURABLE_STEP_METADATA`, so discovery/registrars treat them identically.
|
|
145
|
+
*/
|
|
146
|
+
declare const DurableStep: typeof Step;
|
|
147
|
+
declare function getDurableStepMeta(method: Function): DurableStepMeta | undefined;
|
|
148
|
+
declare const DEAD_LETTER_METADATA: unique symbol;
|
|
149
|
+
/**
|
|
150
|
+
* The payload a dead-letter handler receives: the dead run's id, its workflow, the input it was
|
|
151
|
+
* started with (typed via `TInput`), and the failure that killed it.
|
|
152
|
+
*/
|
|
153
|
+
interface DeadLetter<TInput = unknown> {
|
|
154
|
+
/** Id of the run that was dead-lettered (inspectable + retriable in the dashboard). */
|
|
155
|
+
deadRunId: string;
|
|
156
|
+
/** Name of the workflow whose run died. */
|
|
157
|
+
workflow: string;
|
|
158
|
+
/** The original input the dead run was started with. */
|
|
159
|
+
input: TInput;
|
|
160
|
+
/** The structured error that moved the run to `dead`, when known. */
|
|
161
|
+
error?: StepError;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Marks a method on a `@Workflow` class as that workflow's **inline dead-letter handler**. When a
|
|
165
|
+
* run of the workflow is moved to `dead` (exceeded `maxRecoveryAttempts`), this method runs — as a
|
|
166
|
+
* durable workflow itself, auto-registered as `<workflow>.dlq` with a `dlq:<runId>` id — receiving a
|
|
167
|
+
* {@link DeadLetter} payload. It shares the class's injected dependencies, so the handler lives in
|
|
168
|
+
* the same file as the workflow it protects.
|
|
169
|
+
*
|
|
170
|
+
* Takes precedence over `@Workflow({ deadLetterWorkflow })` and the module-level `deadLetterWorkflow`
|
|
171
|
+
* default. The method signature is `(ctx, dead)` — the same `ctx` a workflow `run` gets.
|
|
172
|
+
*/
|
|
173
|
+
declare function DeadLetter(): MethodDecorator;
|
|
174
|
+
declare function isDeadLetterHandler(method: Function): boolean;
|
|
175
|
+
declare const ON_EVENT_METADATA: unique symbol;
|
|
176
|
+
/**
|
|
177
|
+
* Subscribe a `@Workflow` class to one or more events: when any of them is published via
|
|
178
|
+
* `publishEvent(name, payload)`, a fresh run of the workflow starts with the payload as input.
|
|
179
|
+
* Equivalent to `@Workflow({ onEvent })` — use whichever reads better; multiple `@OnEvent(...)`
|
|
180
|
+
* decorators and the option are all merged.
|
|
181
|
+
*/
|
|
182
|
+
declare function OnEvent(...events: string[]): ClassDecorator;
|
|
183
|
+
/** All events a workflow class subscribes to — the union of `@Workflow({ onEvent })` and `@OnEvent`. */
|
|
184
|
+
declare function getOnEvents(meta: WorkflowMeta, target: object): string[];
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Options for a **store-less thin worker** process: where to consume (`connection`/`prefix`) and
|
|
188
|
+
* which worker `groups` this process serves. There is deliberately NO `store`, `transport`, engine,
|
|
189
|
+
* dashboard, recovery, or timer config — a thin worker only registers `@Workflow`/`@Step`
|
|
190
|
+
* handlers and runs the BullMQ consumer (see {@link DurableWorkerModule}).
|
|
191
|
+
*/
|
|
192
|
+
interface DurableWorkerModuleOptions {
|
|
193
|
+
/** ioredis connection (string or options), as `runRedisWorker` / `BullMQTransport` accept. */
|
|
194
|
+
connection: string | Record<string, unknown>;
|
|
195
|
+
/** The worker groups this process serves — one BullMQ consumer is started per group. */
|
|
196
|
+
groups: string[];
|
|
197
|
+
/**
|
|
198
|
+
* The tenant this worker process serves. Each of {@link groups} is suffixed via
|
|
199
|
+
* `tenantGroup(group, tenant)` (`@dudousxd/nestjs-durable-core`) before the consumer starts, so
|
|
200
|
+
* `undefined`, `''`, or `'default'` stays byte-identical to the bare group (production
|
|
201
|
+
* unchanged), and any other tenant serves `<group>@<tenant>` — matching the group name an
|
|
202
|
+
* OPERATOR control plane's `remoteByConvention` routes that tenant's runs to
|
|
203
|
+
* (`tenantGroup(run.workflow, run.namespace)` on the engine side).
|
|
204
|
+
*/
|
|
205
|
+
tenant?: string;
|
|
206
|
+
/** Key prefix namespacing the durable queues. Defaults to `durable` (matches the transport). */
|
|
207
|
+
prefix?: string;
|
|
208
|
+
/** Stable id for this worker process in heartbeats/control. Defaults to a per-host/pid id. */
|
|
209
|
+
instanceId?: string;
|
|
210
|
+
/**
|
|
211
|
+
* How many tasks each group's consumer runs concurrently (BullMQ Worker concurrency). Defaults to 1.
|
|
212
|
+
* Applies to every group unless overridden per-group by {@link concurrencyByGroup}. Raise it so a
|
|
213
|
+
* fanned-out batch (e.g. the N remote steps of a `gather`) runs in parallel instead of serially.
|
|
214
|
+
*
|
|
215
|
+
* Pass `'adaptive'` (or `{ mode:'adaptive', ... }`) to let each consumer self-tune its concurrency
|
|
216
|
+
* (latency gradient + RAM brake + backpressure) and publish a live status on its heartbeat.
|
|
217
|
+
*/
|
|
218
|
+
concurrency?: ConcurrencyOption;
|
|
219
|
+
/** Per-group concurrency override, keyed by group name. Falls back to {@link concurrency} (then 1).
|
|
220
|
+
* Each value may itself be `'adaptive'` / `{ mode:'adaptive', ... }`. */
|
|
221
|
+
concurrencyByGroup?: Record<string, ConcurrencyOption>;
|
|
222
|
+
}
|
|
223
|
+
interface DurableWorkerModuleAsyncOptions {
|
|
224
|
+
useFactory: (...args: never[]) => DurableWorkerModuleOptions | Promise<DurableWorkerModuleOptions>;
|
|
225
|
+
inject?: InjectionToken[];
|
|
226
|
+
}
|
|
227
|
+
/** The resolved {@link DurableWorkerModuleOptions} (canonical token). */
|
|
228
|
+
declare const DURABLE_WORKER_OPTIONS: unique symbol;
|
|
229
|
+
/**
|
|
230
|
+
* The `runRedisWorker` function the module uses to start each group's BullMQ consumer. Defaults to
|
|
231
|
+
* the real one from `@dudousxd/durable-worker`; tests `overrideProvider` it with a fake so no real
|
|
232
|
+
* Redis is needed.
|
|
233
|
+
*/
|
|
234
|
+
declare const RUN_REDIS_WORKER: unique symbol;
|
|
235
|
+
/** The list of started {@link RunningWorker} handles (one per group), closed on shutdown. */
|
|
236
|
+
declare const DURABLE_WORKER_RUNNERS: unique symbol;
|
|
237
|
+
/** The signature of `runRedisWorker` — injected behind {@link RUN_REDIS_WORKER}. */
|
|
238
|
+
type RunRedisWorkerFn = (opts: RunRedisWorkerOptions) => Promise<RunningWorker>;
|
|
239
|
+
/**
|
|
240
|
+
* Discovers every provider carrying `@Workflow` metadata and registers its `run(ctx, input)` on the
|
|
241
|
+
* thin {@link DurableWorkerRuntime}. Mirrors the engine-side `WorkflowRegistrar`, but registers on
|
|
242
|
+
* the runner-core runtime instead of a `WorkflowEngine` — the runtime drives `run` with the thin
|
|
243
|
+
* `WorkflowContext` (which `implements WorkflowCtx`), so the body runs unchanged. NO store/engine.
|
|
244
|
+
*/
|
|
245
|
+
declare class ThinWorkflowRegistrar implements OnModuleInit {
|
|
246
|
+
private readonly discovery;
|
|
247
|
+
private readonly runtime;
|
|
248
|
+
constructor(discovery: DiscoveryService, runtime: DurableWorkerRuntime);
|
|
249
|
+
onModuleInit(): void;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Discovers every `@Step` method and registers it as a step handler on the thin
|
|
253
|
+
* {@link DurableWorkerRuntime}. Mirrors the engine-side `DurableStepRegistrar`, but always registers
|
|
254
|
+
* on the runtime (a thin worker IS the consumer — there is no in-process-vs-queue branch).
|
|
255
|
+
*/
|
|
256
|
+
declare class ThinStepRegistrar implements OnModuleInit {
|
|
257
|
+
private readonly discovery;
|
|
258
|
+
private readonly metadataScanner;
|
|
259
|
+
private readonly runtime;
|
|
260
|
+
constructor(discovery: DiscoveryService, metadataScanner: MetadataScanner, runtime: DurableWorkerRuntime);
|
|
261
|
+
onModuleInit(): void;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Starts one BullMQ consumer per configured group on bootstrap (after both registrars have run, so
|
|
265
|
+
* every handler is registered before any task is consumed) and closes them on shutdown. The only
|
|
266
|
+
* runtime side effect of a thin worker: register handlers, run the consumer. No engine, no store,
|
|
267
|
+
* no recovery, no timers, no dispatch.
|
|
268
|
+
*
|
|
269
|
+
* For the shutdown close to fire, enable Nest's shutdown hooks: `app.enableShutdownHooks()`.
|
|
270
|
+
*/
|
|
271
|
+
declare class ThinWorkerBootstrap implements OnApplicationBootstrap, OnApplicationShutdown {
|
|
272
|
+
private readonly runtime;
|
|
273
|
+
private readonly options;
|
|
274
|
+
private readonly runRedisWorker;
|
|
275
|
+
private readonly runnersSink;
|
|
276
|
+
private readonly runners;
|
|
277
|
+
constructor(runtime: DurableWorkerRuntime, options: DurableWorkerModuleOptions, runRedisWorker: RunRedisWorkerFn, runnersSink: RunningWorker[]);
|
|
278
|
+
onApplicationBootstrap(): Promise<void>;
|
|
279
|
+
onApplicationShutdown(): Promise<void>;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* A NestJS dynamic module that turns an app into a **PURE durable worker**: it discovers
|
|
283
|
+
* `@Workflow`/`@Step` providers, registers them on the thin {@link DurableWorkerRuntime}, and
|
|
284
|
+
* runs one BullMQ consumer per group via `runRedisWorker`. It is **control-plane-less** — it binds
|
|
285
|
+
* NO `WorkflowEngine`, NO store/ORM, NO dashboard, NO timer poller, NO recovery, and NO run dispatch.
|
|
286
|
+
* Use it for a worker pod that only executes work an engine elsewhere coordinates.
|
|
287
|
+
*
|
|
288
|
+
* Contrast with `DurableModule` (`worker: true`), which is a full control plane WITH a store: this
|
|
289
|
+
* module is the store-less half. A workflow body runs identically on either (conformance-tested).
|
|
290
|
+
*/
|
|
291
|
+
declare class DurableWorkerModule {
|
|
292
|
+
static forRoot(options: DurableWorkerModuleOptions): DynamicModule;
|
|
293
|
+
static forRootAsync(options: DurableWorkerModuleAsyncOptions): DynamicModule;
|
|
294
|
+
private static build;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Opt-in config for an **in-app worker**: the same NestJS process runs the engine AND serves its own
|
|
299
|
+
* discovered `@Workflow`/`@Step` on one default `group`. The engine registers each `@Workflow`
|
|
300
|
+
* GROUP-SERVED (its turns are dispatched to `group` over the transport via a {@link RemoteWorkflowExecutor}
|
|
301
|
+
* instead of run inline), and a co-located {@link DurableWorkerRuntime} consumes `group` and replays the
|
|
302
|
+
* very same TS bodies. This is the uniform-dispatch "one app, both roles" shape — every turn pays a
|
|
303
|
+
* transport round-trip even though the worker is the same process. Requires a transport that carries
|
|
304
|
+
* workflow tasks (BullMQ); an in-process-only transport cannot dispatch a {@link WorkflowExecutor}.
|
|
305
|
+
*/
|
|
306
|
+
interface DurableInAppWorkerOptions {
|
|
307
|
+
/** The default group this app dispatches its `@Workflow` turns to AND consumes as a worker. */
|
|
308
|
+
group: string;
|
|
309
|
+
/** ioredis connection (string or options) for the co-located worker consumer (`runRedisWorker`). */
|
|
310
|
+
connection: string | Record<string, unknown>;
|
|
311
|
+
/** Key prefix namespacing the durable queues. Defaults to `durable` (matches the transport). */
|
|
312
|
+
prefix?: string;
|
|
313
|
+
/** Stable id for this worker process in heartbeats/control. Defaults to a per-host/pid id. */
|
|
314
|
+
instanceId?: string;
|
|
315
|
+
/**
|
|
316
|
+
* How many tasks the co-located worker runs concurrently from its group's queue (BullMQ Worker
|
|
317
|
+
* concurrency). Defaults to 1. Raise it so a fanned-out batch (e.g. the N remote steps of a
|
|
318
|
+
* `gather`) runs in parallel. Per process; total parallelism is `concurrency × replicas`.
|
|
319
|
+
*
|
|
320
|
+
* Pass `'adaptive'` (or `{ mode:'adaptive', ... }`) to let the worker self-tune its concurrency
|
|
321
|
+
* (latency gradient + RAM brake + backpressure) and publish a live status on its heartbeat.
|
|
322
|
+
*/
|
|
323
|
+
concurrency?: ConcurrencyOption;
|
|
324
|
+
}
|
|
325
|
+
/** The resolved {@link DurableInAppWorkerOptions} or `null` when the app didn't opt in. */
|
|
326
|
+
declare const IN_APP_WORKER_OPTIONS: unique symbol;
|
|
327
|
+
/**
|
|
328
|
+
* The group-served binding the {@link import('./workflow.registrar').WorkflowRegistrar} applies to every
|
|
329
|
+
* discovered `@Workflow`: `{ group, executor }` when the app opted into an in-app worker, else `null`
|
|
330
|
+
* (workflows register inline — the unchanged default). The executor is a {@link RemoteWorkflowExecutor}
|
|
331
|
+
* over the engine's own transport, so a group-served turn is dispatched to `group` and the co-located
|
|
332
|
+
* worker (below) advances it.
|
|
333
|
+
*/
|
|
334
|
+
declare const IN_APP_WORKER_BINDING: unique symbol;
|
|
335
|
+
/** The co-located worker's {@link DurableWorkerRuntime} (the consumer half). */
|
|
336
|
+
declare const IN_APP_WORKER_RUNTIME: unique symbol;
|
|
337
|
+
/** `runRedisWorker`, injected so tests can substitute a fake (no real Redis). Defaults to the real one. */
|
|
338
|
+
declare const IN_APP_RUN_REDIS_WORKER: unique symbol;
|
|
339
|
+
/** Started {@link RunningWorker} handles for the in-app group(s), closed on shutdown. */
|
|
340
|
+
declare const IN_APP_WORKER_RUNNERS: unique symbol;
|
|
341
|
+
/** The group-served binding shape resolved behind {@link IN_APP_WORKER_BINDING}. */
|
|
342
|
+
interface InAppWorkerBinding {
|
|
343
|
+
group: string;
|
|
344
|
+
executor: WorkflowExecutor;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* The consumer half of the in-app worker: on init it registers every discovered `@Workflow`/`@Step`
|
|
348
|
+
* on a {@link DurableWorkerRuntime} (the SAME bodies the engine registered group-served), and on bootstrap
|
|
349
|
+
* it starts one `runRedisWorker` consumer on the configured group, closing it on shutdown. A no-op when
|
|
350
|
+
* the app didn't opt in. Mirrors the thin {@link import('./durable-worker.module').DurableWorkerModule},
|
|
351
|
+
* but co-located with a full engine.
|
|
352
|
+
*/
|
|
353
|
+
declare class InAppWorkerBootstrap implements OnModuleInit, OnApplicationBootstrap, OnApplicationShutdown {
|
|
354
|
+
private readonly discovery;
|
|
355
|
+
private readonly metadataScanner;
|
|
356
|
+
private readonly options;
|
|
357
|
+
private readonly runtime;
|
|
358
|
+
private readonly runRedisWorker;
|
|
359
|
+
private readonly runnersSink;
|
|
360
|
+
private readonly runners;
|
|
361
|
+
constructor(discovery: DiscoveryService, metadataScanner: MetadataScanner, options: DurableInAppWorkerOptions | null, runtime: DurableWorkerRuntime, runRedisWorker: RunRedisWorkerFn, runnersSink: RunningWorker[]);
|
|
362
|
+
onModuleInit(): void;
|
|
363
|
+
onApplicationBootstrap(): Promise<void>;
|
|
364
|
+
onApplicationShutdown(): Promise<void>;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* The providers that stand up an in-app worker, added to {@link import('./durable.module').DurableModule}.
|
|
368
|
+
* All are inert when `inAppWorker` is unset (the binding resolves to `null`, the bootstrap no-ops), so a
|
|
369
|
+
* plain `DurableModule` is byte-for-byte unchanged.
|
|
370
|
+
*/
|
|
371
|
+
declare function inAppWorkerProviders(): Provider[];
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Retention config for the {@link RetentionPoller}. One or more {@link RetentionPolicy policies}
|
|
375
|
+
* (status sets must be disjoint — validated at boot), swept together on a shared interval.
|
|
376
|
+
*
|
|
377
|
+
* ```ts
|
|
378
|
+
* retention: {
|
|
379
|
+
* sweepInterval: '1m',
|
|
380
|
+
* batchSize: 1_000,
|
|
381
|
+
* policies: [
|
|
382
|
+
* { statuses: ['completed', 'cancelled'], maxAge: '14d', maxCount: 200 },
|
|
383
|
+
* { statuses: ['failed'], maxAge: '90d' }, // keep failures longer for debugging
|
|
384
|
+
* ],
|
|
385
|
+
* }
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
interface DurableRetentionOptions {
|
|
389
|
+
/** The retention rules, one per (disjoint) status group. */
|
|
390
|
+
policies: RetentionPolicy[];
|
|
391
|
+
/**
|
|
392
|
+
* How often to run the prune sweep. A number is milliseconds; a string is an `ms`-style duration
|
|
393
|
+
* (`'1m'`, `'5m'` — `'m'` is minutes). `0` runs it once on boot only. Defaults to 60000 (1 minute).
|
|
394
|
+
*/
|
|
395
|
+
sweepInterval?: number | string;
|
|
396
|
+
/** Max runs hard-deleted per batch (per policy, looped until drained). Defaults to 1000. */
|
|
397
|
+
batchSize?: number;
|
|
398
|
+
}
|
|
399
|
+
interface DurableModuleOptions {
|
|
400
|
+
store: StateStore;
|
|
401
|
+
transport?: Transport;
|
|
402
|
+
/**
|
|
403
|
+
* An ordered pool of named transports for failover / multi-broker setups. The engine dispatches on
|
|
404
|
+
* the first and fails over to the next; a step pins one via `ctx.call(step, input, { transport })`.
|
|
405
|
+
* Use instead of `transport`.
|
|
406
|
+
*/
|
|
407
|
+
transports?: NamedTransport[];
|
|
408
|
+
/**
|
|
409
|
+
* Cross-instance broadcast pub/sub (lifecycle events + cancellation). Defaults to the (first)
|
|
410
|
+
* transport when it can broadcast (event-emitter, BullMQ); set explicitly to use a dedicated one.
|
|
411
|
+
*/
|
|
412
|
+
controlPlane?: ControlPlane;
|
|
413
|
+
/** Interval (ms) for the durable-timer poller. `0` disables it. Defaults to 1000. */
|
|
414
|
+
timerPollMs?: number;
|
|
415
|
+
/**
|
|
416
|
+
* Auto-create the durable tables on boot via `store.ensureSchema()`. Defaults to true. Turn
|
|
417
|
+
* off in production and call the store adapter's `ensure*DurableSchema()` from a migration.
|
|
418
|
+
*/
|
|
419
|
+
autoSchema?: boolean;
|
|
420
|
+
/**
|
|
421
|
+
* Worker-pool partition for this instance (forwarded to the engine). The poll paths
|
|
422
|
+
* (`runPending`/`recoverIncomplete`/`resumeDueTimers`/`sweepTimeouts`) only act on runs in this
|
|
423
|
+
* namespace. Set distinct values to safely share ONE state store across non-interchangeable
|
|
424
|
+
* pools — e.g. a developer's local instance vs the deployed cluster. **Omit it to make this
|
|
425
|
+
* instance an OPERATOR** — an unset namespace drives/recovers/resumes runs of EVERY namespace and
|
|
426
|
+
* leaves the transport on its bare prefix. See `WorkflowEngineDeps.namespace`.
|
|
427
|
+
*/
|
|
428
|
+
namespace?: string;
|
|
429
|
+
/**
|
|
430
|
+
* Multi-instance recovery lease, in ms — how long an instance owns a run it picked up before
|
|
431
|
+
* another may take over. Defaults to 30000. Set above your longest synchronous run.
|
|
432
|
+
*/
|
|
433
|
+
leaseMs?: number;
|
|
434
|
+
/** Unique id for this instance (for leases). Defaults to a random id. */
|
|
435
|
+
instanceId?: string;
|
|
436
|
+
/**
|
|
437
|
+
* Cap recovery attempts before a still-`running` run is moved to the `dead` dead-letter state
|
|
438
|
+
* (a poison pill that crashes the process every boot). Omit for unlimited.
|
|
439
|
+
*/
|
|
440
|
+
maxRecoveryAttempts?: number;
|
|
441
|
+
/**
|
|
442
|
+
* Opt-in liveness deadline (ms) for a remote (polyglot) workflow `advance`. If the worker neither
|
|
443
|
+
* returns a decision nor sends a run-scoped heartbeat within this window, the engine presumes it dead
|
|
444
|
+
* and lets recovery re-drive; each heartbeat rearms the window so a slow-but-alive worker is never
|
|
445
|
+
* re-driven. Pair with a worker SDK that emits run-scoped heartbeats (`@dudousxd/durable-worker` ≥ the
|
|
446
|
+
* release that ships them, and the Python `durable-worker`). Omit for the prior unbounded await.
|
|
447
|
+
*/
|
|
448
|
+
remoteAdvanceSilenceMs?: number;
|
|
449
|
+
/**
|
|
450
|
+
* The **default** workflow to route dead-lettered runs to, for workflows that don't declare their
|
|
451
|
+
* own. When a run is moved to `dead` (exceeded `maxRecoveryAttempts`), the started handler gets a
|
|
452
|
+
* `DeadLetter` payload `{ deadRunId, workflow, input, error }` (idempotent by a `dlq:<runId>` id) —
|
|
453
|
+
* it can alert, compensate, or queue for review. Resolution per dead run: the workflow's inline
|
|
454
|
+
* `@DeadLetter()` method → its `@Workflow({ deadLetterWorkflow })` reference → this default. Omit
|
|
455
|
+
* everything to just leave dead runs parked (inspectable + retriable from the dashboard). Accepts a
|
|
456
|
+
* workflow class (refactor-safe) or a name (cross-runtime).
|
|
457
|
+
*/
|
|
458
|
+
deadLetterWorkflow?: WorkflowRef;
|
|
459
|
+
/**
|
|
460
|
+
* Whether this instance plays the **worker** role: register `@Step` handlers (consume the
|
|
461
|
+
* transport), recover incomplete runs on boot, and poll due timers. Defaults to `true`. Set
|
|
462
|
+
* `false` for a **dashboard/dispatch-only** instance (e.g. an API pod) that mounts the control
|
|
463
|
+
* plane and reads the store but must not process or recover workflows — leave that to the workers.
|
|
464
|
+
*/
|
|
465
|
+
worker?: boolean;
|
|
466
|
+
/**
|
|
467
|
+
* Internal axis, orthogonal to {@link worker}: whether this instance DRIVES runs — polls
|
|
468
|
+
* pending, recovers crashed (`recoverIncomplete`), resumes due timers, sweeps timeouts — even
|
|
469
|
+
* when it does not itself execute `@Workflow`/`@Step` bodies. Defaults to `worker !== false`
|
|
470
|
+
* (back-compat: `worker:true` drives, and a plain `worker:false` API/dashboard pod does not).
|
|
471
|
+
* `DurableControlPlaneModule` sets this `true` alongside `worker:false`: an OPERATOR control
|
|
472
|
+
* plane that drives every run but dispatches each one to a remote tenant worker group (see
|
|
473
|
+
* {@link remoteByConvention}) instead of running it in-process. Most apps never set this
|
|
474
|
+
* directly — use `DurableModule` (a worker) or `DurableControlPlaneModule` (a driving,
|
|
475
|
+
* non-executing control plane) instead.
|
|
476
|
+
*/
|
|
477
|
+
drive?: boolean;
|
|
478
|
+
/** Max ms to wait for in-flight runs on shutdown before exiting. Defaults to 10000. */
|
|
479
|
+
shutdownTimeoutMs?: number;
|
|
480
|
+
/**
|
|
481
|
+
* Recurring workflows to start on a schedule (fixed interval or cron). The timer poller fires
|
|
482
|
+
* them each tick on **worker** instances only; `engine.start` is idempotent by the schedule's
|
|
483
|
+
* time-bucket run id, so racing instances start each window exactly once. Cron schedules need the
|
|
484
|
+
* optional `cron-parser` peer dependency.
|
|
485
|
+
*/
|
|
486
|
+
schedules?: ScheduledWorkflow[];
|
|
487
|
+
/**
|
|
488
|
+
* Hard-prune terminal run history on an interval so `durable_workflow_runs` (and its child tables)
|
|
489
|
+
* stays bounded — without it, completed/failed/cancelled runs accumulate forever and the timer
|
|
490
|
+
* poller's per-tick status scans get linearly slower. Worker instances only. Omit to keep all
|
|
491
|
+
* history (the default). Requires a store adapter that implements `pruneTerminalRuns` (the
|
|
492
|
+
* MikroORM adapter does); other adapters no-op with a warning. See {@link DurableRetentionOptions}.
|
|
493
|
+
*/
|
|
494
|
+
retention?: DurableRetentionOptions;
|
|
495
|
+
/**
|
|
496
|
+
* Build the public callback URL for a `ctx.webhook()` token, e.g.
|
|
497
|
+
* ``(t) => `https://api.example.com/durable/api/webhooks/${t}` ``. Populates `DurableWebhook.url`
|
|
498
|
+
* so a step can hand the URL to a third party. The dashboard's `POST webhooks/:token` receives the
|
|
499
|
+
* callback. Omit to build URLs yourself from the token.
|
|
500
|
+
*/
|
|
501
|
+
webhookUrl?: (token: string) => string;
|
|
502
|
+
/**
|
|
503
|
+
* Flow-control queues for remote steps, registered on the engine at startup. Reference one from a
|
|
504
|
+
* workflow with `ctx.call(step, input, { queue: name })` to cap its concurrency / admission rate.
|
|
505
|
+
*/
|
|
506
|
+
queues?: QueueConfig[];
|
|
507
|
+
/**
|
|
508
|
+
* Admission backend for the flow-control `queues`. Defaults to in-process (per-instance) caps. Pass
|
|
509
|
+
* a `RedisAdmissionBackend` (from `@dudousxd/nestjs-durable-admission-redis`) to make concurrency /
|
|
510
|
+
* rate-limit / priority ordering GLOBAL across every engine replica.
|
|
511
|
+
*/
|
|
512
|
+
admission?: AdmissionBackend;
|
|
513
|
+
/**
|
|
514
|
+
* Provide the current W3C `traceparent` to stamp on dispatched remote tasks, so workers continue
|
|
515
|
+
* the distributed trace. Pass `otelTraceparent` from `@dudousxd/nestjs-durable-otel`.
|
|
516
|
+
*/
|
|
517
|
+
traceparent?: () => string | undefined;
|
|
518
|
+
/**
|
|
519
|
+
* Provide an opaque context carrier (tenant / user / correlation ids) to stamp on dispatched remote
|
|
520
|
+
* tasks, so workers re-expose it to the step handler alongside the `traceparent`. The engine never
|
|
521
|
+
* inspects its shape.
|
|
522
|
+
*
|
|
523
|
+
* **Auto-feed**: if you omit this AND `@dudousxd/nestjs-context` is installed (its accessor is bound
|
|
524
|
+
* to the shared `CONTEXT_ACCESSOR` token), DurableModule defaults this to a reader that builds
|
|
525
|
+
* `{ traceId, tenantId, userRef }` from the accessor — so a workflow dispatched within a request
|
|
526
|
+
* automatically carries the originating context across process boundaries. Pass your own reader to
|
|
527
|
+
* override the auto-feed; with neither, the carrier is omitted (unchanged behavior).
|
|
528
|
+
*
|
|
529
|
+
* Re-evaluated at each (re)dispatch — including a retry or a crash/scale-down resume that the engine
|
|
530
|
+
* drives OUTSIDE the originating request scope, where this reader may return empty or stale values.
|
|
531
|
+
* Treat the carrier as best-effort correlation/propagation metadata only — do NOT treat it as an
|
|
532
|
+
* authorization boundary.
|
|
533
|
+
*/
|
|
534
|
+
context?: () => Record<string, unknown> | undefined;
|
|
535
|
+
/** Attempts for each saga compensation when a run fails. Default 1 (no retry). Idempotent undos. */
|
|
536
|
+
compensationRetries?: number;
|
|
537
|
+
/**
|
|
538
|
+
* Route an **unregistered** workflow whose name matches a live worker group to that group over the
|
|
539
|
+
* primary transport — no per-workflow `engine.remote()` registration needed. Default `false`. Set
|
|
540
|
+
* `true` on a control plane that dispatches to convention-named polyglot workers (see
|
|
541
|
+
* `WorkflowEngineDeps.remoteByConvention`).
|
|
542
|
+
*/
|
|
543
|
+
remoteByConvention?: boolean;
|
|
544
|
+
/**
|
|
545
|
+
* Opt into tenant read scoping: when `true` AND {@link namespace} is set, the module confines the
|
|
546
|
+
* store's reads to that namespace (a tenant-boundary view) instead of the operator view that sees
|
|
547
|
+
* all namespaces. Default `false` — the control plane (e.g. flip's `/ctrl` operator screens) stays
|
|
548
|
+
* unscoped. Requires a store that exposes the `withScope` capability (the MikroORM adapter does);
|
|
549
|
+
* a pre-built store without it is used as-is (construct it already-scoped instead). See
|
|
550
|
+
* {@link DurableModuleOptions.store}.
|
|
551
|
+
*/
|
|
552
|
+
scopeReads?: boolean;
|
|
553
|
+
/**
|
|
554
|
+
* Opt into an **in-app worker** (uniform dispatch): the same process runs the engine AND serves its
|
|
555
|
+
* own discovered `@Workflow`/`@Step` on one default `group`. Each `@Workflow` is registered
|
|
556
|
+
* GROUP-SERVED — its turns are dispatched to `group` over the transport and replayed by a co-located
|
|
557
|
+
* worker consumer — instead of run inline. Requires a workflow-task transport (BullMQ). Omit (the
|
|
558
|
+
* default) to keep every `@Workflow` on the inline fast path with zero dispatch round-trips. See
|
|
559
|
+
* {@link DurableInAppWorkerOptions}.
|
|
560
|
+
*/
|
|
561
|
+
inAppWorker?: DurableInAppWorkerOptions;
|
|
562
|
+
}
|
|
563
|
+
interface DurableModuleAsyncOptions {
|
|
564
|
+
useFactory: (...args: never[]) => DurableModuleOptions | Promise<DurableModuleOptions>;
|
|
565
|
+
inject?: InjectionToken[];
|
|
566
|
+
}
|
|
567
|
+
declare class DurableModule {
|
|
568
|
+
static forRoot(options: DurableModuleOptions): DynamicModule;
|
|
569
|
+
static forRootAsync(options: DurableModuleAsyncOptions): DynamicModule;
|
|
570
|
+
private static build;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* The **control-plane** packaging of {@link DurableModule}: an intention-revealing alias that always
|
|
574
|
+
* forces `worker: false, drive: true`. A control-plane instance mounts the engine, the dashboard, and
|
|
575
|
+
* the store, and DRIVES every run — polls pending, recovers crashed, resumes due timers, sweeps
|
|
576
|
+
* timeouts — but never itself executes a `@Workflow`/`@Step` body: pair it with
|
|
577
|
+
* `remoteByConvention: true` (or per-workflow `engine.remote()`) so each driven run dispatches to a
|
|
578
|
+
* remote tenant worker group instead of running in-process. A started run no longer sits orphaned
|
|
579
|
+
* `pending` forever waiting for a worker's own poll — this instance drives it itself.
|
|
580
|
+
*
|
|
581
|
+
* Pair it with tenant workers packaged via `DurableWorkerModule` (which run `@Step`/`@Workflow`
|
|
582
|
+
* handlers over the transport, hold no engine/store, and can `startRun` back over the protocol). Use
|
|
583
|
+
* `DurableModule` directly when one instance should be BOTH dispatcher and worker.
|
|
584
|
+
*
|
|
585
|
+
* Any `worker`/`drive` passed in options is ignored — this factory hard-sets `worker: false, drive: true`.
|
|
586
|
+
*/
|
|
587
|
+
declare class DurableControlPlaneModule {
|
|
588
|
+
static forRoot(options: DurableModuleOptions): DynamicModule;
|
|
589
|
+
static forRootAsync(options: DurableModuleAsyncOptions): DynamicModule;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
declare const ENTITY_METADATA: unique symbol;
|
|
593
|
+
declare const ENTITY_ON_METADATA: unique symbol;
|
|
594
|
+
/**
|
|
595
|
+
* Marks an `@Injectable()` class as a **durable entity** (a virtual object): its `@On(op)` methods run
|
|
596
|
+
* serialized per key over the instance's fields as durable state. e.g.
|
|
597
|
+
*
|
|
598
|
+
* ```ts
|
|
599
|
+
* @Entity({ name: 'cart' }) @Injectable()
|
|
600
|
+
* class Cart { items: Item[] = []; @On('add') add(i: Item) { this.items.push(i); } @On('list') list() { return this.items; } }
|
|
601
|
+
* ```
|
|
602
|
+
*
|
|
603
|
+
* The class must be **constructible with no arguments** (a fresh instance is the initial state per key)
|
|
604
|
+
* — keep entities pure state, no DI. Drive them with `EntityService` or `ctx.signalEntity`/`callEntity`.
|
|
605
|
+
*/
|
|
606
|
+
declare function Entity(options: {
|
|
607
|
+
name: string;
|
|
608
|
+
}): ClassDecorator;
|
|
609
|
+
/** Marks an entity method as the handler for operation `op`. */
|
|
610
|
+
declare function On(op: string): MethodDecorator;
|
|
611
|
+
declare function getEntityMeta(target: Function): {
|
|
612
|
+
name: string;
|
|
613
|
+
} | undefined;
|
|
614
|
+
/**
|
|
615
|
+
* Build the engine `EntityConfig` for a discovered `@Entity` class: a fresh instance per key, and
|
|
616
|
+
* handlers that rehydrate the class prototype onto the (serialized) state before dispatching the op,
|
|
617
|
+
* so methods work after replay.
|
|
618
|
+
*/
|
|
619
|
+
declare function entityConfigFor(ctor: Function): {
|
|
620
|
+
initialState: () => object;
|
|
621
|
+
handlers: Record<string, EntityHandler>;
|
|
622
|
+
};
|
|
623
|
+
/** Inject this to drive durable entities from outside a workflow. */
|
|
624
|
+
declare class EntityService {
|
|
625
|
+
private readonly engine;
|
|
626
|
+
constructor(engine: WorkflowEngine);
|
|
627
|
+
/** Send an operation to an entity (fire-and-forget; ordered + exactly-once per key). */
|
|
628
|
+
signal(name: string, key: string, op: string, arg?: unknown): Promise<void>;
|
|
629
|
+
/** Read an entity's current durable state (or undefined if it has none yet). */
|
|
630
|
+
getState<S = unknown>(name: string, key: string): Promise<S | undefined>;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
declare const STEP_INTERCEPTOR_METADATA: unique symbol;
|
|
634
|
+
/**
|
|
635
|
+
* The shape a `@StepInterceptor()` provider must implement: `intercept(invocation, next)` wraps the
|
|
636
|
+
* real execution of every local `ctx.step` (call `next()` to run the step body / next interceptor,
|
|
637
|
+
* and return — or transform — its result). The engine-level {@link StepInterceptor} primitive, with
|
|
638
|
+
* NestJS dependency injection.
|
|
639
|
+
*/
|
|
640
|
+
interface DurableStepInterceptor {
|
|
641
|
+
intercept(invocation: StepInvocation, next: () => Promise<unknown>): Promise<unknown>;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Marks an `@Injectable()` class as a durable step interceptor. The module discovers it on boot and
|
|
645
|
+
* registers its `intercept` method with the engine (so it can inject loggers/tracers/etc.). First
|
|
646
|
+
* declared is outermost. Interceptors fire only when a step actually executes, never on replay.
|
|
647
|
+
*/
|
|
648
|
+
declare function StepInterceptor(): ClassDecorator;
|
|
649
|
+
declare function isStepInterceptor(target: Function): boolean;
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Cross-lib injection token for the current-request context accessor, owned by
|
|
653
|
+
* `@dudousxd/nestjs-context`. We do NOT import nestjs-context (it is an OPTIONAL
|
|
654
|
+
* peer dependency) — instead we share its well-known token by value so DI
|
|
655
|
+
* resolves the same provider when nestjs-context is installed and present.
|
|
656
|
+
*
|
|
657
|
+
* `Symbol.for(key)` uses the global symbol registry, so this resolves to the
|
|
658
|
+
* SAME symbol instance as nestjs-context's `tokens.ts` (and the identical token
|
|
659
|
+
* declared by `@dudousxd/nestjs-authz`) without any import. The key MUST stay
|
|
660
|
+
* byte-identical with nestjs-context's export.
|
|
661
|
+
*/
|
|
662
|
+
declare const CONTEXT_ACCESSOR: unique symbol;
|
|
663
|
+
|
|
664
|
+
/** Public entry point for starting and resuming workflow runs. */
|
|
665
|
+
declare class WorkflowService {
|
|
666
|
+
private readonly engine;
|
|
667
|
+
constructor(engine: WorkflowEngine);
|
|
668
|
+
/**
|
|
669
|
+
* Enqueue a workflow run — it creates the run (`pending`) and returns `{ runId, status: 'pending' }`
|
|
670
|
+
* immediately; a worker executes the body (so the caller never blocks on workflow logic). Use
|
|
671
|
+
* {@link waitForRun} when you need the outcome. Pass the workflow's **class**
|
|
672
|
+
* (`start(CheckoutWorkflow, input)`) for a typed input + refactor-safety, or a **name** string for a
|
|
673
|
+
* cross-runtime workflow. `runId` defaults to a random id; pass your own to make the start idempotent
|
|
674
|
+
* (a redelivery returns the existing run). `opts.tags` are merged with the workflow's static
|
|
675
|
+
* `@Workflow({ tags })`; `opts.searchAttributes` stamp typed, queryable run data.
|
|
676
|
+
*/
|
|
677
|
+
start<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, runId?: string, opts?: StartOptions): Promise<RunResult>;
|
|
678
|
+
start(workflow: string, input: unknown, runId?: string, opts?: StartOptions): Promise<RunResult>;
|
|
679
|
+
resume(runId: string): Promise<RunResult>;
|
|
680
|
+
/**
|
|
681
|
+
* Resolve once a run settles — terminal (completed/failed/cancelled/dead) or suspended. `start`
|
|
682
|
+
* only enqueues (a worker runs the body), so pair them when a request needs the outcome:
|
|
683
|
+
* `const { runId } = await svc.start(...); const result = await svc.waitForRun(runId)`.
|
|
684
|
+
*/
|
|
685
|
+
waitForRun(runId: string, opts?: {
|
|
686
|
+
timeoutMs?: number;
|
|
687
|
+
}): Promise<RunResult>;
|
|
688
|
+
/** Deliver an external signal (e.g. from a webhook) to the run waiting on `token`. */
|
|
689
|
+
signal(token: string, payload: unknown): Promise<RunResult | null>;
|
|
690
|
+
/**
|
|
691
|
+
* Ensure a run exists for `runId`, then deliver a signal to it — race-free (the signal is buffered
|
|
692
|
+
* until the run reaches its `waitForSignal`). The durable-entity / accumulator pattern: one
|
|
693
|
+
* long-lived run per key fed events by many calls. See {@link WorkflowEngine.signalWithStart}.
|
|
694
|
+
*/
|
|
695
|
+
signalWithStart<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, runId: string, signal: {
|
|
696
|
+
token: string;
|
|
697
|
+
payload?: unknown;
|
|
698
|
+
}, opts?: StartOptions): Promise<{
|
|
699
|
+
runId: string;
|
|
700
|
+
}>;
|
|
701
|
+
signalWithStart(workflow: string, input: unknown, runId: string, signal: {
|
|
702
|
+
token: string;
|
|
703
|
+
payload?: unknown;
|
|
704
|
+
}, opts?: StartOptions): Promise<{
|
|
705
|
+
runId: string;
|
|
706
|
+
}>;
|
|
707
|
+
/**
|
|
708
|
+
* Publish a named event. Resumes runs waiting on it via `ctx.waitForEvent(name, { match })` and
|
|
709
|
+
* starts a fresh run of every workflow subscribed via `@Workflow({ onEvent })` / `@OnEvent` (the
|
|
710
|
+
* payload becomes its input). Pass `opts.id` to dedupe redeliveries. Returns how many runs it
|
|
711
|
+
* touched (resumed + started).
|
|
712
|
+
*/
|
|
713
|
+
publishEvent(name: string, payload: unknown, opts?: {
|
|
714
|
+
id?: string;
|
|
715
|
+
}): Promise<number>;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, DEAD_LETTER_METADATA, DURABLE_STEP_METADATA, DURABLE_WORKER_OPTIONS, DURABLE_WORKER_RUNNERS, DeadLetter, DurableControlPlaneModule, type DurableInAppWorkerOptions, DurableModule, type DurableModuleAsyncOptions, type DurableModuleOptions, type DurableRetentionOptions, DurableStep, type DurableStepInterceptor, type DurableStepMeta, DurableWorkerModule, type DurableWorkerModuleAsyncOptions, type DurableWorkerModuleOptions, ENTITY_METADATA, ENTITY_ON_METADATA, Entity, EntityService, IN_APP_RUN_REDIS_WORKER, IN_APP_WORKER_BINDING, IN_APP_WORKER_OPTIONS, IN_APP_WORKER_RUNNERS, IN_APP_WORKER_RUNTIME, type InAppWorkerBinding, InAppWorkerBootstrap, ON_EVENT_METADATA, On, OnEvent, RUN_REDIS_WORKER, type RunRedisWorkerFn, STEP_INTERCEPTOR_METADATA, Step, StepInterceptor, ThinStepRegistrar, ThinWorkerBootstrap, ThinWorkflowRegistrar, type UserRef, WORKFLOW_METADATA, Workflow, type WorkflowMeta, type WorkflowOptions, WorkflowService, entityConfigFor, getDurableStepMeta, getEntityMeta, getOnEvents, getWorkflowMeta, inAppWorkerProviders, isDeadLetterHandler, isStepInterceptor };
|