@dudousxd/nestjs-durable-core 0.30.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +218 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +233 -114
- package/dist/index.d.ts +233 -114
- package/dist/index.js +214 -143
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,166 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Flow control for remote steps. A queue caps how much work `ctx.call(step, input, { queue })`
|
|
5
|
+
* admits at once — a concurrency limit and/or a fixed-window rate limit. When a call can't be
|
|
6
|
+
* admitted it doesn't dispatch: the run re-suspends with a near-future wake time, and the timer
|
|
7
|
+
* poller re-tries admission later — so flow control is durable (survives crashes) without holding
|
|
8
|
+
* the run in memory.
|
|
9
|
+
*
|
|
10
|
+
* Accounting is per engine instance (the DBOS `workerConcurrency` tier): correct for the common
|
|
11
|
+
* single-orchestrator deployment. Global, cross-instance limits would need a durable counter in the
|
|
12
|
+
* store — a deliberate follow-up, not built here.
|
|
13
|
+
*
|
|
14
|
+
* Beyond raw caps, the controller can admit contended slots by PRIORITY and by per-KEY FAIRNESS
|
|
15
|
+
* (round-robin), so a single noisy key/tenant can't monopolize the concurrency budget. This is the
|
|
16
|
+
* DISPATCH/admission layer — it never touches workflow replay (engine positional logic). Default
|
|
17
|
+
* behaviour is unchanged FIFO when neither priority nor fairness is configured/passed.
|
|
18
|
+
*
|
|
19
|
+
* Design (vs Temporal/Inngest/DBOS): Temporal task queues are FIFO with separate priority levels;
|
|
20
|
+
* Inngest concurrency keys + throttle give per-key fairness; DBOS queues are FIFO with a
|
|
21
|
+
* `workerConcurrency` cap. We fold both knobs into the same in-process admission gate: an optional
|
|
22
|
+
* per-call `priority` (higher first) and an optional queue-level `fairness: 'key'` (round-robin by
|
|
23
|
+
* the per-call `key`). Because blocked calls re-suspend durably and retry independently (there is no
|
|
24
|
+
* resident FIFO list to reorder), the controller remembers the set of WAITERS that asked-and-were-
|
|
25
|
+
* blocked, and on a later retry only admits the one that is rightful-next under the policy.
|
|
26
|
+
*/
|
|
27
|
+
interface QueueConfig {
|
|
28
|
+
/** Queue name, referenced by `ctx.call(step, input, { queue: name })`. */
|
|
29
|
+
name: string;
|
|
30
|
+
/** Max steps in flight at once for this queue (this instance). Omit for unlimited. */
|
|
31
|
+
concurrency?: number;
|
|
32
|
+
/** Fixed-window rate limit: at most `limit` admissions per `periodMs`. Omit for unlimited. */
|
|
33
|
+
rateLimit?: {
|
|
34
|
+
limit: number;
|
|
35
|
+
periodMs: number;
|
|
36
|
+
};
|
|
37
|
+
/** Delay (ms) before a concurrency-blocked call re-checks for a free slot. Default 1000. */
|
|
38
|
+
retryMs?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Fairness policy for a contended concurrency slot. `'key'` round-robins across distinct per-call
|
|
41
|
+
* keys (the least-recently-served key wins the next slot), so one key can't monopolize the budget.
|
|
42
|
+
* Omit (or `'fifo'`) for plain first-come ordering. Priority always wins over fairness; fairness
|
|
43
|
+
* only breaks ties within the same priority.
|
|
44
|
+
*/
|
|
45
|
+
fairness?: 'fifo' | 'key';
|
|
46
|
+
/**
|
|
47
|
+
* Arrival-order tiebreak among otherwise-equal waiters (same priority, same fairness rank).
|
|
48
|
+
* `'fifo'` (default) admits the earliest arrival; `'lifo'` admits the most recent (a stack). Orthogonal
|
|
49
|
+
* to `priority` and `fairness` — those still win first; this only breaks their ties.
|
|
50
|
+
*/
|
|
51
|
+
order?: 'fifo' | 'lifo';
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Per-call admission hints. All optional — omitting them keeps the legacy FIFO behaviour.
|
|
55
|
+
* - `priority`: higher is admitted first (default 0).
|
|
56
|
+
* - `key`: the fairness bucket (e.g. a tenant id) for `fairness: 'key'`.
|
|
57
|
+
* - `waiterId`: a stable id for THIS call across its retries (the engine passes the stepId), so the
|
|
58
|
+
* controller can track it as one waiter instead of counting each retry as a new contender.
|
|
59
|
+
*/
|
|
60
|
+
interface AdmissionItem {
|
|
61
|
+
priority?: number | undefined;
|
|
62
|
+
key?: string | undefined;
|
|
63
|
+
waiterId?: string | undefined;
|
|
64
|
+
}
|
|
65
|
+
/** Admission decision: either admitted, or blocked until `retryAt` (epoch ms). */
|
|
66
|
+
type Admission = {
|
|
67
|
+
ok: true;
|
|
68
|
+
} | {
|
|
69
|
+
ok: false;
|
|
70
|
+
retryAt: number;
|
|
71
|
+
};
|
|
72
|
+
/** In-process admission controller for one {@link QueueConfig}. */
|
|
73
|
+
declare class QueueController {
|
|
74
|
+
readonly config: QueueConfig;
|
|
75
|
+
private readonly clock;
|
|
76
|
+
private inFlight;
|
|
77
|
+
private windowStart;
|
|
78
|
+
private windowCount;
|
|
79
|
+
/** Registered waiters by id (only used when priority/fairness ordering is in play). */
|
|
80
|
+
private readonly waiters;
|
|
81
|
+
/** Monotonic counter stamping each newly-seen waiter for a stable FIFO tiebreak. */
|
|
82
|
+
private waiterSeq;
|
|
83
|
+
/** Per-key "last served" order for round-robin fairness — higher = more recently served. */
|
|
84
|
+
private readonly keyServed;
|
|
85
|
+
private servedTick;
|
|
86
|
+
constructor(config: QueueConfig, clock: () => number);
|
|
87
|
+
/** Whether this controller does any priority/fairness ordering (else it's a plain FIFO gate). */
|
|
88
|
+
private get ordered();
|
|
89
|
+
/**
|
|
90
|
+
* Try to admit one unit of work. On `ok`, the caller has taken a concurrency slot and must call
|
|
91
|
+
* {@link release} when the step settles. On block, `retryAt` is when admission may next succeed.
|
|
92
|
+
*
|
|
93
|
+
* `item` carries optional priority/fairness hints; omitting it is the legacy FIFO path. When
|
|
94
|
+
* ordering is configured (or a priority is given), a blocked call REGISTERS as a waiter and is only
|
|
95
|
+
* admitted on a later retry once it is the rightful next under (priority desc, fairness, FIFO).
|
|
96
|
+
*/
|
|
97
|
+
tryAdmit(item?: AdmissionItem): Admission;
|
|
98
|
+
/** Release a concurrency slot taken by a successful {@link tryAdmit}. */
|
|
99
|
+
release(): void;
|
|
100
|
+
/** Track (or refresh) a blocked call as a waiter; its FIFO seq is stamped only on first sight. */
|
|
101
|
+
private register;
|
|
102
|
+
/**
|
|
103
|
+
* Is the calling item the best candidate among all registered waiters? Ordering, best first:
|
|
104
|
+
* 1. higher `priority`;
|
|
105
|
+
* 2. fairness: with `fairness: 'key'`, the least-recently-served key wins (round-robin);
|
|
106
|
+
* 3. earliest registration (FIFO) — stable tiebreak.
|
|
107
|
+
* A bare item (no waiterId) is compared on its hints directly.
|
|
108
|
+
*/
|
|
109
|
+
private isNext;
|
|
110
|
+
/** Strict "a should be admitted before b" comparison under the active policy. */
|
|
111
|
+
private better;
|
|
112
|
+
/** Last-served tick for a key (0 = never served, so an unseen key is admitted before a busy one). */
|
|
113
|
+
private servedOf;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Pluggable backend for the remote-step flow-control gate (`ctx.call(step, input, { queue })`). The
|
|
118
|
+
* engine asks it whether a queued call may be admitted now (concurrency / rate / ordering) and tells
|
|
119
|
+
* it when an admitted slot is released.
|
|
120
|
+
*
|
|
121
|
+
* The default {@link InMemoryAdmissionBackend} keeps per-instance counts — correct for a single
|
|
122
|
+
* orchestrator. Swap in a store/Redis-backed backend to make the caps and ordering GLOBAL across
|
|
123
|
+
* engine replicas (so `concurrency: 5` means 5 in-flight across the whole fleet, not 5 per pod).
|
|
124
|
+
*
|
|
125
|
+
* `tryAdmit`/`release` are async so a global backend can do its atomic round-trip; the in-memory one
|
|
126
|
+
* resolves synchronously. An UNREGISTERED queue name is treated as ungated (admits immediately) — the
|
|
127
|
+
* engine only tracks/releases a slot for a registered queue.
|
|
128
|
+
*/
|
|
129
|
+
interface AdmissionBackend {
|
|
130
|
+
/** Register (or replace) a queue's config. Called from `engine.registerQueue`. */
|
|
131
|
+
register(config: QueueConfig): void;
|
|
132
|
+
/** Whether `queue` is registered (gated). The engine only tracks/releases a slot for a gated queue. */
|
|
133
|
+
handles(queue: string): boolean;
|
|
134
|
+
/** May a call on `queue` take a slot now? On `ok`, the caller holds a slot until {@link release}. */
|
|
135
|
+
tryAdmit(queue: string, item: AdmissionItem): Promise<Admission>;
|
|
136
|
+
/**
|
|
137
|
+
* Release the slot granted by {@link tryAdmit} for `queue`. `slotId` is the admitted item's
|
|
138
|
+
* `waiterId` (the engine's stepId), so a lease-based backend can drop the exact slot; an in-process
|
|
139
|
+
* counter ignores it.
|
|
140
|
+
*/
|
|
141
|
+
release(queue: string, slotId: string): Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Optional: subscribe to "a slot just freed on `queue`" events (e.g. a fleet-wide pub/sub). The
|
|
144
|
+
* engine uses it to wake its admission-blocked runs early instead of waiting for their retry tick.
|
|
145
|
+
* A backend without it falls back to retry-tick polling. Fired best-effort across the fleet.
|
|
146
|
+
*/
|
|
147
|
+
onFreed?(handler: (queue: string) => void): void;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Default admission backend: one in-process {@link QueueController} per registered queue. Caps and
|
|
151
|
+
* waiter ordering are per engine instance (see {@link QueueConfig}). Preserves the exact pre-backend
|
|
152
|
+
* behaviour — the engine used to hold these controllers directly.
|
|
153
|
+
*/
|
|
154
|
+
declare class InMemoryAdmissionBackend implements AdmissionBackend {
|
|
155
|
+
private readonly clock;
|
|
156
|
+
private readonly controllers;
|
|
157
|
+
constructor(clock: () => number);
|
|
158
|
+
register(config: QueueConfig): void;
|
|
159
|
+
handles(queue: string): boolean;
|
|
160
|
+
tryAdmit(queue: string, item: AdmissionItem): Promise<Admission>;
|
|
161
|
+
release(queue: string, _slotId: string): Promise<void>;
|
|
162
|
+
}
|
|
163
|
+
|
|
3
164
|
/**
|
|
4
165
|
* Parse a human duration to milliseconds. Accepts a number (already ms) or a string like
|
|
5
166
|
* `'500ms'`, `'30s'`, `'15m'`, `'2h'`, `'7d'`, `'7 days'`.
|
|
@@ -77,6 +238,13 @@ interface WorkflowRun {
|
|
|
77
238
|
tags?: string[] | undefined;
|
|
78
239
|
/** Typed, queryable run data (e.g. `{ amount: 200, tier: 'pro' }`) — see {@link RunQuery.attributes}. */
|
|
79
240
|
searchAttributes?: SearchAttributes | undefined;
|
|
241
|
+
/**
|
|
242
|
+
* Dispatch priority for a REMOTE run (one advanced by a {@link WorkflowExecutor} over a broker):
|
|
243
|
+
* carried onto each {@link WorkflowTask} so an urgent child workflow can jump ahead of enqueued
|
|
244
|
+
* lower-priority ones at the worker. Higher wins; absent = unprioritised. Best-effort ordering, not
|
|
245
|
+
* durable state required for correctness — a transport without priority support ignores it.
|
|
246
|
+
*/
|
|
247
|
+
priority?: number | undefined;
|
|
80
248
|
createdAt: Date;
|
|
81
249
|
updatedAt: Date;
|
|
82
250
|
}
|
|
@@ -378,6 +546,13 @@ interface RemoteTask {
|
|
|
378
546
|
* so failover is symmetric without the worker choosing a transport. Absent for a single transport.
|
|
379
547
|
*/
|
|
380
548
|
transport?: string | undefined;
|
|
549
|
+
/**
|
|
550
|
+
* Admission priority carried through to the broker (BullMQ job `priority`) so a transport that
|
|
551
|
+
* supports priority ordering lets an urgent task jump ahead of already-enqueued lower-priority
|
|
552
|
+
* ones at the worker. Mirrors the per-call `priority` from `ctx.call(..., { priority })`. Higher
|
|
553
|
+
* wins; absent means default/unprioritised. Transports without priority support ignore it.
|
|
554
|
+
*/
|
|
555
|
+
priority?: number | undefined;
|
|
381
556
|
attempt: number;
|
|
382
557
|
}
|
|
383
558
|
interface StepResult {
|
|
@@ -419,6 +594,12 @@ interface WorkflowTask {
|
|
|
419
594
|
/** Id of the transport this task was dispatched on (pool failover) — see {@link NamedTransport}. */
|
|
420
595
|
transport?: string;
|
|
421
596
|
traceparent?: string;
|
|
597
|
+
/**
|
|
598
|
+
* Admission priority carried to the broker (BullMQ job `priority`) so an urgent child workflow can
|
|
599
|
+
* jump ahead of already-enqueued lower-priority ones at the worker. Higher wins; absent means
|
|
600
|
+
* default/unprioritised. Transports without priority support ignore it.
|
|
601
|
+
*/
|
|
602
|
+
priority?: number | undefined;
|
|
422
603
|
attempt: number;
|
|
423
604
|
}
|
|
424
605
|
/** One resolved durable op in a run's history — a superset of a completed {@link StepCheckpoint}. */
|
|
@@ -624,6 +805,21 @@ type ControlMessage = {
|
|
|
624
805
|
runId: string;
|
|
625
806
|
});
|
|
626
807
|
type BackoffStrategy = 'fixed' | 'exp';
|
|
808
|
+
/**
|
|
809
|
+
* Options for `ctx.child` / `ctx.startChild`. A bare string passed instead is shorthand for
|
|
810
|
+
* `{ childId }`, so the existing `ctx.child(ref, input, 'my-id')` form keeps working.
|
|
811
|
+
*/
|
|
812
|
+
interface ChildCallOptions {
|
|
813
|
+
/** Deterministic child run id; defaults to one derived from the parent run id + call position. */
|
|
814
|
+
childId?: string | undefined;
|
|
815
|
+
/**
|
|
816
|
+
* Dispatch priority for a REMOTE child workflow — stamped on the child run and carried onto every
|
|
817
|
+
* {@link WorkflowTask} dispatched to advance it, so an urgent child can jump ahead of enqueued
|
|
818
|
+
* lower-priority ones at the worker. Higher wins; absent = unprioritised. Ignored for an in-process
|
|
819
|
+
* (TS class) child, which runs in the engine and never hits a broker queue.
|
|
820
|
+
*/
|
|
821
|
+
priority?: number | undefined;
|
|
822
|
+
}
|
|
627
823
|
interface StepOptions {
|
|
628
824
|
/** Max attempts before the step (and run) fails. */
|
|
629
825
|
retries?: number;
|
|
@@ -781,8 +977,8 @@ interface WorkflowCtx {
|
|
|
781
977
|
* Pass the child's **class** (`ctx.child(ShippingWorkflow, input)`) for a typed input + result; pass
|
|
782
978
|
* a **string** name for a cross-runtime child (e.g. a Python workflow) where there's no class.
|
|
783
979
|
*/
|
|
784
|
-
child<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>,
|
|
785
|
-
child<TOutput>(workflow: string, input: unknown,
|
|
980
|
+
child<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, options?: string | ChildCallOptions): Promise<WorkflowOutputOf<C>>;
|
|
981
|
+
child<TOutput>(workflow: string, input: unknown, options?: string | ChildCallOptions): Promise<TOutput>;
|
|
786
982
|
/**
|
|
787
983
|
* Start a child workflow **fire-and-forget**: dispatches it once (checkpointed, replay-safe) and
|
|
788
984
|
* returns its run id immediately — the parent keeps running instead of suspending. Use it to kick
|
|
@@ -790,8 +986,8 @@ interface WorkflowCtx {
|
|
|
790
986
|
* the ids, then later `await ctx.child(...)` each with the same id to join (the start is idempotent
|
|
791
987
|
* by id, so the child runs exactly once). Class or string ref, like {@link child}.
|
|
792
988
|
*/
|
|
793
|
-
startChild<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>,
|
|
794
|
-
startChild(workflow: string, input: unknown,
|
|
989
|
+
startChild<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, options?: string | ChildCallOptions): Promise<string>;
|
|
990
|
+
startChild(workflow: string, input: unknown, options?: string | ChildCallOptions): Promise<string>;
|
|
795
991
|
/**
|
|
796
992
|
* Pause the run at this point until a human resumes it from the dashboard (or
|
|
797
993
|
* `engine.continue(runId)`). Records a visible `pending` checkpoint so the breakpoint shows up
|
|
@@ -995,113 +1191,6 @@ declare class EventAccumulators {
|
|
|
995
1191
|
private batch;
|
|
996
1192
|
}
|
|
997
1193
|
|
|
998
|
-
/**
|
|
999
|
-
* Flow control for remote steps. A queue caps how much work `ctx.call(step, input, { queue })`
|
|
1000
|
-
* admits at once — a concurrency limit and/or a fixed-window rate limit. When a call can't be
|
|
1001
|
-
* admitted it doesn't dispatch: the run re-suspends with a near-future wake time, and the timer
|
|
1002
|
-
* poller re-tries admission later — so flow control is durable (survives crashes) without holding
|
|
1003
|
-
* the run in memory.
|
|
1004
|
-
*
|
|
1005
|
-
* Accounting is per engine instance (the DBOS `workerConcurrency` tier): correct for the common
|
|
1006
|
-
* single-orchestrator deployment. Global, cross-instance limits would need a durable counter in the
|
|
1007
|
-
* store — a deliberate follow-up, not built here.
|
|
1008
|
-
*
|
|
1009
|
-
* Beyond raw caps, the controller can admit contended slots by PRIORITY and by per-KEY FAIRNESS
|
|
1010
|
-
* (round-robin), so a single noisy key/tenant can't monopolize the concurrency budget. This is the
|
|
1011
|
-
* DISPATCH/admission layer — it never touches workflow replay (engine positional logic). Default
|
|
1012
|
-
* behaviour is unchanged FIFO when neither priority nor fairness is configured/passed.
|
|
1013
|
-
*
|
|
1014
|
-
* Design (vs Temporal/Inngest/DBOS): Temporal task queues are FIFO with separate priority levels;
|
|
1015
|
-
* Inngest concurrency keys + throttle give per-key fairness; DBOS queues are FIFO with a
|
|
1016
|
-
* `workerConcurrency` cap. We fold both knobs into the same in-process admission gate: an optional
|
|
1017
|
-
* per-call `priority` (higher first) and an optional queue-level `fairness: 'key'` (round-robin by
|
|
1018
|
-
* the per-call `key`). Because blocked calls re-suspend durably and retry independently (there is no
|
|
1019
|
-
* resident FIFO list to reorder), the controller remembers the set of WAITERS that asked-and-were-
|
|
1020
|
-
* blocked, and on a later retry only admits the one that is rightful-next under the policy.
|
|
1021
|
-
*/
|
|
1022
|
-
interface QueueConfig {
|
|
1023
|
-
/** Queue name, referenced by `ctx.call(step, input, { queue: name })`. */
|
|
1024
|
-
name: string;
|
|
1025
|
-
/** Max steps in flight at once for this queue (this instance). Omit for unlimited. */
|
|
1026
|
-
concurrency?: number;
|
|
1027
|
-
/** Fixed-window rate limit: at most `limit` admissions per `periodMs`. Omit for unlimited. */
|
|
1028
|
-
rateLimit?: {
|
|
1029
|
-
limit: number;
|
|
1030
|
-
periodMs: number;
|
|
1031
|
-
};
|
|
1032
|
-
/** Delay (ms) before a concurrency-blocked call re-checks for a free slot. Default 1000. */
|
|
1033
|
-
retryMs?: number;
|
|
1034
|
-
/**
|
|
1035
|
-
* Fairness policy for a contended concurrency slot. `'key'` round-robins across distinct per-call
|
|
1036
|
-
* keys (the least-recently-served key wins the next slot), so one key can't monopolize the budget.
|
|
1037
|
-
* Omit (or `'fifo'`) for plain first-come ordering. Priority always wins over fairness; fairness
|
|
1038
|
-
* only breaks ties within the same priority.
|
|
1039
|
-
*/
|
|
1040
|
-
fairness?: 'fifo' | 'key';
|
|
1041
|
-
}
|
|
1042
|
-
/**
|
|
1043
|
-
* Per-call admission hints. All optional — omitting them keeps the legacy FIFO behaviour.
|
|
1044
|
-
* - `priority`: higher is admitted first (default 0).
|
|
1045
|
-
* - `key`: the fairness bucket (e.g. a tenant id) for `fairness: 'key'`.
|
|
1046
|
-
* - `waiterId`: a stable id for THIS call across its retries (the engine passes the stepId), so the
|
|
1047
|
-
* controller can track it as one waiter instead of counting each retry as a new contender.
|
|
1048
|
-
*/
|
|
1049
|
-
interface AdmissionItem {
|
|
1050
|
-
priority?: number | undefined;
|
|
1051
|
-
key?: string | undefined;
|
|
1052
|
-
waiterId?: string | undefined;
|
|
1053
|
-
}
|
|
1054
|
-
/** Admission decision: either admitted, or blocked until `retryAt` (epoch ms). */
|
|
1055
|
-
type Admission = {
|
|
1056
|
-
ok: true;
|
|
1057
|
-
} | {
|
|
1058
|
-
ok: false;
|
|
1059
|
-
retryAt: number;
|
|
1060
|
-
};
|
|
1061
|
-
/** In-process admission controller for one {@link QueueConfig}. */
|
|
1062
|
-
declare class QueueController {
|
|
1063
|
-
readonly config: QueueConfig;
|
|
1064
|
-
private readonly clock;
|
|
1065
|
-
private inFlight;
|
|
1066
|
-
private windowStart;
|
|
1067
|
-
private windowCount;
|
|
1068
|
-
/** Registered waiters by id (only used when priority/fairness ordering is in play). */
|
|
1069
|
-
private readonly waiters;
|
|
1070
|
-
/** Monotonic counter stamping each newly-seen waiter for a stable FIFO tiebreak. */
|
|
1071
|
-
private waiterSeq;
|
|
1072
|
-
/** Per-key "last served" order for round-robin fairness — higher = more recently served. */
|
|
1073
|
-
private readonly keyServed;
|
|
1074
|
-
private servedTick;
|
|
1075
|
-
constructor(config: QueueConfig, clock: () => number);
|
|
1076
|
-
/** Whether this controller does any priority/fairness ordering (else it's a plain FIFO gate). */
|
|
1077
|
-
private get ordered();
|
|
1078
|
-
/**
|
|
1079
|
-
* Try to admit one unit of work. On `ok`, the caller has taken a concurrency slot and must call
|
|
1080
|
-
* {@link release} when the step settles. On block, `retryAt` is when admission may next succeed.
|
|
1081
|
-
*
|
|
1082
|
-
* `item` carries optional priority/fairness hints; omitting it is the legacy FIFO path. When
|
|
1083
|
-
* ordering is configured (or a priority is given), a blocked call REGISTERS as a waiter and is only
|
|
1084
|
-
* admitted on a later retry once it is the rightful next under (priority desc, fairness, FIFO).
|
|
1085
|
-
*/
|
|
1086
|
-
tryAdmit(item?: AdmissionItem): Admission;
|
|
1087
|
-
/** Release a concurrency slot taken by a successful {@link tryAdmit}. */
|
|
1088
|
-
release(): void;
|
|
1089
|
-
/** Track (or refresh) a blocked call as a waiter; its FIFO seq is stamped only on first sight. */
|
|
1090
|
-
private register;
|
|
1091
|
-
/**
|
|
1092
|
-
* Is the calling item the best candidate among all registered waiters? Ordering, best first:
|
|
1093
|
-
* 1. higher `priority`;
|
|
1094
|
-
* 2. fairness: with `fairness: 'key'`, the least-recently-served key wins (round-robin);
|
|
1095
|
-
* 3. earliest registration (FIFO) — stable tiebreak.
|
|
1096
|
-
* A bare item (no waiterId) is compared on its hints directly.
|
|
1097
|
-
*/
|
|
1098
|
-
private isNext;
|
|
1099
|
-
/** Strict "a should be admitted before b" comparison under the active policy. */
|
|
1100
|
-
private better;
|
|
1101
|
-
/** Last-served tick for a key (0 = never served, so an unseen key is admitted before a busy one). */
|
|
1102
|
-
private servedOf;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
1194
|
type WorkflowFn = (ctx: WorkflowCtx, input: unknown) => Promise<unknown>;
|
|
1106
1195
|
/** Options for {@link WorkflowEngine.start}. */
|
|
1107
1196
|
interface StartOptions {
|
|
@@ -1109,6 +1198,11 @@ interface StartOptions {
|
|
|
1109
1198
|
tags?: string[] | undefined;
|
|
1110
1199
|
/** Typed, queryable run data stamped on the run (e.g. `{ amount: 200, tier: 'pro' }`). */
|
|
1111
1200
|
searchAttributes?: SearchAttributes | undefined;
|
|
1201
|
+
/**
|
|
1202
|
+
* Dispatch priority for a remote run — stamped on the run and carried onto every {@link WorkflowTask}
|
|
1203
|
+
* the engine dispatches to advance it. Higher wins; absent = unprioritised. See {@link WorkflowRun.priority}.
|
|
1204
|
+
*/
|
|
1205
|
+
priority?: number | undefined;
|
|
1112
1206
|
}
|
|
1113
1207
|
/**
|
|
1114
1208
|
* Serialize runs of a workflow that share a key — a durable, FIFO mutex (e.g. one pipeline per base).
|
|
@@ -1147,6 +1241,12 @@ interface WorkflowEngineDeps {
|
|
|
1147
1241
|
controlPlane?: ControlPlane | undefined;
|
|
1148
1242
|
/** Epoch-ms clock; injectable for tests. Defaults to `Date.now`. */
|
|
1149
1243
|
clock?: (() => number) | undefined;
|
|
1244
|
+
/**
|
|
1245
|
+
* Flow-control admission backend for `ctx.call(step, input, { queue })`. Defaults to an in-process
|
|
1246
|
+
* {@link InMemoryAdmissionBackend} (per-instance caps). Inject a store/Redis-backed backend to make
|
|
1247
|
+
* concurrency / rate-limit / ordering GLOBAL across engine replicas.
|
|
1248
|
+
*/
|
|
1249
|
+
admission?: AdmissionBackend | undefined;
|
|
1150
1250
|
/** Unique id for this engine instance, used for recovery leases. Defaults to a random id. */
|
|
1151
1251
|
instanceId?: string | undefined;
|
|
1152
1252
|
/** Recovery lease duration in ms — how long this instance owns a run it picked up. Default 30s. */
|
|
@@ -1269,8 +1369,10 @@ declare class WorkflowEngine {
|
|
|
1269
1369
|
private readonly updateValidators;
|
|
1270
1370
|
/** Runs being cancelled WITH saga compensation — see `cancel({ compensate: true })`. */
|
|
1271
1371
|
private readonly cancelRequested;
|
|
1272
|
-
/** Flow-control
|
|
1273
|
-
private readonly
|
|
1372
|
+
/** Flow-control admission backend for remote steps (see {@link registerQueue}). */
|
|
1373
|
+
private readonly admission;
|
|
1374
|
+
/** Runs on THIS instance blocked on admission, by queue — woken early on a freed-slot signal. */
|
|
1375
|
+
private readonly queueWaiters;
|
|
1274
1376
|
/** Which queue a dispatched step took a slot from, by stepId — so the result can release it. */
|
|
1275
1377
|
private readonly stepQueue;
|
|
1276
1378
|
/** Executions currently in flight, so a graceful shutdown can wait for them to settle. */
|
|
@@ -1638,6 +1740,12 @@ declare class WorkflowEngine {
|
|
|
1638
1740
|
* A no-op if the checkpoint isn't `pending` (a duplicate or late delivery).
|
|
1639
1741
|
*/
|
|
1640
1742
|
private completeRemoteResult;
|
|
1743
|
+
/**
|
|
1744
|
+
* A slot freed on `queue` (a fleet-wide signal): resume this instance's runs blocked on it so they
|
|
1745
|
+
* re-contend now instead of at their retry tick. Snapshot-and-clear — a run still blocked after the
|
|
1746
|
+
* retry re-registers itself, and one that's gone (cancelled/admitted) is dropped. Best-effort.
|
|
1747
|
+
*/
|
|
1748
|
+
private wakeQueueWaiters;
|
|
1641
1749
|
/** Release the flow-control slot a dispatched step held (if any), by its stepId. */
|
|
1642
1750
|
private releaseQueueSlot;
|
|
1643
1751
|
/** In-memory await path for a remote step with a liveness `timeoutMs` (re-dispatch on timeout). */
|
|
@@ -2014,6 +2122,17 @@ declare function attributeOperand(filter: AttributeFilter): string | number;
|
|
|
2014
2122
|
declare const STATE_STORE: unique symbol;
|
|
2015
2123
|
declare const TRANSPORT: unique symbol;
|
|
2016
2124
|
declare const DURABLE_OPTIONS: unique symbol;
|
|
2125
|
+
/**
|
|
2126
|
+
* Canonical, cross-lib-discoverable aliases of the durable DI tokens, following the ecosystem
|
|
2127
|
+
* capability naming `@dudousxd/nestjs-durable:<name>` (identical to `capability('durable', <name>)`).
|
|
2128
|
+
* `DurableModule` binds these alongside the legacy tokens (dual-bind), so an external library can
|
|
2129
|
+
* resolve durable's store/transport/options by the canonical name without importing durable
|
|
2130
|
+
* internals. Bare `Symbol.for` (same cross-copy-stability rationale as the legacy tokens above)
|
|
2131
|
+
* keeps `core` free of any `@dudousxd/nestjs-diagnostics` runtime dependency.
|
|
2132
|
+
*/
|
|
2133
|
+
declare const STATE_STORE_CANONICAL: unique symbol;
|
|
2134
|
+
declare const TRANSPORT_CANONICAL: unique symbol;
|
|
2135
|
+
declare const DURABLE_OPTIONS_CANONICAL: unique symbol;
|
|
2017
2136
|
|
|
2018
2137
|
/**
|
|
2019
2138
|
* A non-durable, in-process `StateStore` for tests and local development.
|
|
@@ -2101,4 +2220,4 @@ declare class InMemoryTransport implements Transport, ControlPlane {
|
|
|
2101
2220
|
onControl(handler: (msg: ControlMessage) => void): void;
|
|
2102
2221
|
}
|
|
2103
2222
|
|
|
2104
|
-
export { type AccumulatorHost, type Admission, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, type DurableWebhook, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, EventAccumulators, type EventBatchConfig, FatalError, type GroupHealth, type Heartbeat, type HistoryEvent, InMemoryStateStore, InMemoryTransport, type MetricsCollector, type MetricsSnapshot, type NamedTransport, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, type RemoteStepConfig, type RemoteStepDef, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, type RunAttributeRow, type RunDispatcher, type RunQuery, type RunResult, type RunSchedulesOptions, type RunStatus, STATE_STORE, type ScheduledWorkflow, type SearchAttributes, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, type StartOptions, type StateStore, type StepCheckpoint, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepResult, type StoreTransaction, type SubProcessHandle, TRANSPORT, type Transport, type UpdateResult, type UpdateValidator, WORKFLOW_NAME_KEY, type WorkerHeartbeat, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRun, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, applyAttributeQuery, attributeColumnFor, attributeOperand, breakpointToken, collectMetrics, eventMatchOf, eventMatches, eventPrefix, eventToken, matchesAttributes, normalizeAttributeRows, parseDuration, prevCronFireMs, remoteStep, runSchedules, runStepHandler, scheduledRunId, sqlComparator, stepId, workflowName };
|
|
2223
|
+
export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, type DurableWebhook, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, EventAccumulators, type EventBatchConfig, FatalError, type GroupHealth, type Heartbeat, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type MetricsCollector, type MetricsSnapshot, type NamedTransport, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, type RemoteStepConfig, type RemoteStepDef, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, type RunAttributeRow, type RunDispatcher, type RunQuery, type RunResult, type RunSchedulesOptions, type RunStatus, STATE_STORE, STATE_STORE_CANONICAL, type ScheduledWorkflow, type SearchAttributes, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, type StartOptions, type StateStore, type StepCheckpoint, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepResult, type StoreTransaction, type SubProcessHandle, TRANSPORT, TRANSPORT_CANONICAL, type Transport, type UpdateResult, type UpdateValidator, WORKFLOW_NAME_KEY, type WorkerHeartbeat, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRun, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, applyAttributeQuery, attributeColumnFor, attributeOperand, breakpointToken, collectMetrics, eventMatchOf, eventMatches, eventPrefix, eventToken, matchesAttributes, normalizeAttributeRows, parseDuration, prevCronFireMs, remoteStep, runSchedules, runStepHandler, scheduledRunId, sqlComparator, stepId, workflowName };
|