@dudousxd/nestjs-durable-core 0.64.0 → 0.66.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 +518 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +678 -137
- package/dist/index.d.ts +678 -137
- package/dist/index.js +509 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -168,6 +168,34 @@ declare class InMemoryAdmissionBackend implements AdmissionBackend {
|
|
|
168
168
|
* wire contract — the adonis + python SDKs build the byte-identical shape (design §7.8), and the
|
|
169
169
|
* golden fixtures in `<repo>/fixtures/wire/` pin the exact bytes all three SDKs must agree on.
|
|
170
170
|
*/
|
|
171
|
+
/**
|
|
172
|
+
* One workflow a worker ANNOUNCES it can execute — the richer form of a `workflows` name.
|
|
173
|
+
*
|
|
174
|
+
* The rule is "announce only what you can run": a process publishes a registration for a workflow
|
|
175
|
+
* whose body it holds and whose queue it consumes. It never publishes one for a workflow it merely
|
|
176
|
+
* knows how to *route to* (an engine's `registerRemote`), because that claim says nothing about a
|
|
177
|
+
* live executor existing — which is precisely the ambiguity an aggregate registry has to remove.
|
|
178
|
+
*
|
|
179
|
+
* Every field but `name` is optional because the SDKs hold different amounts of this. A worker that
|
|
180
|
+
* knows only names (the Python `@worker.workflow("x")` form, or any pre-registrations SDK) publishes
|
|
181
|
+
* bare `workflows` names instead and is still a first-class announcer — see {@link WorkerDescriptor.registrations}.
|
|
182
|
+
* A reader must therefore treat an absent field as "not stated", never as a value: the aggregate in
|
|
183
|
+
* `./announced` never invents a version, a group or an origin that nobody announced.
|
|
184
|
+
*/
|
|
185
|
+
interface WorkflowRegistration {
|
|
186
|
+
/** Registered workflow name — the `workflow` a {@link WorkflowTask} carries. */
|
|
187
|
+
name: string;
|
|
188
|
+
/** Registered version, when the announcer knows one. Absent = unversioned announcement. */
|
|
189
|
+
version?: string;
|
|
190
|
+
/** The routing token this workflow's turns are dispatched to (the queue this worker consumes for
|
|
191
|
+
* it). Absent = the announcer did not say; a reader must NOT assume the by-convention token. */
|
|
192
|
+
group?: string;
|
|
193
|
+
/** Capabilities a worker must advertise to run it (design §7.5), as the announcer declares them. */
|
|
194
|
+
requires?: string[];
|
|
195
|
+
/** The package that DECLARED the workflow (see `WorkflowRun.origin`). Absent = unknown; nothing
|
|
196
|
+
* ever substitutes a default, because a wrong origin is worse than no origin. */
|
|
197
|
+
origin?: string;
|
|
198
|
+
}
|
|
171
199
|
/**
|
|
172
200
|
* A worker's (or control-plane's) advertised identity, wire-protocol support, feature capabilities
|
|
173
201
|
* and registered handlers. Published on startup + on change (design §7.2) and consumed by
|
|
@@ -200,6 +228,19 @@ interface WorkerDescriptor {
|
|
|
200
228
|
workflows: string[];
|
|
201
229
|
/** Registered step handler names → routing targets. Order-insensitive (a set). */
|
|
202
230
|
steps: string[];
|
|
231
|
+
/**
|
|
232
|
+
* The richer, per-workflow ANNOUNCEMENT of what this instance can execute (name + version + group
|
|
233
|
+
* + requires + origin). OPTIONAL and purely additive: an SDK that has only names keeps publishing
|
|
234
|
+
* {@link workflows} and is aggregated all the same, just unversioned. An SDK that publishes this
|
|
235
|
+
* SHOULD also list the same names in {@link workflows}, so a reader that predates this field still
|
|
236
|
+
* sees the workflow at all.
|
|
237
|
+
*
|
|
238
|
+
* It rides the descriptor rather than a registry of its own so it inherits the whole liveness
|
|
239
|
+
* story for free: the descriptor key is written with the heartbeat's TTL and refreshed by the same
|
|
240
|
+
* beat, so an announcement from a worker that has since died EXPIRES instead of offering a choice
|
|
241
|
+
* that fails at run time.
|
|
242
|
+
*/
|
|
243
|
+
registrations?: WorkflowRegistration[];
|
|
203
244
|
/** Optional routing partition (queue/group sharding). */
|
|
204
245
|
partition?: string;
|
|
205
246
|
/** Optional tenant namespace this instance serves. */
|
|
@@ -252,7 +293,13 @@ declare function isLegacyDescriptor(raw: RawWorkerDescriptor | WorkerDescriptor)
|
|
|
252
293
|
* - absent `workflows`/`steps` → `[]`, absent `sdk` → an `unknown` marker, absent `startedAt` → `0`.
|
|
253
294
|
*
|
|
254
295
|
* The undefined-vs-`[]` distinction is deliberate: an absent field means "legacy, doesn't advertise
|
|
255
|
-
* this axis"; an explicit empty array means "modern SDK that genuinely advertises nothing".
|
|
296
|
+
* this axis"; an explicit empty array means "modern SDK that genuinely advertises nothing". The same
|
|
297
|
+
* reasoning keeps `registrations` ABSENT when it was absent (rather than defaulting to `[]`): "this
|
|
298
|
+
* SDK does not announce registrations" and "this SDK announces none" are different facts, and only
|
|
299
|
+
* the second one licenses a reader to conclude the worker serves nothing. (No SDK publishes `[]`
|
|
300
|
+
* today — {@link describeWorker} and the Python builder both OMIT the field when there is nothing to
|
|
301
|
+
* announce, so a worker with no workflows keeps its pre-announcement bytes and ETag — but a reader
|
|
302
|
+
* must not collapse the two cases on that basis.)
|
|
256
303
|
*/
|
|
257
304
|
declare function normalizeDescriptor(raw: RawWorkerDescriptor | WorkerDescriptor): WorkerDescriptor;
|
|
258
305
|
/**
|
|
@@ -292,6 +339,35 @@ declare function heartbeatStatus(descriptor: RawWorkerDescriptor | WorkerDescrip
|
|
|
292
339
|
ts?: number;
|
|
293
340
|
status?: WorkerLifecycle;
|
|
294
341
|
}): HeartbeatStatus;
|
|
342
|
+
/**
|
|
343
|
+
* Build a worker's own {@link WorkerDescriptor} in the exact §7.1 shape every SDK publishes — the
|
|
344
|
+
* single builder both TS advertisers use (the BullMQ transport's in-app worker and the standalone
|
|
345
|
+
* `runRedisWorker`), so two processes in the same fleet cannot drift into two dialects of the same
|
|
346
|
+
* wire.
|
|
347
|
+
*
|
|
348
|
+
* Set-valued fields are sorted here (not just in the hash projection) so the published BYTES are
|
|
349
|
+
* stable across registration order too — a re-registration in a different order must not look like a
|
|
350
|
+
* change to a reader diffing the raw value. `partition` and `namespace` are omitted when empty, and
|
|
351
|
+
* `namespace` additionally when it is `default`, keeping a single-tenant deployment byte-identical
|
|
352
|
+
* to the un-namespaced scheme. `capabilities` is the canonical v1 baseline this protocol guarantees.
|
|
353
|
+
*/
|
|
354
|
+
declare function describeWorker(opts: {
|
|
355
|
+
instanceId: string;
|
|
356
|
+
runtime: 'node' | 'python';
|
|
357
|
+
sdk: {
|
|
358
|
+
name: string;
|
|
359
|
+
version: string;
|
|
360
|
+
};
|
|
361
|
+
steps: string[];
|
|
362
|
+
workflows?: string[] | undefined;
|
|
363
|
+
/** What this instance announces it can EXECUTE — see {@link WorkflowRegistration}. Omitted from
|
|
364
|
+
* the descriptor entirely when absent or empty, so a worker with nothing to announce keeps the
|
|
365
|
+
* pre-registrations bytes (and therefore the pre-registrations hash). */
|
|
366
|
+
registrations?: readonly WorkflowRegistration[] | undefined;
|
|
367
|
+
startedAt: number;
|
|
368
|
+
partition?: string | undefined;
|
|
369
|
+
namespace?: string | undefined;
|
|
370
|
+
}): WorkerDescriptor;
|
|
295
371
|
|
|
296
372
|
/**
|
|
297
373
|
* Capability- & protocol-aware dispatch planning (design §7.5/§7.6). Sits BETWEEN the pure handshake
|
|
@@ -1463,6 +1539,34 @@ interface Transport {
|
|
|
1463
1539
|
* a run can dispatch to `group` or must park `blocked`.
|
|
1464
1540
|
*/
|
|
1465
1541
|
readWorkerDescriptors?(group: string): Promise<WorkerDescriptor[]>;
|
|
1542
|
+
/**
|
|
1543
|
+
* Every live {@link WorkerDescriptor} in the deployment, across all groups — the read behind
|
|
1544
|
+
* {@link WorkflowEngine.announcedWorkflows}. Optional, same as {@link readWorkerDescriptors}: a
|
|
1545
|
+
* transport that cannot introspect the advertisement keyspace returns nothing and the registry is
|
|
1546
|
+
* simply empty.
|
|
1547
|
+
*
|
|
1548
|
+
* Deliberately NOT `listWorkerGroups()` + a read per group: a worker publishes the SAME descriptor
|
|
1549
|
+
* under every token it consumes, so per-group reads cost one round trip per group to re-fetch bytes
|
|
1550
|
+
* the caller already has. One pass over the advertisement keyspace, de-duplicated by `instanceId`,
|
|
1551
|
+
* answers with one scan and no per-group fan-out — and it also sees an instance whose groups the
|
|
1552
|
+
* caller could not have enumerated.
|
|
1553
|
+
*/
|
|
1554
|
+
readAllWorkerDescriptors?(): Promise<WorkerDescriptor[]>;
|
|
1555
|
+
/**
|
|
1556
|
+
* Every live worker heartbeat in the deployment, across all groups — the LIVENESS floor under
|
|
1557
|
+
* {@link WorkflowEngine.workflowDirectory}. Optional, same as {@link readAllWorkerDescriptors}.
|
|
1558
|
+
*
|
|
1559
|
+
* It exists because a descriptor is the richer advertisement but not the universal one: an SDK
|
|
1560
|
+
* that predates the handshake beats a liveness key and publishes no descriptor at all, and reading
|
|
1561
|
+
* only descriptors reports such a fleet as EMPTY while it is serving work. Convention routing
|
|
1562
|
+
* already resolves those workers off exactly these keys, so a directory built without them
|
|
1563
|
+
* contradicts the dispatcher standing next to it.
|
|
1564
|
+
*
|
|
1565
|
+
* Distinct from {@link listWorkerGroups}, which answers with tokens alone: this carries the
|
|
1566
|
+
* instance behind each one, so the directory can say how many workers back a name and a partition
|
|
1567
|
+
* sighting can name who is beating there.
|
|
1568
|
+
*/
|
|
1569
|
+
readAllWorkerHeartbeats?(): Promise<WorkerHeartbeat[]>;
|
|
1466
1570
|
/**
|
|
1467
1571
|
* DB-less tenant worker → control plane: publish a {@link StartRunMessage} requesting a new run.
|
|
1468
1572
|
* Optional — only transports that carry the hosted-control-plane protocol (P4) implement this.
|
|
@@ -1615,6 +1719,19 @@ type BackoffStrategy = 'fixed' | 'exp';
|
|
|
1615
1719
|
interface ChildCallOptions {
|
|
1616
1720
|
/** Deterministic child run id; defaults to one derived from the parent run id + call position. */
|
|
1617
1721
|
childId?: string | undefined;
|
|
1722
|
+
/**
|
|
1723
|
+
* Pin the child to this EXACT registered version instead of the newest one — the same contract as
|
|
1724
|
+
* `StartOptions.version`, reached from inside a workflow body: an unregistered version fails the
|
|
1725
|
+
* child's start rather than quietly running a different body, and a version can only be pinned on a
|
|
1726
|
+
* workflow that is really registered (not on the synthesized remote/convention routes). A `ctx.child`
|
|
1727
|
+
* whose pinned start fails surfaces it as a failed child on the parent's waiter; a fire-and-forget
|
|
1728
|
+
* `ctx.startChild` buffers it until the parent joins the id.
|
|
1729
|
+
*
|
|
1730
|
+
* Replay-safe as long as you pass a CONSTANT (`{ version: '2' }`), which is the point — it names the
|
|
1731
|
+
* body you meant. Computing it from a live registry read at call time would make the body
|
|
1732
|
+
* non-deterministic, exactly like reading the clock.
|
|
1733
|
+
*/
|
|
1734
|
+
version?: string | undefined;
|
|
1618
1735
|
/**
|
|
1619
1736
|
* Dispatch priority for a REMOTE child workflow — stamped on the child run and carried onto every
|
|
1620
1737
|
* {@link WorkflowTask} dispatched to advance it, so an urgent child can jump ahead of enqueued
|
|
@@ -2288,6 +2405,438 @@ declare class EventAccumulators {
|
|
|
2288
2405
|
private batch;
|
|
2289
2406
|
}
|
|
2290
2407
|
|
|
2408
|
+
/**
|
|
2409
|
+
* The announced workflow registry (design §7.9) — the aggregate answer to "what workflows exist in
|
|
2410
|
+
* this deployment?", built ONLY from what live workers say about themselves.
|
|
2411
|
+
*
|
|
2412
|
+
* ## Why this is announced, not inferred
|
|
2413
|
+
*
|
|
2414
|
+
* `WorkflowEngine.workflowBody(name, version)` answers for the process that asks, and a missing body
|
|
2415
|
+
* is ambiguous by design: it means "not registered here", but equally "registered via
|
|
2416
|
+
* `registerRemote` against another SDK" or "a group this pod resolves by convention against a live
|
|
2417
|
+
* worker". A picker built on that inference would offer different options depending on which replica
|
|
2418
|
+
* served the request. So the registry is not derived from any engine's registry: a worker publishes
|
|
2419
|
+
* what it can execute ({@link WorkerDescriptor.registrations}, or bare `workflows` names), and this
|
|
2420
|
+
* module folds those statements together. Every entry is therefore a fact somebody asserted, and the
|
|
2421
|
+
* aggregate is the same on every pod because every pod reads the same published statements.
|
|
2422
|
+
*
|
|
2423
|
+
* The announcer is always the process that CONSUMES the queue, never the engine that dispatches to
|
|
2424
|
+
* it — including when the two share a process (the co-located in-app worker announces; the engine
|
|
2425
|
+
* beside it does not). One consequence is worth stating plainly rather than leaving to be
|
|
2426
|
+
* rediscovered: a pure operator (`store` with no `connection`) runs its bodies inline, consumes no
|
|
2427
|
+
* queue, and therefore announces NOTHING. Its workflows are real and startable through its own
|
|
2428
|
+
* `start()`, but they are not in this registry, because nothing about them is externally addressable
|
|
2429
|
+
* for a picker to point at.
|
|
2430
|
+
*
|
|
2431
|
+
* ## Liveness
|
|
2432
|
+
*
|
|
2433
|
+
* There is no expiry logic here because there is no state here. An announcement lives on the
|
|
2434
|
+
* descriptor key, which is written with the worker-heartbeat TTL and refreshed by the same beat; a
|
|
2435
|
+
* worker that dies stops refreshing and its key expires, taking its announcements with it. The
|
|
2436
|
+
* resolution of that liveness is therefore the TTL (35s against a 10s beat), so an entry can name a
|
|
2437
|
+
* worker that died within the last beat window — the same staleness the capability router already
|
|
2438
|
+
* accepts when it reads descriptors to decide whether to dispatch. A caller that needs a stronger
|
|
2439
|
+
* guarantee should not seek it here: dispatch to a dead worker parks the run `blocked`/`pending` on
|
|
2440
|
+
* its queue and the existing worker-health surface reports it. What this design DOES rule out is the
|
|
2441
|
+
* unbounded failure — an announcement that outlives its worker forever.
|
|
2442
|
+
*
|
|
2443
|
+
* ## Disagreement
|
|
2444
|
+
*
|
|
2445
|
+
* Two live workers can announce the same `name@version` with different groups, origins or capability
|
|
2446
|
+
* demands. The aggregate keeps EVERY distinct claim and reports the axes they differ on
|
|
2447
|
+
* ({@link AnnouncedWorkflow.disagreements}); it never merges them into one value and never picks a
|
|
2448
|
+
* winner, because either choice would make a caller act on a claim nobody made. Silence is not a
|
|
2449
|
+
* claim: a worker that announced no origin does not disagree with one that did.
|
|
2450
|
+
*
|
|
2451
|
+
* ## Steps are deliberately out of scope
|
|
2452
|
+
*
|
|
2453
|
+
* There is no equivalent registry for steps, and this is not an omission to be fixed later. A step is
|
|
2454
|
+
* not addressable from outside a run: it is identified by its `(runId, seq)` position in one
|
|
2455
|
+
* workflow's history, `ctx.step` is only callable from inside a replaying body, and no engine entry
|
|
2456
|
+
* point starts a step on its own. "Call this step" is therefore not something the engine can do, so a
|
|
2457
|
+
* picker offering steps would be offering an operation that does not exist. Step HANDLER names are
|
|
2458
|
+
* still advertised (`WorkerDescriptor.steps`) — that is a routing/capability fact used to decide
|
|
2459
|
+
* whether a dispatched step can be run, not an invocable catalog.
|
|
2460
|
+
*
|
|
2461
|
+
* ## The floor: a live worker that announces nothing
|
|
2462
|
+
*
|
|
2463
|
+
* An SDK old enough to predate the descriptor advertisement publishes only its TTL'd liveness
|
|
2464
|
+
* heartbeat — one key per routing token it consumes. Read strictly, such a fleet announces nothing
|
|
2465
|
+
* and this registry would be empty while work is being served. That answer is not merely unhelpful,
|
|
2466
|
+
* it CONTRADICTS the engine beside it: `resolveRemoteByConvention` routes a call to `X` the moment a
|
|
2467
|
+
* token named `X` heartbeats, so the deployment already treats liveness as sufficient to CALL a
|
|
2468
|
+
* workflow while claiming to know nothing when asked to LIST one. Both answers come out of the same
|
|
2469
|
+
* Redis.
|
|
2470
|
+
*
|
|
2471
|
+
* So the heartbeat is folded in as a second, weaker tier of evidence ({@link AnnouncedWorkflow.evidence}
|
|
2472
|
+
* `'observed'`): the entry exists because a live token of that name exists, which is exactly — no more
|
|
2473
|
+
* and no less — the condition under which convention routing would resolve it. Listing it therefore
|
|
2474
|
+
* cannot introduce a failure the dispatcher did not already have. What it CANNOT do is state a
|
|
2475
|
+
* version, an origin, a runtime, or even that the token serves a workflow rather than a step handler
|
|
2476
|
+
* of the same name: none of that is in a heartbeat, and every one of those stays un-stated rather than
|
|
2477
|
+
* being guessed. A descriptor always wins — a token an announcement already covers is never re-added
|
|
2478
|
+
* as an observation.
|
|
2479
|
+
*/
|
|
2480
|
+
|
|
2481
|
+
/** One live worker's claim about one workflow: the registration plus who announced it. */
|
|
2482
|
+
interface WorkflowAnnouncement extends WorkflowRegistration {
|
|
2483
|
+
/** The announcing worker's instance id. */
|
|
2484
|
+
instanceId: string;
|
|
2485
|
+
/** The announcing worker's runtime — how a Python-served workflow is told from a Node-served one. */
|
|
2486
|
+
runtime: 'node' | 'python';
|
|
2487
|
+
}
|
|
2488
|
+
/** An axis on which the live announcers of one workflow do not agree. `values` holds every distinct
|
|
2489
|
+
* DECLARED value (sorted); an announcer that stated nothing on the axis contributes nothing. */
|
|
2490
|
+
interface Disagreement {
|
|
2491
|
+
axis: 'group' | 'origin' | 'requires';
|
|
2492
|
+
values: string[];
|
|
2493
|
+
}
|
|
2494
|
+
/**
|
|
2495
|
+
* How strong the fleet's claim about a workflow is. The two tiers are NOT interchangeable and a
|
|
2496
|
+
* caller must not read them as the same fact:
|
|
2497
|
+
*
|
|
2498
|
+
* - `'declared'` — at least one live worker published a descriptor naming this workflow. Everything
|
|
2499
|
+
* populated on the entry was STATED by that worker; everything absent it declined to state.
|
|
2500
|
+
* - `'observed'` — nobody described it. What exists is a live routing token of this name, which is
|
|
2501
|
+
* precisely the condition `resolveRemoteByConvention` uses to route a call, so the name IS
|
|
2502
|
+
* reachable. Nothing else is known: no version, no origin, no runtime, and no assurance that the
|
|
2503
|
+
* worker consuming that token serves a WORKFLOW there rather than a step handler of the same name.
|
|
2504
|
+
* An entry like this is a live queue, not a promise.
|
|
2505
|
+
*/
|
|
2506
|
+
type AnnouncementEvidence = 'declared' | 'observed';
|
|
2507
|
+
/** One workflow the live fleet announces, folded across every worker that announced it. */
|
|
2508
|
+
interface AnnouncedWorkflow {
|
|
2509
|
+
/** What kind of claim this entry rests on — see {@link AnnouncementEvidence}. A caller that shows
|
|
2510
|
+
* a version, an origin or a runtime must check this first: an `'observed'` entry has none of
|
|
2511
|
+
* them, and the emptiness is a fact about the fleet, not a rendering gap. */
|
|
2512
|
+
evidence: AnnouncementEvidence;
|
|
2513
|
+
/** `name@version`, or the bare `name` when no announcer stated a version. Stable sort key. */
|
|
2514
|
+
key: string;
|
|
2515
|
+
name: string;
|
|
2516
|
+
/** Absent when no announcer stated one — never inferred from another announcer's version. */
|
|
2517
|
+
version?: string;
|
|
2518
|
+
/** Every distinct routing group announced (sorted). Empty = nobody stated one. On an `'observed'`
|
|
2519
|
+
* entry this is the token the heartbeat was published UNDER — not a claim by anybody, but the
|
|
2520
|
+
* queue a call would actually land on, which is the one thing an observation does establish. */
|
|
2521
|
+
groups: string[];
|
|
2522
|
+
/** Every distinct declaring package announced (sorted). Empty = nobody stated one. */
|
|
2523
|
+
origins: string[];
|
|
2524
|
+
/** Every distinct capability demand announced, each de-duplicated + sorted. Empty = nobody stated
|
|
2525
|
+
* one. Two entries here means two workers demand different capabilities for the same version. */
|
|
2526
|
+
requires: string[][];
|
|
2527
|
+
/** Runtimes of the live announcers (sorted, de-duplicated). EMPTY on an `'observed'` entry: a
|
|
2528
|
+
* heartbeat does not say what runtime wrote it, and the instance id's shape is a convention, not
|
|
2529
|
+
* a statement, so it is not read as one. */
|
|
2530
|
+
runtimes: ('node' | 'python')[];
|
|
2531
|
+
/** Instance ids of the live workers announcing it (sorted, de-duplicated). Its length is how many
|
|
2532
|
+
* workers can currently run this workflow — `1` is a single point of failure, and it is never 0
|
|
2533
|
+
* (an entry exists only because somebody announced it, or because somebody is beating on it). */
|
|
2534
|
+
instances: string[];
|
|
2535
|
+
/** The axes the announcers differ on, empty when they speak with one voice. A caller that must
|
|
2536
|
+
* pick a single target has to resolve these itself — the registry refuses to guess. */
|
|
2537
|
+
disagreements: Disagreement[];
|
|
2538
|
+
}
|
|
2539
|
+
/**
|
|
2540
|
+
* What one descriptor announces. `registrations` is authoritative when present; any `workflows` name
|
|
2541
|
+
* it does not cover is still announced as a bare, unversioned claim, so a worker that describes some
|
|
2542
|
+
* of its workflows richly and others by name alone is reported in full rather than in part.
|
|
2543
|
+
*
|
|
2544
|
+
* A descriptor with neither field announces nothing — including the control plane's own descriptor,
|
|
2545
|
+
* which is a router, not an executor.
|
|
2546
|
+
*/
|
|
2547
|
+
declare function announcementsOf(raw: RawWorkerDescriptor | WorkerDescriptor): WorkflowAnnouncement[];
|
|
2548
|
+
/**
|
|
2549
|
+
* Fold every live descriptor's announcements into one entry per `name@version`, sorted by key.
|
|
2550
|
+
*
|
|
2551
|
+
* Pure: it holds nothing and caches nothing, so no pod ever carries a growing table of the fleet's
|
|
2552
|
+
* registrations — the caller reads the live descriptors it already knows how to read, folds them
|
|
2553
|
+
* here, and drops the result. The work is O(descriptors × announcements) per call, on demand.
|
|
2554
|
+
*
|
|
2555
|
+
* The same instance announcing the same workflow twice (a worker publishes its descriptor under
|
|
2556
|
+
* every routing token it consumes, so the same bytes arrive several times) folds to ONE announcer:
|
|
2557
|
+
* `instances` is a set, so it counts workers, not keys.
|
|
2558
|
+
*/
|
|
2559
|
+
declare function aggregateAnnouncements(descriptors: readonly (RawWorkerDescriptor | WorkerDescriptor)[]): AnnouncedWorkflow[];
|
|
2560
|
+
/**
|
|
2561
|
+
* The part of one live worker heartbeat this registry can read: the routing token the beat was
|
|
2562
|
+
* published under, and which process published it. That is genuinely all a heartbeat holds — it is
|
|
2563
|
+
* the cheap tier of the two-tier advertisement, and its job is liveness, not description.
|
|
2564
|
+
*
|
|
2565
|
+
* Structurally satisfied by `WorkerHeartbeat` (the transport-facing shape, which also carries a beat
|
|
2566
|
+
* time and an execution snapshot). Declared here rather than imported so this module keeps its "pure
|
|
2567
|
+
* data, no transport" footing and `interfaces.ts` can keep importing FROM it.
|
|
2568
|
+
*/
|
|
2569
|
+
interface HeartbeatSighting {
|
|
2570
|
+
/** The routing token the beat was published under (already partition-suffixed when partitioned). */
|
|
2571
|
+
group: string;
|
|
2572
|
+
/** The beating process's instance id. */
|
|
2573
|
+
instanceId: string;
|
|
2574
|
+
}
|
|
2575
|
+
/**
|
|
2576
|
+
* The weaker tier: one entry per live routing token that NO descriptor accounts for.
|
|
2577
|
+
*
|
|
2578
|
+
* `covered` holds every token the declared announcements already explain — both the sanitized form of
|
|
2579
|
+
* each announced NAME and every group any announcer stated. A token in that set is dropped, so a
|
|
2580
|
+
* fleet that describes itself properly gets exactly the same registry it got before this tier
|
|
2581
|
+
* existed, and a worker is never counted twice under two kinds of evidence.
|
|
2582
|
+
*
|
|
2583
|
+
* Everything an observation cannot know stays empty rather than being filled with a plausible guess:
|
|
2584
|
+
* no version (so nothing can be pinned against it), no origin, no requires, no runtime. The `name` is
|
|
2585
|
+
* the token itself, which equals the workflow's name for every name a queue token can round-trip —
|
|
2586
|
+
* `sanitizeQueueToken` rewrites `:` to `-` and that rewrite is not invertible, so a workflow named
|
|
2587
|
+
* `orders:fulfill` is observed as `orders-fulfill`. Stated here because a picker showing that string
|
|
2588
|
+
* should show what the queue is really called, not a reconstruction that might be wrong.
|
|
2589
|
+
*/
|
|
2590
|
+
declare function observedAnnouncements(heartbeats: readonly HeartbeatSighting[], covered: ReadonlySet<string>): AnnouncedWorkflow[];
|
|
2591
|
+
/**
|
|
2592
|
+
* Every routing token a live descriptor already ACCOUNTS FOR, and which therefore needs no
|
|
2593
|
+
* observation — for either of the two opposite reasons.
|
|
2594
|
+
*
|
|
2595
|
+
* - A token some worker announced as a WORKFLOW: already in the registry, richer than an
|
|
2596
|
+
* observation could ever be, and re-adding it would count one worker twice.
|
|
2597
|
+
* - A token some worker declared as a STEP HANDLER (`WorkerDescriptor.steps`): it must be left OUT,
|
|
2598
|
+
* because a step is not addressable from outside a run and offering one as callable would be
|
|
2599
|
+
* offering an operation the engine does not have. Route-by-handler gives every step its own queue,
|
|
2600
|
+
* so without this the liveness floor turns a fleet's entire step surface into fake workflows.
|
|
2601
|
+
*
|
|
2602
|
+
* That second exclusion is a DECLARED negative — the worker said "this name is a step" — which is
|
|
2603
|
+
* exactly the sort of fact an observation cannot supply for itself, and the clearest illustration of
|
|
2604
|
+
* what the two tiers are each worth. A worker on an SDK too old to describe itself declares neither
|
|
2605
|
+
* its workflows nor its steps, so every one of its tokens stays observed and indistinguishable: its
|
|
2606
|
+
* step queues are listed alongside its workflow queues because nothing published says which is
|
|
2607
|
+
* which. That is a fair report of the fleet, not a defect in the read.
|
|
2608
|
+
*
|
|
2609
|
+
* `sanitize` is injected so this module keeps no dependency on the queue layer; a declared name is
|
|
2610
|
+
* covered in its bare token form and, for a partitioned worker, in the suffixed form its queues
|
|
2611
|
+
* actually carry.
|
|
2612
|
+
*/
|
|
2613
|
+
declare function coveredTokens(announced: readonly AnnouncedWorkflow[], descriptors: readonly (RawWorkerDescriptor | WorkerDescriptor)[], sanitize: (name: string) => string): Set<string>;
|
|
2614
|
+
|
|
2615
|
+
/**
|
|
2616
|
+
* The workflow directory — {@link ./announced}'s registry plus the thing an empty registry could
|
|
2617
|
+
* never say: WHY it is empty.
|
|
2618
|
+
*
|
|
2619
|
+
* ## The three states an empty list used to collapse
|
|
2620
|
+
*
|
|
2621
|
+
* "What workflows can I call?" has three honest answers, and returning `[]` for all of them is the
|
|
2622
|
+
* bug this module exists to fix. They are not shades of the same answer; they call for three
|
|
2623
|
+
* different actions from whoever asked.
|
|
2624
|
+
*
|
|
2625
|
+
* 1. **This process cannot ask.** No transport here can introspect the advertisement or liveness
|
|
2626
|
+
* keyspace — a pure in-process pool advertises nothing and scans nothing. Workers may well be
|
|
2627
|
+
* running; nobody looked. ({@link WorkflowDirectory.supported} is `false`.) The action is to
|
|
2628
|
+
* configure a transport, or to stop treating this pod's answer as the deployment's answer.
|
|
2629
|
+
* 2. **Nothing is live.** The fleet WAS asked and the fleet is empty. ({@link WorkflowDirectory.supported}
|
|
2630
|
+
* is `true`, `workflows` is `[]`.) The action is to start a worker.
|
|
2631
|
+
* 3. **Here it is.** ({@link WorkflowDirectory.supported} is `true`, `workflows` is populated.)
|
|
2632
|
+
*
|
|
2633
|
+
* There is a fourth that hides inside the second and is the most expensive to debug, because it
|
|
2634
|
+
* looks exactly like "nobody is running" from the caller's side: **a worker is live on a partition
|
|
2635
|
+
* this engine does not serve.** A worker started with a partition consumes `<token>@<partition>`,
|
|
2636
|
+
* convention resolution computes the token for THIS engine's namespace, misses, and reports absence.
|
|
2637
|
+
* The heartbeat keyspace is shared, so the evidence is right there — and
|
|
2638
|
+
* {@link WorkflowDirectory.otherPartitions} reports it instead of letting the caller conclude that
|
|
2639
|
+
* nothing exists.
|
|
2640
|
+
*
|
|
2641
|
+
* ## Why a sentence rides along
|
|
2642
|
+
*
|
|
2643
|
+
* Every field here is structured and a caller can branch on all of it. {@link WorkflowDirectory.detail}
|
|
2644
|
+
* exists anyway because the three states are only useful if the human reading the picker learns which
|
|
2645
|
+
* one they are in, and a UI that has to re-derive that prose from booleans will re-derive it
|
|
2646
|
+
* differently in each place it is shown. The sentence is written once, next to the logic that decides
|
|
2647
|
+
* which state it is.
|
|
2648
|
+
*/
|
|
2649
|
+
|
|
2650
|
+
/** The namespace a descriptor or a bare routing token belongs to when it states none. Mirrors
|
|
2651
|
+
* `tenantGroup`'s default-is-bare rule, so a single-tenant deployment reads as `'default'`
|
|
2652
|
+
* everywhere rather than as a special case. */
|
|
2653
|
+
declare const DEFAULT_PARTITION = "default";
|
|
2654
|
+
/** Live workers seen on a partition the asking engine does not serve — the case that otherwise reads
|
|
2655
|
+
* identically to "nothing is running". */
|
|
2656
|
+
interface PartitionSighting {
|
|
2657
|
+
/** The partition suffix carried by the tokens (never this engine's own). */
|
|
2658
|
+
partition: string;
|
|
2659
|
+
/** The routing tokens live on it, as published (sorted). */
|
|
2660
|
+
groups: string[];
|
|
2661
|
+
/** The instance ids beating there (sorted). */
|
|
2662
|
+
instances: string[];
|
|
2663
|
+
}
|
|
2664
|
+
/** What the live fleet can be asked, and what it answered. See the module docblock for the states. */
|
|
2665
|
+
interface WorkflowDirectory {
|
|
2666
|
+
/**
|
|
2667
|
+
* Could this process ask the fleet at all?
|
|
2668
|
+
*
|
|
2669
|
+
* `false` means no transport here can read the advertisement or liveness keyspace, so `workflows`
|
|
2670
|
+
* is empty because nothing was asked — NOT because nothing is running. `true` means the read
|
|
2671
|
+
* happened and `workflows` is the answer, including when it is empty.
|
|
2672
|
+
*/
|
|
2673
|
+
supported: boolean;
|
|
2674
|
+
/** Everything callable in this engine's partition, declared and observed alike. */
|
|
2675
|
+
workflows: AnnouncedWorkflow[];
|
|
2676
|
+
/** Live workers on OTHER partitions (empty for an operator engine, which serves them all). */
|
|
2677
|
+
otherPartitions: PartitionSighting[];
|
|
2678
|
+
/** One sentence saying which state this is and what it implies. Written for a human. */
|
|
2679
|
+
detail: string;
|
|
2680
|
+
}
|
|
2681
|
+
/** The partition a routing token belongs to: the suffix after the LAST `@`, or {@link DEFAULT_PARTITION}
|
|
2682
|
+
* for a bare token. Matches how `tenantGroup` builds the token and how `workerHealth` takes it apart. */
|
|
2683
|
+
declare function partitionOfToken(token: string): string;
|
|
2684
|
+
/**
|
|
2685
|
+
* Fold live descriptors and live heartbeats into the directory for one engine.
|
|
2686
|
+
*
|
|
2687
|
+
* `namespace` is the asking engine's, and `undefined` means an operator that serves every partition
|
|
2688
|
+
* ("ver tudo = ausência de namespace"): it filters nothing and reports no foreign partitions, because
|
|
2689
|
+
* none of them are foreign to it. A namespaced engine sees only its own partition's workers, and
|
|
2690
|
+
* everything else it can see is reported as a sighting rather than silently dropped — dropping is
|
|
2691
|
+
* what made a misconfigured partition indistinguishable from an empty fleet.
|
|
2692
|
+
*
|
|
2693
|
+
* Pure and holds nothing: the caller does the two live reads it already knows how to do, folds them
|
|
2694
|
+
* here, and drops the result.
|
|
2695
|
+
*/
|
|
2696
|
+
declare function buildWorkflowDirectory(input: {
|
|
2697
|
+
descriptors: readonly (RawWorkerDescriptor | WorkerDescriptor)[];
|
|
2698
|
+
heartbeats: readonly HeartbeatSighting[];
|
|
2699
|
+
namespace: string | undefined;
|
|
2700
|
+
/** Whether ANY transport in the pool could perform the reads above. */
|
|
2701
|
+
supported: boolean;
|
|
2702
|
+
/** The queue-token form of a workflow name — injected so this module needs no queue dependency. */
|
|
2703
|
+
sanitize: (name: string) => string;
|
|
2704
|
+
}): WorkflowDirectory;
|
|
2705
|
+
|
|
2706
|
+
/**
|
|
2707
|
+
* Bilateral compatibility negotiation between two handshake descriptors (design §7.3/§7.4). Pure
|
|
2708
|
+
* logic: given a local and a remote {@link WorkerDescriptor}, compute the negotiated session (highest
|
|
2709
|
+
* common protocol major + capability intersection) and classify it into one of three outcomes with a
|
|
2710
|
+
* precise, structured reason — never a bare boolean (design §7.6).
|
|
2711
|
+
*/
|
|
2712
|
+
|
|
2713
|
+
/**
|
|
2714
|
+
* The three compatibility outcomes (design §7.4):
|
|
2715
|
+
* - `compatible` — protocol ranges intersect + full capability parity → dispatch freely.
|
|
2716
|
+
* - `degraded` — ranges intersect but a capability is missing on one side → dispatch, but route
|
|
2717
|
+
* capability-requiring work only to capable workers (soft warning).
|
|
2718
|
+
* - `incompatible` — no protocol-range overlap → do NOT dispatch; red flag with the exact reason.
|
|
2719
|
+
*/
|
|
2720
|
+
type NegotiationOutcome = 'compatible' | 'degraded' | 'incompatible';
|
|
2721
|
+
/** Structured failure/warning payload — carries the precise delta so a diagnostics event can render
|
|
2722
|
+
* it on the timeline + dashboard health panel (design §7.6). Never a bare boolean. */
|
|
2723
|
+
interface NegotiationReason {
|
|
2724
|
+
/** Stable machine code for alerting/telescope. */
|
|
2725
|
+
code: 'protocol.incompatible' | 'capability.unavailable';
|
|
2726
|
+
/** Human-readable one-liner (dashboard/telescope copy). */
|
|
2727
|
+
message: string;
|
|
2728
|
+
/** The exact delta: which protocol ranges failed to overlap, or which capabilities are missing. */
|
|
2729
|
+
detail: {
|
|
2730
|
+
localRange?: [number, number];
|
|
2731
|
+
remoteRange?: [number, number];
|
|
2732
|
+
/** Capabilities the local side advertises that the remote lacks. */
|
|
2733
|
+
missingOnRemote?: string[];
|
|
2734
|
+
/** Capabilities the remote side advertises that the local lacks. */
|
|
2735
|
+
missingOnLocal?: string[];
|
|
2736
|
+
/** Explicitly-required capabilities not satisfied by the negotiated session. */
|
|
2737
|
+
missingRequired?: string[];
|
|
2738
|
+
};
|
|
2739
|
+
}
|
|
2740
|
+
/** The negotiated session + full delta between two sides. Carries both normalized descriptors so a
|
|
2741
|
+
* downstream diagnostics event can attach them wholesale (design §7.6). */
|
|
2742
|
+
interface NegotiationResult {
|
|
2743
|
+
outcome: NegotiationOutcome;
|
|
2744
|
+
/** Highest common protocol major, or `null` when ranges do not overlap (incompatible). */
|
|
2745
|
+
negotiatedProtocol: number | null;
|
|
2746
|
+
protocol: {
|
|
2747
|
+
localRange: [number, number];
|
|
2748
|
+
remoteRange: [number, number];
|
|
2749
|
+
};
|
|
2750
|
+
capabilities: {
|
|
2751
|
+
/** Intersection — the capabilities the negotiated session can rely on everywhere. */
|
|
2752
|
+
shared: string[];
|
|
2753
|
+
/** Local advertises, remote lacks. */
|
|
2754
|
+
missingOnRemote: string[];
|
|
2755
|
+
/** Remote advertises, local lacks. */
|
|
2756
|
+
missingOnLocal: string[];
|
|
2757
|
+
};
|
|
2758
|
+
/** Present for `degraded` + `incompatible`; absent for `compatible`. */
|
|
2759
|
+
reason?: NegotiationReason;
|
|
2760
|
+
local: WorkerDescriptor;
|
|
2761
|
+
remote: WorkerDescriptor;
|
|
2762
|
+
}
|
|
2763
|
+
interface NegotiateOptions {
|
|
2764
|
+
/**
|
|
2765
|
+
* Capabilities the local side needs the negotiated session to provide. A required capability the
|
|
2766
|
+
* remote lacks degrades the session (dispatch generally, but capability-requiring runs park via
|
|
2767
|
+
* the router, design §7.5) — it never, on its own, makes the pair *incompatible*: only a protocol
|
|
2768
|
+
* range gap does that (design §7.4).
|
|
2769
|
+
*/
|
|
2770
|
+
required?: string[];
|
|
2771
|
+
}
|
|
2772
|
+
/**
|
|
2773
|
+
* Negotiate compatibility between `local` and `remote` (design §7.3/§7.4). Accepts raw/partial
|
|
2774
|
+
* descriptors (a missing `protocol`/`capabilities` is normalized to the legacy-v1 baseline, so a
|
|
2775
|
+
* legacy peer negotiates as **assume-compatible**, design §7.7).
|
|
2776
|
+
*
|
|
2777
|
+
* Outcome logic:
|
|
2778
|
+
* 1. no protocol-range overlap → `incompatible` (`protocol.incompatible`);
|
|
2779
|
+
* 2. else if a `required` capability is missing on the remote → `degraded` (`capability.unavailable`);
|
|
2780
|
+
* 3. else if the capability sets differ at all (either direction) → `degraded`;
|
|
2781
|
+
* 4. else full parity → `compatible`.
|
|
2782
|
+
*
|
|
2783
|
+
* **Bilateral-symmetric** on outcome for the parity path: `negotiate(a, b).outcome ===
|
|
2784
|
+
* negotiate(b, a).outcome` (the `missingOnLocal`/`missingOnRemote` delta swaps). `required` is the
|
|
2785
|
+
* caller's own need and intentionally asymmetric.
|
|
2786
|
+
*/
|
|
2787
|
+
declare function negotiate(localRaw: RawWorkerDescriptor | WorkerDescriptor, remoteRaw: RawWorkerDescriptor | WorkerDescriptor, opts?: NegotiateOptions): NegotiationResult;
|
|
2788
|
+
|
|
2789
|
+
/**
|
|
2790
|
+
* Capability-aware routing (design §7.5). A workflow/step handler may declare
|
|
2791
|
+
* `requires: ['saga', 'search-attr-v2']`; the control-plane dispatches only to workers whose
|
|
2792
|
+
* descriptor advertises every required capability. If no live capable worker exists the run must
|
|
2793
|
+
* **park as `blocked`** with a precise reason (visible in the dashboard) — never a silent hang.
|
|
2794
|
+
*
|
|
2795
|
+
* Pure logic: no transport, no store. Consumers (the dispatcher/provider) feed live descriptors in.
|
|
2796
|
+
*/
|
|
2797
|
+
|
|
2798
|
+
/** A handler's capability demand — either a bare list, or anything carrying a `requires` array
|
|
2799
|
+
* (a workflow/step ref). An absent/empty list means "runs anywhere" (design §7.5). */
|
|
2800
|
+
type CapabilityRequirement = string[] | {
|
|
2801
|
+
requires?: string[];
|
|
2802
|
+
};
|
|
2803
|
+
/** Normalize the two accepted shapes to a de-duplicated list of required capability names. */
|
|
2804
|
+
declare function requiredCapabilities(handler: CapabilityRequirement): string[];
|
|
2805
|
+
/**
|
|
2806
|
+
* Can `worker` run a handler with these requirements? True iff the worker's (normalized) descriptor
|
|
2807
|
+
* advertises **every** required capability. A legacy worker (no `capabilities` field) is normalized
|
|
2808
|
+
* to the v1 baseline first (design §7.7), so it can still run baseline-only work.
|
|
2809
|
+
*/
|
|
2810
|
+
declare function canRoute(handler: CapabilityRequirement, worker: RawWorkerDescriptor | WorkerDescriptor): boolean;
|
|
2811
|
+
/**
|
|
2812
|
+
* The capabilities a handler requires that `worker` does NOT advertise — the precise routing delta.
|
|
2813
|
+
* Empty iff {@link canRoute} is true.
|
|
2814
|
+
*/
|
|
2815
|
+
declare function missingCapabilities(handler: CapabilityRequirement, worker: RawWorkerDescriptor | WorkerDescriptor): string[];
|
|
2816
|
+
/** Filter a fleet down to the workers that can run a handler (design §7.5). */
|
|
2817
|
+
declare function capableWorkers<T extends RawWorkerDescriptor | WorkerDescriptor>(handler: CapabilityRequirement, workers: readonly T[]): T[];
|
|
2818
|
+
/** A run is dispatchable — at least one live worker can execute the handler. */
|
|
2819
|
+
interface RoutableResolution {
|
|
2820
|
+
status: 'routable';
|
|
2821
|
+
/** The normalized subset of the fleet that can run it. */
|
|
2822
|
+
workers: WorkerDescriptor[];
|
|
2823
|
+
requires: string[];
|
|
2824
|
+
}
|
|
2825
|
+
/** No live capable worker — the run parks as `blocked` with a human reason (design §7.5). */
|
|
2826
|
+
interface BlockedResolution {
|
|
2827
|
+
status: 'blocked';
|
|
2828
|
+
/** Exactly the dashboard copy: `blocked: no compatible worker (requires <caps>)`. */
|
|
2829
|
+
reason: string;
|
|
2830
|
+
requires: string[];
|
|
2831
|
+
}
|
|
2832
|
+
type RoutingResolution = RoutableResolution | BlockedResolution;
|
|
2833
|
+
/**
|
|
2834
|
+
* Decide whether a handler can be dispatched to the given live fleet (design §7.5). Returns
|
|
2835
|
+
* `routable` with the capable subset, or `blocked` with a precise reason when nobody can run it —
|
|
2836
|
+
* so the run parks visibly instead of hanging silently. An empty fleet always blocks.
|
|
2837
|
+
*/
|
|
2838
|
+
declare function resolveRouting(handler: CapabilityRequirement, workers: readonly (RawWorkerDescriptor | WorkerDescriptor)[]): RoutingResolution;
|
|
2839
|
+
|
|
2291
2840
|
type WorkflowFn = (ctx: InternalWorkflowCtx, input: unknown) => Promise<unknown>;
|
|
2292
2841
|
/**
|
|
2293
2842
|
* Options for {@link WorkflowEngine.start}.
|
|
@@ -2298,6 +2847,26 @@ type WorkflowFn = (ctx: InternalWorkflowCtx, input: unknown) => Promise<unknown>
|
|
|
2298
2847
|
* another lib's, and an origin filter would quietly lie.
|
|
2299
2848
|
*/
|
|
2300
2849
|
interface StartOptions {
|
|
2850
|
+
/**
|
|
2851
|
+
* Start THIS registered version instead of the newest one. Omit (the default) and the run starts on
|
|
2852
|
+
* the newest registered version, exactly as it always has — this option only ever narrows.
|
|
2853
|
+
*
|
|
2854
|
+
* Resolved against the by-name@version registry, so it pins anything explicitly registered:
|
|
2855
|
+
* {@link WorkflowEngine.register}, {@link WorkflowEngine.registerRemote}, {@link WorkflowEngine.remote}.
|
|
2856
|
+
* A version that is NOT registered throws before a run row is created and NEVER falls back to
|
|
2857
|
+
* `latest` — a silent fallback is the exact failure this option exists to prevent (a caller that
|
|
2858
|
+
* asked for `name@3` and got `name@4` has no way to tell, and its run is already going).
|
|
2859
|
+
*
|
|
2860
|
+
* NOT available on the two SYNTHESIZED registration paths, which exist precisely because nothing is
|
|
2861
|
+
* registered: a child inheriting its remote ancestor's routing (see `findInheritedRegistration`),
|
|
2862
|
+
* and convention routing to a live worker group of the same name (`resolveRemoteByConvention`).
|
|
2863
|
+
* Both INVENT the version they stamp — the ancestor's, and the `'1'` convention default — as a
|
|
2864
|
+
* routing placeholder; neither is evidence that the worker has a body at the version you asked for.
|
|
2865
|
+
* A pinned start that reaches them therefore throws rather than pretending to have honoured the pin.
|
|
2866
|
+
* To pin a cross-SDK workflow, register it for real — `engine.remote('name', { version: '2', group })`
|
|
2867
|
+
* — which puts `name@2` in the registry and pins like any other registration.
|
|
2868
|
+
*/
|
|
2869
|
+
version?: string | undefined;
|
|
2301
2870
|
/** Run-scoped tags, merged with the workflow's static `@Workflow({ tags })` onto the run. */
|
|
2302
2871
|
tags?: string[] | undefined;
|
|
2303
2872
|
/** Typed, queryable run data stamped on the run (e.g. `{ amount: 200, tier: 'pro' }`). */
|
|
@@ -2333,6 +2902,30 @@ interface SingletonConfig {
|
|
|
2333
2902
|
*/
|
|
2334
2903
|
maxQueueDepth?: number;
|
|
2335
2904
|
}
|
|
2905
|
+
/**
|
|
2906
|
+
* Stamped on a run whose workflow version NOBODY declared: a remote resolved by convention, served
|
|
2907
|
+
* by a worker that announces no version for it (an SDK too old to publish a descriptor, or a body
|
|
2908
|
+
* whose author simply never stated one).
|
|
2909
|
+
*
|
|
2910
|
+
* ## Why a run has to carry this
|
|
2911
|
+
*
|
|
2912
|
+
* Such a run's `workflowVersion` is `'1'`, the engine's convention default. That value is a routing
|
|
2913
|
+
* placeholder, not an observation — and read back later it is indistinguishable from a worker that
|
|
2914
|
+
* genuinely declared version `'1'`. Any check comparing an authored pin against it therefore passes
|
|
2915
|
+
* whenever the pin is `'1'` and fails whenever it is not, regardless of what the callee actually is.
|
|
2916
|
+
* A check that cannot fail is worse than no check, because it reads as a guarantee.
|
|
2917
|
+
*
|
|
2918
|
+
* The tag is the difference, carried ON the run so a retrospective check needs no second read: a
|
|
2919
|
+
* caller that pinned a version can see that the version it is comparing against was never stated,
|
|
2920
|
+
* and say so, instead of reporting a match it did not really make. It is deliberately a tag rather
|
|
2921
|
+
* than a new column — every store already persists and queries tags, so this needs no migration and
|
|
2922
|
+
* shows up in run listings and dashboards for free. Follows the `singleton:<key>` precedent for an
|
|
2923
|
+
* engine-stamped reserved tag.
|
|
2924
|
+
*
|
|
2925
|
+
* A run whose callee DOES declare a version never carries it; that run's version is the declared one
|
|
2926
|
+
* and a pin against it is genuinely checked.
|
|
2927
|
+
*/
|
|
2928
|
+
declare const VERSION_UNDECLARED_TAG = "version:undeclared";
|
|
2336
2929
|
interface WorkflowEngineDeps {
|
|
2337
2930
|
store: StateStore;
|
|
2338
2931
|
/** A single task transport. Shorthand for a one-entry `transports` pool (id `default`). */
|
|
@@ -2645,6 +3238,10 @@ declare class WorkflowEngine {
|
|
|
2645
3238
|
* remote (a `registerRemote` / synthesized child carries only a throwing stub, never handed out).
|
|
2646
3239
|
*/
|
|
2647
3240
|
workflowBody(name: string, version: string): WorkflowFn | undefined;
|
|
3241
|
+
/** The registered versions of `name`, for the pinned-start error — "2 is not registered" is a
|
|
3242
|
+
* guess-the-typo error without them, and the answer (which versions this deploy actually carries)
|
|
3243
|
+
* is right here. `none` when the name is unknown entirely. */
|
|
3244
|
+
private registeredVersions;
|
|
2648
3245
|
/**
|
|
2649
3246
|
* Register a workflow whose body runs in another SDK (e.g. Python). The engine owns the run exactly
|
|
2650
3247
|
* as for a TS workflow — it persists checkpoints, recovers, runs timers — but advances it by handing
|
|
@@ -2770,6 +3367,21 @@ declare class WorkflowEngine {
|
|
|
2770
3367
|
* liveness changes between calls, exactly as {@link synthesizeRemoteChild} does.
|
|
2771
3368
|
*/
|
|
2772
3369
|
private resolveRemoteByConvention;
|
|
3370
|
+
/**
|
|
3371
|
+
* The version the LIVE fleet declares for `workflow` on `group`, or `undefined` when it declares
|
|
3372
|
+
* none — which is a fact about the fleet, not a failure to read it.
|
|
3373
|
+
*
|
|
3374
|
+
* The read is the descriptor advertisement, scoped to the one group being resolved: the workers on
|
|
3375
|
+
* that queue, their `registrations`, the entry whose `name` matches. That is where a version can
|
|
3376
|
+
* come from at all — a heartbeat has no room for one, so a worker on an SDK too old to publish a
|
|
3377
|
+
* descriptor declares nothing and the caller is told so rather than handed a default.
|
|
3378
|
+
*
|
|
3379
|
+
* `undefined` when the announcers DISAGREE, and deliberately: two live workers claiming different
|
|
3380
|
+
* versions of one name is a deployment in the middle of something, and picking either would stamp
|
|
3381
|
+
* a run with a version half the fleet does not serve. Undeclared is the honest reading of a
|
|
3382
|
+
* contradiction, and it is the reading that refuses to let a pin pass on a coin flip.
|
|
3383
|
+
*/
|
|
3384
|
+
private declaredVersion;
|
|
2773
3385
|
/**
|
|
2774
3386
|
* Walk the parent chain of `childRunId` to the nearest ancestor registered as REMOTE, returning its
|
|
2775
3387
|
* registration (or `undefined` if there is none). The parent of an awaited child is found via its
|
|
@@ -2800,7 +3412,32 @@ declare class WorkflowEngine {
|
|
|
2800
3412
|
/**
|
|
2801
3413
|
* Cancel in-flight runs that have outlived their workflow's `executionTimeout`. Call it from the
|
|
2802
3414
|
* timer poller alongside {@link resumeDueTimers}. A timed-out run is moved to `cancelled` with an
|
|
2803
|
-
* `execution_timeout` error (terminal, so a late step result can't resurrect it)
|
|
3415
|
+
* `execution_timeout` error (terminal, so a late step result can't resurrect it), AND its children
|
|
3416
|
+
* are cascaded exactly as an explicit {@link cancel} cascades them.
|
|
3417
|
+
*
|
|
3418
|
+
* WHY THE TERMINAL WRITE IS DIRECT, not a call to {@link cancel}. Two reasons, both still standing:
|
|
3419
|
+
* the run must land with the `execution_timeout` error `cancel` knows nothing about (`cancel` writes
|
|
3420
|
+
* a plain `cancelled`, and the code is what a dashboard/alert distinguishes a timeout by), and this
|
|
3421
|
+
* is a SCAN — it walks every in-flight run of every timed workflow on every poller tick, so it must
|
|
3422
|
+
* not pay `cancel`'s per-run re-read and singleton/control-plane work on runs it isn't cancelling.
|
|
3423
|
+
* The cascade below is charged only to the runs that ACTUALLY timed out, which are rare by
|
|
3424
|
+
* construction; the scan itself is unchanged.
|
|
3425
|
+
*
|
|
3426
|
+
* The subtree, though, has no reason to differ, and before this it did: a timed-out parent's
|
|
3427
|
+
* children kept running with the parent gone and nothing pointing at them — a silent orphan you
|
|
3428
|
+
* could only find by reading the runs table by hand. {@link cancelChildren} is the SAME recursive
|
|
3429
|
+
* walk `cancel` uses, so children of children go too, and it inherits that path's two guards:
|
|
3430
|
+
* `cancel` returns early on a run that is already `completed`/`cancelled`/`dead` (a child that beat
|
|
3431
|
+
* the sweep to the finish is never clobbered, and a re-cancel is a no-op), and it writes the
|
|
3432
|
+
* parent's terminal status BEFORE recursing, so a cyclic parent→child graph terminates on the
|
|
3433
|
+
* second visit instead of looping. Two pollers sweeping the same run concurrently therefore write
|
|
3434
|
+
* the same terminal state twice and cascade into already-cancelled children — idempotent, not
|
|
3435
|
+
* corrupting. The one window it does NOT close is the pre-existing check-then-write race an
|
|
3436
|
+
* explicit `cancel` has always had (a child that completes between `cancel`'s read and its write);
|
|
3437
|
+
* closing that needs a compare-and-set in the store contract, not a change here.
|
|
3438
|
+
*
|
|
3439
|
+
* Cascade failures are swallowed per subtree for the same reason `cancelChildren` swallows them per
|
|
3440
|
+
* child: one unreachable child must not abort the sweep and leave every LATER timed-out run alive.
|
|
2804
3441
|
*/
|
|
2805
3442
|
sweepTimeouts(now?: number): Promise<void>;
|
|
2806
3443
|
/**
|
|
@@ -3064,6 +3701,10 @@ declare class WorkflowEngine {
|
|
|
3064
3701
|
* `child:<id>` waiter) and fire-and-forget (`ctx.startChild`, found via its `spawn:<id>`
|
|
3065
3702
|
* checkpoint). Recursive, so a whole subtree is cancelled; the terminal guard in `cancel` stops it
|
|
3066
3703
|
* at finished / already-cancelled runs (no loops, no re-cancel).
|
|
3704
|
+
*
|
|
3705
|
+
* Two callers, deliberately the same walk: an explicit {@link cancel}, and {@link sweepTimeouts}
|
|
3706
|
+
* when a parent hits its `executionTimeout` — a timed-out parent's subtree dies the same way an
|
|
3707
|
+
* explicitly cancelled one's does, or the children are orphans nobody is holding a reference to.
|
|
3067
3708
|
*/
|
|
3068
3709
|
private cancelChildren;
|
|
3069
3710
|
/**
|
|
@@ -3090,6 +3731,40 @@ declare class WorkflowEngine {
|
|
|
3090
3731
|
* from registrations, shows once its workers beat). Empty when no transport can introspect health
|
|
3091
3732
|
* (only the BullMQ transport implements `groupHealth`). */
|
|
3092
3733
|
workerHealth(extra?: string[]): Promise<GroupHealth[]>;
|
|
3734
|
+
/**
|
|
3735
|
+
* What workflows the LIVE fleet announces it can execute — the deployment-wide registry a picker
|
|
3736
|
+
* ("call this existing workflow") needs, and the one question this engine's own registry cannot
|
|
3737
|
+
* answer. {@link workflowBody} answers only for this process, and a missing body is ambiguous: it
|
|
3738
|
+
* means "not registered here", but equally "registered via {@link registerRemote} against another
|
|
3739
|
+
* SDK" or "a group resolved by convention against a worker". An inferred answer would therefore
|
|
3740
|
+
* differ per replica. This one does not: every pod folds the same published announcements.
|
|
3741
|
+
*
|
|
3742
|
+
* Scoped like every other poll surface — an engine WITH a `namespace` sees only workers in it, and
|
|
3743
|
+
* an operator (namespace unset) sees the whole deployment ("ver tudo = ausência de namespace"). A
|
|
3744
|
+
* descriptor that states no namespace is `default`, matching how a single-tenant fleet publishes.
|
|
3745
|
+
*
|
|
3746
|
+
* Cost: one advertisement scan per CALL, on demand. Nothing is added to the poll loop and nothing
|
|
3747
|
+
* is retained — no pod holds a table of the fleet's registrations. Empty when no transport can
|
|
3748
|
+
* introspect the advertisement keyspace (a pure in-process transport advertises nothing).
|
|
3749
|
+
*
|
|
3750
|
+
* Workflows only, and deliberately: see the "steps are out of scope" note in `handshake/announced`.
|
|
3751
|
+
*/
|
|
3752
|
+
announcedWorkflows(): Promise<AnnouncedWorkflow[]>;
|
|
3753
|
+
/**
|
|
3754
|
+
* {@link announcedWorkflows}, plus the thing a list of workflows can never say: why it is the
|
|
3755
|
+
* length it is. See {@link WorkflowDirectory} for the three states an empty array used to collapse
|
|
3756
|
+
* into one — "nobody asked", "nothing is live", and the partition mismatch that reads exactly like
|
|
3757
|
+
* the second from the caller's side.
|
|
3758
|
+
*
|
|
3759
|
+
* Two live reads, both on demand and neither retained: the descriptor advertisement (what workers
|
|
3760
|
+
* SAY they serve) and the heartbeat keyspace (what is demonstrably consuming a queue right now).
|
|
3761
|
+
* The second is why a worker on an SDK too old to publish a descriptor appears here at all — it
|
|
3762
|
+
* beats and never describes itself, and reading only descriptors reported such a fleet as empty
|
|
3763
|
+
* while `resolveRemoteByConvention`, a few lines away in this same class, was routing calls to it
|
|
3764
|
+
* off those very keys. Two answers out of one Redis, disagreeing. Now they agree, and the entry
|
|
3765
|
+
* says which of the two kinds of evidence it rests on rather than flattening them together.
|
|
3766
|
+
*/
|
|
3767
|
+
workflowDirectory(): Promise<WorkflowDirectory>;
|
|
3093
3768
|
/**
|
|
3094
3769
|
* The routing groups of every in-flight run's still-`pending` remote step — the queues where work is
|
|
3095
3770
|
* dispatched and awaiting a worker. Folded into {@link workerHealth} so a step waiting on a group
|
|
@@ -3550,140 +4225,6 @@ declare class ContinueAsNew extends Error {
|
|
|
3550
4225
|
constructor(input: unknown);
|
|
3551
4226
|
}
|
|
3552
4227
|
|
|
3553
|
-
/**
|
|
3554
|
-
* Bilateral compatibility negotiation between two handshake descriptors (design §7.3/§7.4). Pure
|
|
3555
|
-
* logic: given a local and a remote {@link WorkerDescriptor}, compute the negotiated session (highest
|
|
3556
|
-
* common protocol major + capability intersection) and classify it into one of three outcomes with a
|
|
3557
|
-
* precise, structured reason — never a bare boolean (design §7.6).
|
|
3558
|
-
*/
|
|
3559
|
-
|
|
3560
|
-
/**
|
|
3561
|
-
* The three compatibility outcomes (design §7.4):
|
|
3562
|
-
* - `compatible` — protocol ranges intersect + full capability parity → dispatch freely.
|
|
3563
|
-
* - `degraded` — ranges intersect but a capability is missing on one side → dispatch, but route
|
|
3564
|
-
* capability-requiring work only to capable workers (soft warning).
|
|
3565
|
-
* - `incompatible` — no protocol-range overlap → do NOT dispatch; red flag with the exact reason.
|
|
3566
|
-
*/
|
|
3567
|
-
type NegotiationOutcome = 'compatible' | 'degraded' | 'incompatible';
|
|
3568
|
-
/** Structured failure/warning payload — carries the precise delta so a diagnostics event can render
|
|
3569
|
-
* it on the timeline + dashboard health panel (design §7.6). Never a bare boolean. */
|
|
3570
|
-
interface NegotiationReason {
|
|
3571
|
-
/** Stable machine code for alerting/telescope. */
|
|
3572
|
-
code: 'protocol.incompatible' | 'capability.unavailable';
|
|
3573
|
-
/** Human-readable one-liner (dashboard/telescope copy). */
|
|
3574
|
-
message: string;
|
|
3575
|
-
/** The exact delta: which protocol ranges failed to overlap, or which capabilities are missing. */
|
|
3576
|
-
detail: {
|
|
3577
|
-
localRange?: [number, number];
|
|
3578
|
-
remoteRange?: [number, number];
|
|
3579
|
-
/** Capabilities the local side advertises that the remote lacks. */
|
|
3580
|
-
missingOnRemote?: string[];
|
|
3581
|
-
/** Capabilities the remote side advertises that the local lacks. */
|
|
3582
|
-
missingOnLocal?: string[];
|
|
3583
|
-
/** Explicitly-required capabilities not satisfied by the negotiated session. */
|
|
3584
|
-
missingRequired?: string[];
|
|
3585
|
-
};
|
|
3586
|
-
}
|
|
3587
|
-
/** The negotiated session + full delta between two sides. Carries both normalized descriptors so a
|
|
3588
|
-
* downstream diagnostics event can attach them wholesale (design §7.6). */
|
|
3589
|
-
interface NegotiationResult {
|
|
3590
|
-
outcome: NegotiationOutcome;
|
|
3591
|
-
/** Highest common protocol major, or `null` when ranges do not overlap (incompatible). */
|
|
3592
|
-
negotiatedProtocol: number | null;
|
|
3593
|
-
protocol: {
|
|
3594
|
-
localRange: [number, number];
|
|
3595
|
-
remoteRange: [number, number];
|
|
3596
|
-
};
|
|
3597
|
-
capabilities: {
|
|
3598
|
-
/** Intersection — the capabilities the negotiated session can rely on everywhere. */
|
|
3599
|
-
shared: string[];
|
|
3600
|
-
/** Local advertises, remote lacks. */
|
|
3601
|
-
missingOnRemote: string[];
|
|
3602
|
-
/** Remote advertises, local lacks. */
|
|
3603
|
-
missingOnLocal: string[];
|
|
3604
|
-
};
|
|
3605
|
-
/** Present for `degraded` + `incompatible`; absent for `compatible`. */
|
|
3606
|
-
reason?: NegotiationReason;
|
|
3607
|
-
local: WorkerDescriptor;
|
|
3608
|
-
remote: WorkerDescriptor;
|
|
3609
|
-
}
|
|
3610
|
-
interface NegotiateOptions {
|
|
3611
|
-
/**
|
|
3612
|
-
* Capabilities the local side needs the negotiated session to provide. A required capability the
|
|
3613
|
-
* remote lacks degrades the session (dispatch generally, but capability-requiring runs park via
|
|
3614
|
-
* the router, design §7.5) — it never, on its own, makes the pair *incompatible*: only a protocol
|
|
3615
|
-
* range gap does that (design §7.4).
|
|
3616
|
-
*/
|
|
3617
|
-
required?: string[];
|
|
3618
|
-
}
|
|
3619
|
-
/**
|
|
3620
|
-
* Negotiate compatibility between `local` and `remote` (design §7.3/§7.4). Accepts raw/partial
|
|
3621
|
-
* descriptors (a missing `protocol`/`capabilities` is normalized to the legacy-v1 baseline, so a
|
|
3622
|
-
* legacy peer negotiates as **assume-compatible**, design §7.7).
|
|
3623
|
-
*
|
|
3624
|
-
* Outcome logic:
|
|
3625
|
-
* 1. no protocol-range overlap → `incompatible` (`protocol.incompatible`);
|
|
3626
|
-
* 2. else if a `required` capability is missing on the remote → `degraded` (`capability.unavailable`);
|
|
3627
|
-
* 3. else if the capability sets differ at all (either direction) → `degraded`;
|
|
3628
|
-
* 4. else full parity → `compatible`.
|
|
3629
|
-
*
|
|
3630
|
-
* **Bilateral-symmetric** on outcome for the parity path: `negotiate(a, b).outcome ===
|
|
3631
|
-
* negotiate(b, a).outcome` (the `missingOnLocal`/`missingOnRemote` delta swaps). `required` is the
|
|
3632
|
-
* caller's own need and intentionally asymmetric.
|
|
3633
|
-
*/
|
|
3634
|
-
declare function negotiate(localRaw: RawWorkerDescriptor | WorkerDescriptor, remoteRaw: RawWorkerDescriptor | WorkerDescriptor, opts?: NegotiateOptions): NegotiationResult;
|
|
3635
|
-
|
|
3636
|
-
/**
|
|
3637
|
-
* Capability-aware routing (design §7.5). A workflow/step handler may declare
|
|
3638
|
-
* `requires: ['saga', 'search-attr-v2']`; the control-plane dispatches only to workers whose
|
|
3639
|
-
* descriptor advertises every required capability. If no live capable worker exists the run must
|
|
3640
|
-
* **park as `blocked`** with a precise reason (visible in the dashboard) — never a silent hang.
|
|
3641
|
-
*
|
|
3642
|
-
* Pure logic: no transport, no store. Consumers (the dispatcher/provider) feed live descriptors in.
|
|
3643
|
-
*/
|
|
3644
|
-
|
|
3645
|
-
/** A handler's capability demand — either a bare list, or anything carrying a `requires` array
|
|
3646
|
-
* (a workflow/step ref). An absent/empty list means "runs anywhere" (design §7.5). */
|
|
3647
|
-
type CapabilityRequirement = string[] | {
|
|
3648
|
-
requires?: string[];
|
|
3649
|
-
};
|
|
3650
|
-
/** Normalize the two accepted shapes to a de-duplicated list of required capability names. */
|
|
3651
|
-
declare function requiredCapabilities(handler: CapabilityRequirement): string[];
|
|
3652
|
-
/**
|
|
3653
|
-
* Can `worker` run a handler with these requirements? True iff the worker's (normalized) descriptor
|
|
3654
|
-
* advertises **every** required capability. A legacy worker (no `capabilities` field) is normalized
|
|
3655
|
-
* to the v1 baseline first (design §7.7), so it can still run baseline-only work.
|
|
3656
|
-
*/
|
|
3657
|
-
declare function canRoute(handler: CapabilityRequirement, worker: RawWorkerDescriptor | WorkerDescriptor): boolean;
|
|
3658
|
-
/**
|
|
3659
|
-
* The capabilities a handler requires that `worker` does NOT advertise — the precise routing delta.
|
|
3660
|
-
* Empty iff {@link canRoute} is true.
|
|
3661
|
-
*/
|
|
3662
|
-
declare function missingCapabilities(handler: CapabilityRequirement, worker: RawWorkerDescriptor | WorkerDescriptor): string[];
|
|
3663
|
-
/** Filter a fleet down to the workers that can run a handler (design §7.5). */
|
|
3664
|
-
declare function capableWorkers<T extends RawWorkerDescriptor | WorkerDescriptor>(handler: CapabilityRequirement, workers: readonly T[]): T[];
|
|
3665
|
-
/** A run is dispatchable — at least one live worker can execute the handler. */
|
|
3666
|
-
interface RoutableResolution {
|
|
3667
|
-
status: 'routable';
|
|
3668
|
-
/** The normalized subset of the fleet that can run it. */
|
|
3669
|
-
workers: WorkerDescriptor[];
|
|
3670
|
-
requires: string[];
|
|
3671
|
-
}
|
|
3672
|
-
/** No live capable worker — the run parks as `blocked` with a human reason (design §7.5). */
|
|
3673
|
-
interface BlockedResolution {
|
|
3674
|
-
status: 'blocked';
|
|
3675
|
-
/** Exactly the dashboard copy: `blocked: no compatible worker (requires <caps>)`. */
|
|
3676
|
-
reason: string;
|
|
3677
|
-
requires: string[];
|
|
3678
|
-
}
|
|
3679
|
-
type RoutingResolution = RoutableResolution | BlockedResolution;
|
|
3680
|
-
/**
|
|
3681
|
-
* Decide whether a handler can be dispatched to the given live fleet (design §7.5). Returns
|
|
3682
|
-
* `routable` with the capable subset, or `blocked` with a precise reason when nobody can run it —
|
|
3683
|
-
* so the run parks visibly instead of hanging silently. An empty fleet always blocks.
|
|
3684
|
-
*/
|
|
3685
|
-
declare function resolveRouting(handler: CapabilityRequirement, workers: readonly (RawWorkerDescriptor | WorkerDescriptor)[]): RoutingResolution;
|
|
3686
|
-
|
|
3687
4228
|
/**
|
|
3688
4229
|
* Wrappers around the **real execution** of a unit of durable work — a workflow turn, or a step
|
|
3689
4230
|
* handler body. Distinct from {@link StepInterceptor} (`engine.use`), which only sees the in-process
|
|
@@ -4423,4 +4964,4 @@ declare class InMemoryTransport implements Transport, ControlPlane {
|
|
|
4423
4964
|
onControl(handler: (msg: ControlMessage) => void): void;
|
|
4424
4965
|
}
|
|
4425
4966
|
|
|
4426
|
-
export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, type BlockedDispatch, type BlockedResolution, CONTROL_FLOW_SIGNAL, CURRENT_PROTOCOL_VERSION, type CapabilityRequirement, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, type DispatchDiagnostics, type DispatchPlan, type DurableExecution, type DurableExecutionWrapper, type DurableTopology, type DurableWebhook, DurableWorkflow, EVENT_BUFFER_SCAN_LIMIT, EVENT_TOKEN_PREFIX, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, EventAccumulators, type EventBatchConfig, FatalError, GatherError, type GroupHealth, type Heartbeat, type HeartbeatStatus, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type InferSearchAttributes, LEGACY_V1_CAPABILITIES, LEGACY_V1_PROTOCOL, type MetricsCollector, type MetricsSnapshot, type NamedTransport, type NegotiateOptions, type NegotiationOutcome, type NegotiationReason, type NegotiationResult, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, type RawWorkerDescriptor, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RetentionPolicy, type RoutableDispatch, type RoutableResolution, type RoutingResolution, type RunAttributeRow, type RunDetail, type RunDispatcher, RunGateway, type RunListItem, type RunQuery, type RunReply, type RunReplyResult, type RunRequest, type RunRequestKind, type RunResult, type RunSchedulesOptions, type RunStatus, type RunWaiting, STATE_STORE, STATE_STORE_CANONICAL, type ScheduledWorkflow, type SearchAttributes, type SearchAttributesSchema, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, StandardSchemaV1, type StartOptions, type StartRunMessage, type StateStore, type StepCheckpoint, type StepConfig, type StepDef, type StepDispatchOpts, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepRef, type StepResult, type StepUndo, type StoreTransaction, type SubProcessHandle, TERMINAL_RUN_STATUSES, TRANSPORT, TRANSPORT_CANONICAL, type TenantEvent, type Transport, type UndoOf, type UpdateResult, type UpdateValidator, WORKFLOW_NAME_KEY, type WireDates, type WorkerAdjust, type WorkerConcurrencyStatus, type WorkerDescriptor, type WorkerHeartbeat, type WorkerLifecycle, type WorkerStatus, WorkflowBlocked, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowHandler, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRun, type WorkflowStartClient, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, applyAttributeQuery, attributeColumnFor, attributeOperand, bindWorkflowClass, breakpointToken, canRoute, capableWorkers, classifyWaiterToken, clearDurableExecutionWrappers, collectMetrics, controlPlaneDescriptor, createStepLogger, currentStep, currentWorkflowCtx, debug, descriptorHash, error, eventMatchOf, eventMatches, eventNameOf, eventPrefix, eventToken, hasDurableExecutionWrappers, heartbeatStatus, indexWaitersByRun, info, isEventToken, isLegacyDescriptor, isWorkflowControlFlowSignal, log, matchesAttributes, missingCapabilities, negotiate, normalizeAttributeRows, normalizeDescriptor, parseDuration, planDispatch, prevCronFireMs, readSearchAttributes, requiredCapabilities, resolveRouting, resolveRunWaiting, runDurableExecution, runInStepLogger, runInWorkflowCtx, runSchedules, runStepHandler, sanitizeQueueToken, scheduledRunId, sqlComparator, stepConfigOf, stepId, stepNameOf, sub, subEvent, subProcess, tenantGroup, useDurableExecution, warn, workflowName };
|
|
4967
|
+
export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AnnouncedWorkflow, type AnnouncementEvidence, type AttributeFilter, type AttributeOp, type BackoffStrategy, type BlockedDispatch, type BlockedResolution, CONTROL_FLOW_SIGNAL, CURRENT_PROTOCOL_VERSION, type CapabilityRequirement, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DEFAULT_PARTITION, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, type Disagreement, type DispatchDiagnostics, type DispatchPlan, type DurableExecution, type DurableExecutionWrapper, type DurableTopology, type DurableWebhook, DurableWorkflow, EVENT_BUFFER_SCAN_LIMIT, EVENT_TOKEN_PREFIX, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, EventAccumulators, type EventBatchConfig, FatalError, GatherError, type GroupHealth, type Heartbeat, type HeartbeatSighting, type HeartbeatStatus, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type InferSearchAttributes, LEGACY_V1_CAPABILITIES, LEGACY_V1_PROTOCOL, type MetricsCollector, type MetricsSnapshot, type NamedTransport, type NegotiateOptions, type NegotiationOutcome, type NegotiationReason, type NegotiationResult, NonDeterminismError, type PartitionSighting, type PayloadCodec, type QueueConfig, QueueController, type RawWorkerDescriptor, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RetentionPolicy, type RoutableDispatch, type RoutableResolution, type RoutingResolution, type RunAttributeRow, type RunDetail, type RunDispatcher, RunGateway, type RunListItem, type RunQuery, type RunReply, type RunReplyResult, type RunRequest, type RunRequestKind, type RunResult, type RunSchedulesOptions, type RunStatus, type RunWaiting, STATE_STORE, STATE_STORE_CANONICAL, type ScheduledWorkflow, type SearchAttributes, type SearchAttributesSchema, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, StandardSchemaV1, type StartOptions, type StartRunMessage, type StateStore, type StepCheckpoint, type StepConfig, type StepDef, type StepDispatchOpts, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepRef, type StepResult, type StepUndo, type StoreTransaction, type SubProcessHandle, TERMINAL_RUN_STATUSES, TRANSPORT, TRANSPORT_CANONICAL, type TenantEvent, type Transport, type UndoOf, type UpdateResult, type UpdateValidator, VERSION_UNDECLARED_TAG, WORKFLOW_NAME_KEY, type WireDates, type WorkerAdjust, type WorkerConcurrencyStatus, type WorkerDescriptor, type WorkerHeartbeat, type WorkerLifecycle, type WorkerStatus, type WorkflowAnnouncement, WorkflowBlocked, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, type WorkflowDirectory, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowHandler, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRegistration, type WorkflowRun, type WorkflowStartClient, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, aggregateAnnouncements, announcementsOf, applyAttributeQuery, attributeColumnFor, attributeOperand, bindWorkflowClass, breakpointToken, buildWorkflowDirectory, canRoute, capableWorkers, classifyWaiterToken, clearDurableExecutionWrappers, collectMetrics, controlPlaneDescriptor, coveredTokens, createStepLogger, currentStep, currentWorkflowCtx, debug, describeWorker, descriptorHash, error, eventMatchOf, eventMatches, eventNameOf, eventPrefix, eventToken, hasDurableExecutionWrappers, heartbeatStatus, indexWaitersByRun, info, isEventToken, isLegacyDescriptor, isWorkflowControlFlowSignal, log, matchesAttributes, missingCapabilities, negotiate, normalizeAttributeRows, normalizeDescriptor, observedAnnouncements, parseDuration, partitionOfToken, planDispatch, prevCronFireMs, readSearchAttributes, requiredCapabilities, resolveRouting, resolveRunWaiting, runDurableExecution, runInStepLogger, runInWorkflowCtx, runSchedules, runStepHandler, sanitizeQueueToken, scheduledRunId, sqlComparator, stepConfigOf, stepId, stepNameOf, sub, subEvent, subProcess, tenantGroup, useDurableExecution, warn, workflowName };
|