@dudousxd/nestjs-durable-core 0.52.0 → 0.54.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 +330 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +187 -1
- package/dist/index.d.ts +187 -1
- package/dist/index.js +327 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -628,6 +628,16 @@ interface StateStore {
|
|
|
628
628
|
takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
|
|
629
629
|
/** List waiters whose `token` starts with `prefix` — used to fan out an event to its subscribers. */
|
|
630
630
|
listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
|
|
631
|
+
/**
|
|
632
|
+
* Delete the EXACT waiter row — `token` AND `runId` AND `seq` must all match — no-op if absent.
|
|
633
|
+
* Unlike {@link takeSignalWaiter}, which deletes ANY row for `token` (fine when the caller just won
|
|
634
|
+
* the race to consume it), this is for a caller removing its OWN registration after resolving the
|
|
635
|
+
* wait some other way (a buffered hit, a timeout): blind `takeSignalWaiter(token)` there could steal
|
|
636
|
+
* a DIFFERENT run's waiter that has since claimed the same token (`token` is the store's primary key,
|
|
637
|
+
* so a later `putSignalWaiter` for the same token replaces the row). The exact-match variant only
|
|
638
|
+
* ever removes the row this caller itself put there.
|
|
639
|
+
*/
|
|
640
|
+
removeSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
631
641
|
/**
|
|
632
642
|
* Buffer a signal whose waiter hasn't arrived yet, so the next `waitForSignal(token)` consumes it
|
|
633
643
|
* instead of it being lost (FIFO per token). Makes signals reliable regardless of timing and
|
|
@@ -638,6 +648,39 @@ interface StateStore {
|
|
|
638
648
|
takeBufferedSignal(token: string): Promise<{
|
|
639
649
|
payload: unknown;
|
|
640
650
|
} | null>;
|
|
651
|
+
/**
|
|
652
|
+
* Buffer a published event that matched NO live waiter, so a LATER `ctx.waitForEvent(name, { match })`
|
|
653
|
+
* still consumes it instead of it being silently dropped — the events analog of {@link bufferSignal}'s
|
|
654
|
+
* reliability contract for signals, but MATCH-based rather than token-based: an event's buffer is keyed
|
|
655
|
+
* by `name` alone (many waiters can share a name with different `match` criteria), so consumption is
|
|
656
|
+
* list ({@link listBufferedEvents}) + evaluate the WAITER's own match predicate + claim
|
|
657
|
+
* ({@link removeBufferedEvent}) — never a blind take, because the store has no way to know which
|
|
658
|
+
* candidate a given waiter wants. `input.id` is minted by the caller (engine.publishEvent) so
|
|
659
|
+
* {@link removeBufferedEvent} can later target this exact row.
|
|
660
|
+
*/
|
|
661
|
+
bufferEvent(input: {
|
|
662
|
+
name: string;
|
|
663
|
+
payload: unknown;
|
|
664
|
+
id: string;
|
|
665
|
+
publishedAt: number;
|
|
666
|
+
}): Promise<void>;
|
|
667
|
+
/**
|
|
668
|
+
* Buffered events for `name`, OLDEST (`publishedAt`) first, capped at `limit`. A waiter scans these,
|
|
669
|
+
* evaluates its own `eventMatches(payload, match)` locally, and claims the one it wants via
|
|
670
|
+
* {@link removeBufferedEvent} — the match predicate belongs to the WAITER, never the store.
|
|
671
|
+
*/
|
|
672
|
+
listBufferedEvents(name: string, limit: number): Promise<Array<{
|
|
673
|
+
id: string;
|
|
674
|
+
payload: unknown;
|
|
675
|
+
publishedAt: number;
|
|
676
|
+
}>>;
|
|
677
|
+
/**
|
|
678
|
+
* Atomically delete the buffered event `id`. Returns `true` iff a row was actually deleted, `false`
|
|
679
|
+
* if it was already gone (claimed by a concurrent waiter, reclaimed by `engine.publishEvent`'s own
|
|
680
|
+
* late re-check, or pruned as expired) — the arbiter under concurrency: whichever caller's delete
|
|
681
|
+
* returns `true` is the one that gets to deliver the payload; every other caller backs off.
|
|
682
|
+
*/
|
|
683
|
+
removeBufferedEvent(id: string): Promise<boolean>;
|
|
641
684
|
/**
|
|
642
685
|
* Run `work` in a SINGLE store transaction — giving it the store-native transaction handle (`raw`)
|
|
643
686
|
* for the caller's own DB writes plus a `saveCheckpoint` that commits IN THE SAME transaction, so a
|
|
@@ -1997,6 +2040,19 @@ interface WorkflowEngineDeps {
|
|
|
1997
2040
|
* for a genuinely-orphaned run. Default 5min; set `0` to disable (the prior wake-forever behavior).
|
|
1998
2041
|
*/
|
|
1999
2042
|
reconcileMs?: number | undefined;
|
|
2043
|
+
/**
|
|
2044
|
+
* Retention for a BUFFERED event that no `waitForEvent` ever claims (see
|
|
2045
|
+
* {@link WorkflowEngine.publishEvent}'s buffering — a publish matching no live waiter keeps ONE
|
|
2046
|
+
* copy around for a later matching wait). Default `undefined`: keep it until consumed, same as
|
|
2047
|
+
* {@link StateStore.bufferSignal}'s buffered signals — no silent expiry. Set a duration (ms) to
|
|
2048
|
+
* have the {@link resumeDueTimers} reconcile pass prune rows past `publishedAt + eventBufferTtlMs`
|
|
2049
|
+
* as it encounters them. Scoped to event NAMES the current reconcile tick's registered waiters
|
|
2050
|
+
* reference (the sweep has no reason to otherwise enumerate every buffered name) — a name with a
|
|
2051
|
+
* stale buffered event but zero waiters ever registered against it is not visited by this pass; that
|
|
2052
|
+
* is an accepted gap given `listBufferedEvents` is name-scoped by design (see interfaces.ts), not a
|
|
2053
|
+
* bug to route around here.
|
|
2054
|
+
*/
|
|
2055
|
+
eventBufferTtlMs?: number | undefined;
|
|
2000
2056
|
/**
|
|
2001
2057
|
* Opt-in self-heal window (ms) for a remote step with NO `timeoutMs` whose dispatched job was LOST —
|
|
2002
2058
|
* the worker crashed mid-step leaving no result, or the transport dropped the job (a Redis
|
|
@@ -2094,6 +2150,7 @@ declare class WorkflowEngine {
|
|
|
2094
2150
|
private readonly maxRecoveryAttempts?;
|
|
2095
2151
|
private readonly remoteAdvanceSilenceMs?;
|
|
2096
2152
|
private readonly reconcileMs;
|
|
2153
|
+
private readonly eventBufferTtlMs;
|
|
2097
2154
|
private readonly remoteRedispatchMs;
|
|
2098
2155
|
private readonly remoteRedispatchMax;
|
|
2099
2156
|
private readonly webhookUrl?;
|
|
@@ -2373,8 +2430,46 @@ declare class WorkflowEngine {
|
|
|
2373
2430
|
/**
|
|
2374
2431
|
* Resume every suspended run whose durable timer is due. Call periodically (a poller) and on
|
|
2375
2432
|
* boot. A run still not due re-suspends cheaply without running new work.
|
|
2433
|
+
*
|
|
2434
|
+
* Piggy-backs the signal/event-waiter reconcile safety net (see {@link reconcileSignalWaiter}): a
|
|
2435
|
+
* crash between the waiter/buffer pairing ops on EITHER side (this run's own `putSignalWaiter` →
|
|
2436
|
+
* `takeBufferedSignal`/`listBufferedEvents`, or the other side's `takeSignalWaiter`/fan-out →
|
|
2437
|
+
* `bufferSignal`/`bufferEvent` → re-check — see `signal`'s interleaving proof, which `publishEvent`
|
|
2438
|
+
* mirrors for events) can leave both rows stranded with nothing left to pair them: the waiter's turn
|
|
2439
|
+
* already suspended, the other side that buffered it already returned. Event-wait suspends already
|
|
2440
|
+
* carry a `reconcileMs` fallback `wakeAt` (see {@link reconcileWakeAt}) precisely so they show up
|
|
2441
|
+
* HERE — that's what makes this due-timer pass the natural place for the sweep (and, for events, also
|
|
2442
|
+
* where an `eventBufferTtlMs` prune piggy-backs — see {@link reconcileBufferedEvent}). One bulk waiter
|
|
2443
|
+
* scan, filtered to just this tick's batch, keeps it cheap regardless of store size.
|
|
2376
2444
|
*/
|
|
2377
2445
|
resumeDueTimers(nowMs?: number): Promise<RunResult[]>;
|
|
2446
|
+
/**
|
|
2447
|
+
* The reconcile safety net's per-run check (see {@link resumeDueTimers}): if this suspended run has
|
|
2448
|
+
* a registered signal OR event waiter and a matching payload is already buffered, pair and deliver
|
|
2449
|
+
* them directly instead of falling through to a full replay. Child/breakpoint waiter tokens are
|
|
2450
|
+
* never buffered (a child/breakpoint notifies via `signal` only once its counterpart is already
|
|
2451
|
+
* registered) — `takeBufferedSignal` on those is a guaranteed miss, so this only ever does real work
|
|
2452
|
+
* for a plain signal or an event waiter (classified via {@link isEventToken}).
|
|
2453
|
+
*
|
|
2454
|
+
* Also opportunistically prunes expired buffered EVENTS for this waiter's name when
|
|
2455
|
+
* {@link eventBufferTtlMs} is set (see its doc — scoped to names a registered waiter references in
|
|
2456
|
+
* this tick's batch, piggy-backing the SAME scan rather than a separate pass), even on a miss.
|
|
2457
|
+
*
|
|
2458
|
+
* Returns `undefined` (not settled — `resumeLeased` proceeds with its normal `resume`) when there's
|
|
2459
|
+
* no waiter or no buffered hit. Deliberately never returns `null`: a delivered signal always resumes
|
|
2460
|
+
* via {@link deliverSignal}, and even a `NamespaceMismatch` there is reported as a real (non-nullish)
|
|
2461
|
+
* result — returning `null` would read as "not settled" to `resumeLeased`'s `??` fallback and
|
|
2462
|
+
* trigger a second, redundant `resume` call on the same run.
|
|
2463
|
+
*/
|
|
2464
|
+
private reconcileSignalWaiter;
|
|
2465
|
+
/**
|
|
2466
|
+
* The EVENT half of {@link reconcileSignalWaiter}: scan this waiter's name for a matching buffered
|
|
2467
|
+
* event (same list+evaluate+claim shape as `waitForEvent`'s own consumption — the match predicate
|
|
2468
|
+
* belongs to the waiter, never the store), pruning any expired candidates it encounters along the
|
|
2469
|
+
* way when {@link eventBufferTtlMs} is configured. A pruned-but-non-matching candidate does not stop
|
|
2470
|
+
* the scan; only a MATCHING hit (or exhausting the scan window) does.
|
|
2471
|
+
*/
|
|
2472
|
+
private reconcileBufferedEvent;
|
|
2378
2473
|
/**
|
|
2379
2474
|
* Lease and execute one run by id — the worker side of dispatch. Acquires the recovery lease (so
|
|
2380
2475
|
* exactly one instance runs it), then resumes the body. Returns the result, or null if another
|
|
@@ -2443,10 +2538,57 @@ declare class WorkflowEngine {
|
|
|
2443
2538
|
* 2. **Starts** a fresh run of every workflow registered with `onEvent: [name]`, passing the
|
|
2444
2539
|
* payload as input. Idempotent by `evt:<id>:<workflow>` — pass `opts.id` to dedupe redeliveries
|
|
2445
2540
|
* of the same logical event (default: a fresh uuid, so each publish triggers once).
|
|
2541
|
+
*
|
|
2542
|
+
* Semantics (deliberate — mirrors `signalWithStart`'s reliability contract, so document it exactly):
|
|
2543
|
+
* a publish that resumes ≥1 live waiter, OR routes into an `eventBatch` accumulator / starts ≥1
|
|
2544
|
+
* subscriber, behaves exactly as above and is NOT buffered — fan-out stays live-only, same as
|
|
2545
|
+
* today. A publish that touches NEITHER — no live waiter matched, no subscriber exists/started —
|
|
2546
|
+
* buffers ONE copy (via {@link StateStore.bufferEvent}) unless `opts.buffer === false`, so it isn't
|
|
2547
|
+
* silently dropped just because nobody happened to be listening yet. That buffered copy is consumed
|
|
2548
|
+
* by the FIRST future `ctx.waitForEvent(name, { match })` whose match accepts its payload —
|
|
2549
|
+
* point-to-point on redelivery, by design (only one waiter ever gets it, even though the LIVE path
|
|
2550
|
+
* above is fan-out) — never by a second, later-registered onEvent subscriber (subscriber starts stay
|
|
2551
|
+
* live-only; `opts.id` dedupe applies to those only, unchanged). Right after buffering, this re-checks
|
|
2552
|
+
* `listSignalWaiters` ONCE — sandwich parity with `signal`'s own take → buffer → re-check — so a
|
|
2553
|
+
* waiter that registers in the sliver between the initial miss and the buffer write is still paired
|
|
2554
|
+
* instead of leaving both rows stranded (see `signal`'s interleaving proof; `waitForEvent`'s own
|
|
2555
|
+
* post-registration buffer scan is the mirror-image half of this same race).
|
|
2446
2556
|
*/
|
|
2447
2557
|
publishEvent(name: string, payload: unknown, opts?: {
|
|
2448
2558
|
id?: string;
|
|
2559
|
+
buffer?: boolean;
|
|
2449
2560
|
}): Promise<number>;
|
|
2561
|
+
/**
|
|
2562
|
+
* Deliver a signal to an already-known waiter: write its resolving `signal:<token>` checkpoint and
|
|
2563
|
+
* resume the run. Shared by {@link signal}'s direct hit, its post-buffer re-check reclaim, and the
|
|
2564
|
+
* due-timer reconcile sweep ({@link reconcileSignalWaiter}) — all three resolve a waiter the exact
|
|
2565
|
+
* same way.
|
|
2566
|
+
*/
|
|
2567
|
+
private deliverSignal;
|
|
2568
|
+
/**
|
|
2569
|
+
* Deliver an external signal to the run waiting on `token`, resume it with `payload`, and return
|
|
2570
|
+
* the run result — or null if no run is (or ends up) waiting for it.
|
|
2571
|
+
*
|
|
2572
|
+
* Race with the waiter side (`waitForSignal`'s check → `putSignalWaiter` → re-check, same shape in
|
|
2573
|
+
* the remote `waitSignal` command): a waiter can register in the sliver between this method's
|
|
2574
|
+
* initial `takeSignalWaiter` miss and its `bufferSignal` write, and the naive old flow (buffer and
|
|
2575
|
+
* return) would then leave BOTH a buffered payload and a registered waiter sitting in the store with
|
|
2576
|
+
* nothing left to pair them — a lost wake, forever suspended.
|
|
2577
|
+
*
|
|
2578
|
+
* The interleaving proof: the waiter side does check → put → re-check, this side does take → buffer
|
|
2579
|
+
* → re-check. Whichever side's SECOND look runs last is guaranteed to observe the other side's
|
|
2580
|
+
* write, because it always follows that write in real time:
|
|
2581
|
+
* - If the waiter's `put` lands before this method's first `take`, the take just finds it directly
|
|
2582
|
+
* (the pre-existing, non-racy path).
|
|
2583
|
+
* - If the waiter's `put` lands after this method's `bufferSignal`, our re-check below either sees
|
|
2584
|
+
* it (if `put` also lands before the re-check) or the waiter's OWN post-put re-check sees our
|
|
2585
|
+
* buffer (if `put` lands after our re-check but the waiter hasn't re-checked yet either — its
|
|
2586
|
+
* re-check strictly follows its own put).
|
|
2587
|
+
* - If the waiter's `put` lands between this method's `take` and its re-`take`, the re-check below
|
|
2588
|
+
* catches it.
|
|
2589
|
+
* Either way, the destructive `take*` ops (on the waiter row, on the buffer) arbitrate: whichever
|
|
2590
|
+
* side actually removes the row is the one that delivers, so a signal is never delivered twice.
|
|
2591
|
+
*/
|
|
2450
2592
|
signal(token: string, payload: unknown): Promise<RunResult | null>;
|
|
2451
2593
|
/**
|
|
2452
2594
|
* Ensure a run exists for `runId`, then deliver a signal to it — atomically race-free thanks to
|
|
@@ -3155,18 +3297,46 @@ declare class CodecStateStore implements StateStore {
|
|
|
3155
3297
|
putSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
3156
3298
|
takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
|
|
3157
3299
|
listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
|
|
3300
|
+
removeSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
3158
3301
|
bufferSignal(token: string, payload: unknown): Promise<void>;
|
|
3159
3302
|
takeBufferedSignal(token: string): Promise<{
|
|
3160
3303
|
payload: unknown;
|
|
3161
3304
|
} | null>;
|
|
3305
|
+
bufferEvent(input: {
|
|
3306
|
+
name: string;
|
|
3307
|
+
payload: unknown;
|
|
3308
|
+
id: string;
|
|
3309
|
+
publishedAt: number;
|
|
3310
|
+
}): Promise<void>;
|
|
3311
|
+
listBufferedEvents(name: string, limit: number): Promise<Array<{
|
|
3312
|
+
id: string;
|
|
3313
|
+
payload: unknown;
|
|
3314
|
+
publishedAt: number;
|
|
3315
|
+
}>>;
|
|
3316
|
+
removeBufferedEvent(id: string): Promise<boolean>;
|
|
3162
3317
|
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
3163
3318
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
3164
3319
|
getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
3165
3320
|
listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
3166
3321
|
}
|
|
3167
3322
|
|
|
3323
|
+
/**
|
|
3324
|
+
* How many candidates a buffered-event scan considers (`listBufferedEvents(name, limit)`) before
|
|
3325
|
+
* giving up. An event's buffer is keyed by `name` only (see {@link eventMatches}'s match-based
|
|
3326
|
+
* consumption), so a hot name with many distinct payloads sitting unclaimed could in principle
|
|
3327
|
+
* accumulate an unbounded backlog; this caps the per-scan cost regardless. Shared by the workflow-ctx
|
|
3328
|
+
* consumer and the engine's reconcile sweep so both see the same window.
|
|
3329
|
+
*/
|
|
3330
|
+
declare const EVENT_BUFFER_SCAN_LIMIT = 50;
|
|
3331
|
+
/** The literal prefix EVERY event-waiter token starts with, regardless of `name` — `eventPrefix(name)`
|
|
3332
|
+
* always begins with this; exported so a caller that only needs to CLASSIFY a token (not fan out a
|
|
3333
|
+
* specific name) doesn't have to re-derive or duplicate the literal (see run-waiting.ts's own copy,
|
|
3334
|
+
* kept in sync with this one, and engine.ts's reconcile sweep). */
|
|
3335
|
+
declare const EVENT_TOKEN_PREFIX = "event:";
|
|
3168
3336
|
/** Token prefix for every waiter on `name` — used by `listSignalWaiters` to fan an event out. */
|
|
3169
3337
|
declare function eventPrefix(name: string): string;
|
|
3338
|
+
/** Whether `token` is an event-waiter token (vs. a plain signal / child / webhook / breakpoint one). */
|
|
3339
|
+
declare function isEventToken(token: string): boolean;
|
|
3170
3340
|
/** Build the unique waiter token for one `waitForEvent` call (name + match + run position). */
|
|
3171
3341
|
declare function eventToken(name: string, match: Record<string, unknown> | undefined, runId: string, seq: number): string;
|
|
3172
3342
|
/** The event name embedded in an event waiter token (the 2nd `:`-segment is the base64 name), so a
|
|
@@ -3522,6 +3692,7 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
3522
3692
|
putSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
3523
3693
|
takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
|
|
3524
3694
|
listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
|
|
3695
|
+
removeSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
3525
3696
|
transaction<T>(work: (tx: {
|
|
3526
3697
|
raw: unknown;
|
|
3527
3698
|
saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;
|
|
@@ -3531,6 +3702,21 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
3531
3702
|
takeBufferedSignal(token: string): Promise<{
|
|
3532
3703
|
payload: unknown;
|
|
3533
3704
|
} | null>;
|
|
3705
|
+
/** `id -> row`, so {@link removeBufferedEvent} can delete an exact row regardless of iteration
|
|
3706
|
+
* order — mirrors the SQL stores' PK-by-id delete. */
|
|
3707
|
+
private readonly bufferedEvents;
|
|
3708
|
+
bufferEvent(input: {
|
|
3709
|
+
name: string;
|
|
3710
|
+
payload: unknown;
|
|
3711
|
+
id: string;
|
|
3712
|
+
publishedAt: number;
|
|
3713
|
+
}): Promise<void>;
|
|
3714
|
+
listBufferedEvents(name: string, limit: number): Promise<Array<{
|
|
3715
|
+
id: string;
|
|
3716
|
+
payload: unknown;
|
|
3717
|
+
publishedAt: number;
|
|
3718
|
+
}>>;
|
|
3719
|
+
removeBufferedEvent(id: string): Promise<boolean>;
|
|
3534
3720
|
/**
|
|
3535
3721
|
* Candidate run ids satisfying ONE attribute predicate, served from {@link attributeIndex} — the
|
|
3536
3722
|
* pushdown step. Numeric predicates scan only `numValue` rows under the key; string/boolean ones
|
|
@@ -3569,4 +3755,4 @@ declare class InMemoryTransport implements Transport, ControlPlane {
|
|
|
3569
3755
|
onControl(handler: (msg: ControlMessage) => void): void;
|
|
3570
3756
|
}
|
|
3571
3757
|
|
|
3572
|
-
export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, CONTROL_FLOW_SIGNAL, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, type DurableTopology, type DurableWebhook, DurableWorkflow, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, EventAccumulators, type EventBatchConfig, FatalError, GatherError, type GroupHealth, type Heartbeat, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type InferSearchAttributes, type MetricsCollector, type MetricsSnapshot, type NamedTransport, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RetentionPolicy, type RunAttributeRow, type RunDetail, type RunDispatcher, type 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 WorkerHeartbeat, type WorkerStatus, 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, classifyWaiterToken, collectMetrics, createStepLogger, currentWorkflowCtx, eventMatchOf, eventMatches, eventNameOf, eventPrefix, eventToken, indexWaitersByRun, isWorkflowControlFlowSignal, matchesAttributes, normalizeAttributeRows, parseDuration, prevCronFireMs, readSearchAttributes, resolveRunWaiting, runInWorkflowCtx, runSchedules, runStepHandler, sanitizeQueueToken, scheduledRunId, sqlComparator, stepConfigOf, stepId, stepNameOf, tenantGroup, workflowName };
|
|
3758
|
+
export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, CONTROL_FLOW_SIGNAL, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, 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 HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type InferSearchAttributes, type MetricsCollector, type MetricsSnapshot, type NamedTransport, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RetentionPolicy, type RunAttributeRow, type RunDetail, type RunDispatcher, type 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 WorkerHeartbeat, type WorkerStatus, 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, classifyWaiterToken, collectMetrics, createStepLogger, currentWorkflowCtx, eventMatchOf, eventMatches, eventNameOf, eventPrefix, eventToken, indexWaitersByRun, isEventToken, isWorkflowControlFlowSignal, matchesAttributes, normalizeAttributeRows, parseDuration, prevCronFireMs, readSearchAttributes, resolveRunWaiting, runInWorkflowCtx, runSchedules, runStepHandler, sanitizeQueueToken, scheduledRunId, sqlComparator, stepConfigOf, stepId, stepNameOf, tenantGroup, workflowName };
|
package/dist/index.d.ts
CHANGED
|
@@ -628,6 +628,16 @@ interface StateStore {
|
|
|
628
628
|
takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
|
|
629
629
|
/** List waiters whose `token` starts with `prefix` — used to fan out an event to its subscribers. */
|
|
630
630
|
listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
|
|
631
|
+
/**
|
|
632
|
+
* Delete the EXACT waiter row — `token` AND `runId` AND `seq` must all match — no-op if absent.
|
|
633
|
+
* Unlike {@link takeSignalWaiter}, which deletes ANY row for `token` (fine when the caller just won
|
|
634
|
+
* the race to consume it), this is for a caller removing its OWN registration after resolving the
|
|
635
|
+
* wait some other way (a buffered hit, a timeout): blind `takeSignalWaiter(token)` there could steal
|
|
636
|
+
* a DIFFERENT run's waiter that has since claimed the same token (`token` is the store's primary key,
|
|
637
|
+
* so a later `putSignalWaiter` for the same token replaces the row). The exact-match variant only
|
|
638
|
+
* ever removes the row this caller itself put there.
|
|
639
|
+
*/
|
|
640
|
+
removeSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
631
641
|
/**
|
|
632
642
|
* Buffer a signal whose waiter hasn't arrived yet, so the next `waitForSignal(token)` consumes it
|
|
633
643
|
* instead of it being lost (FIFO per token). Makes signals reliable regardless of timing and
|
|
@@ -638,6 +648,39 @@ interface StateStore {
|
|
|
638
648
|
takeBufferedSignal(token: string): Promise<{
|
|
639
649
|
payload: unknown;
|
|
640
650
|
} | null>;
|
|
651
|
+
/**
|
|
652
|
+
* Buffer a published event that matched NO live waiter, so a LATER `ctx.waitForEvent(name, { match })`
|
|
653
|
+
* still consumes it instead of it being silently dropped — the events analog of {@link bufferSignal}'s
|
|
654
|
+
* reliability contract for signals, but MATCH-based rather than token-based: an event's buffer is keyed
|
|
655
|
+
* by `name` alone (many waiters can share a name with different `match` criteria), so consumption is
|
|
656
|
+
* list ({@link listBufferedEvents}) + evaluate the WAITER's own match predicate + claim
|
|
657
|
+
* ({@link removeBufferedEvent}) — never a blind take, because the store has no way to know which
|
|
658
|
+
* candidate a given waiter wants. `input.id` is minted by the caller (engine.publishEvent) so
|
|
659
|
+
* {@link removeBufferedEvent} can later target this exact row.
|
|
660
|
+
*/
|
|
661
|
+
bufferEvent(input: {
|
|
662
|
+
name: string;
|
|
663
|
+
payload: unknown;
|
|
664
|
+
id: string;
|
|
665
|
+
publishedAt: number;
|
|
666
|
+
}): Promise<void>;
|
|
667
|
+
/**
|
|
668
|
+
* Buffered events for `name`, OLDEST (`publishedAt`) first, capped at `limit`. A waiter scans these,
|
|
669
|
+
* evaluates its own `eventMatches(payload, match)` locally, and claims the one it wants via
|
|
670
|
+
* {@link removeBufferedEvent} — the match predicate belongs to the WAITER, never the store.
|
|
671
|
+
*/
|
|
672
|
+
listBufferedEvents(name: string, limit: number): Promise<Array<{
|
|
673
|
+
id: string;
|
|
674
|
+
payload: unknown;
|
|
675
|
+
publishedAt: number;
|
|
676
|
+
}>>;
|
|
677
|
+
/**
|
|
678
|
+
* Atomically delete the buffered event `id`. Returns `true` iff a row was actually deleted, `false`
|
|
679
|
+
* if it was already gone (claimed by a concurrent waiter, reclaimed by `engine.publishEvent`'s own
|
|
680
|
+
* late re-check, or pruned as expired) — the arbiter under concurrency: whichever caller's delete
|
|
681
|
+
* returns `true` is the one that gets to deliver the payload; every other caller backs off.
|
|
682
|
+
*/
|
|
683
|
+
removeBufferedEvent(id: string): Promise<boolean>;
|
|
641
684
|
/**
|
|
642
685
|
* Run `work` in a SINGLE store transaction — giving it the store-native transaction handle (`raw`)
|
|
643
686
|
* for the caller's own DB writes plus a `saveCheckpoint` that commits IN THE SAME transaction, so a
|
|
@@ -1997,6 +2040,19 @@ interface WorkflowEngineDeps {
|
|
|
1997
2040
|
* for a genuinely-orphaned run. Default 5min; set `0` to disable (the prior wake-forever behavior).
|
|
1998
2041
|
*/
|
|
1999
2042
|
reconcileMs?: number | undefined;
|
|
2043
|
+
/**
|
|
2044
|
+
* Retention for a BUFFERED event that no `waitForEvent` ever claims (see
|
|
2045
|
+
* {@link WorkflowEngine.publishEvent}'s buffering — a publish matching no live waiter keeps ONE
|
|
2046
|
+
* copy around for a later matching wait). Default `undefined`: keep it until consumed, same as
|
|
2047
|
+
* {@link StateStore.bufferSignal}'s buffered signals — no silent expiry. Set a duration (ms) to
|
|
2048
|
+
* have the {@link resumeDueTimers} reconcile pass prune rows past `publishedAt + eventBufferTtlMs`
|
|
2049
|
+
* as it encounters them. Scoped to event NAMES the current reconcile tick's registered waiters
|
|
2050
|
+
* reference (the sweep has no reason to otherwise enumerate every buffered name) — a name with a
|
|
2051
|
+
* stale buffered event but zero waiters ever registered against it is not visited by this pass; that
|
|
2052
|
+
* is an accepted gap given `listBufferedEvents` is name-scoped by design (see interfaces.ts), not a
|
|
2053
|
+
* bug to route around here.
|
|
2054
|
+
*/
|
|
2055
|
+
eventBufferTtlMs?: number | undefined;
|
|
2000
2056
|
/**
|
|
2001
2057
|
* Opt-in self-heal window (ms) for a remote step with NO `timeoutMs` whose dispatched job was LOST —
|
|
2002
2058
|
* the worker crashed mid-step leaving no result, or the transport dropped the job (a Redis
|
|
@@ -2094,6 +2150,7 @@ declare class WorkflowEngine {
|
|
|
2094
2150
|
private readonly maxRecoveryAttempts?;
|
|
2095
2151
|
private readonly remoteAdvanceSilenceMs?;
|
|
2096
2152
|
private readonly reconcileMs;
|
|
2153
|
+
private readonly eventBufferTtlMs;
|
|
2097
2154
|
private readonly remoteRedispatchMs;
|
|
2098
2155
|
private readonly remoteRedispatchMax;
|
|
2099
2156
|
private readonly webhookUrl?;
|
|
@@ -2373,8 +2430,46 @@ declare class WorkflowEngine {
|
|
|
2373
2430
|
/**
|
|
2374
2431
|
* Resume every suspended run whose durable timer is due. Call periodically (a poller) and on
|
|
2375
2432
|
* boot. A run still not due re-suspends cheaply without running new work.
|
|
2433
|
+
*
|
|
2434
|
+
* Piggy-backs the signal/event-waiter reconcile safety net (see {@link reconcileSignalWaiter}): a
|
|
2435
|
+
* crash between the waiter/buffer pairing ops on EITHER side (this run's own `putSignalWaiter` →
|
|
2436
|
+
* `takeBufferedSignal`/`listBufferedEvents`, or the other side's `takeSignalWaiter`/fan-out →
|
|
2437
|
+
* `bufferSignal`/`bufferEvent` → re-check — see `signal`'s interleaving proof, which `publishEvent`
|
|
2438
|
+
* mirrors for events) can leave both rows stranded with nothing left to pair them: the waiter's turn
|
|
2439
|
+
* already suspended, the other side that buffered it already returned. Event-wait suspends already
|
|
2440
|
+
* carry a `reconcileMs` fallback `wakeAt` (see {@link reconcileWakeAt}) precisely so they show up
|
|
2441
|
+
* HERE — that's what makes this due-timer pass the natural place for the sweep (and, for events, also
|
|
2442
|
+
* where an `eventBufferTtlMs` prune piggy-backs — see {@link reconcileBufferedEvent}). One bulk waiter
|
|
2443
|
+
* scan, filtered to just this tick's batch, keeps it cheap regardless of store size.
|
|
2376
2444
|
*/
|
|
2377
2445
|
resumeDueTimers(nowMs?: number): Promise<RunResult[]>;
|
|
2446
|
+
/**
|
|
2447
|
+
* The reconcile safety net's per-run check (see {@link resumeDueTimers}): if this suspended run has
|
|
2448
|
+
* a registered signal OR event waiter and a matching payload is already buffered, pair and deliver
|
|
2449
|
+
* them directly instead of falling through to a full replay. Child/breakpoint waiter tokens are
|
|
2450
|
+
* never buffered (a child/breakpoint notifies via `signal` only once its counterpart is already
|
|
2451
|
+
* registered) — `takeBufferedSignal` on those is a guaranteed miss, so this only ever does real work
|
|
2452
|
+
* for a plain signal or an event waiter (classified via {@link isEventToken}).
|
|
2453
|
+
*
|
|
2454
|
+
* Also opportunistically prunes expired buffered EVENTS for this waiter's name when
|
|
2455
|
+
* {@link eventBufferTtlMs} is set (see its doc — scoped to names a registered waiter references in
|
|
2456
|
+
* this tick's batch, piggy-backing the SAME scan rather than a separate pass), even on a miss.
|
|
2457
|
+
*
|
|
2458
|
+
* Returns `undefined` (not settled — `resumeLeased` proceeds with its normal `resume`) when there's
|
|
2459
|
+
* no waiter or no buffered hit. Deliberately never returns `null`: a delivered signal always resumes
|
|
2460
|
+
* via {@link deliverSignal}, and even a `NamespaceMismatch` there is reported as a real (non-nullish)
|
|
2461
|
+
* result — returning `null` would read as "not settled" to `resumeLeased`'s `??` fallback and
|
|
2462
|
+
* trigger a second, redundant `resume` call on the same run.
|
|
2463
|
+
*/
|
|
2464
|
+
private reconcileSignalWaiter;
|
|
2465
|
+
/**
|
|
2466
|
+
* The EVENT half of {@link reconcileSignalWaiter}: scan this waiter's name for a matching buffered
|
|
2467
|
+
* event (same list+evaluate+claim shape as `waitForEvent`'s own consumption — the match predicate
|
|
2468
|
+
* belongs to the waiter, never the store), pruning any expired candidates it encounters along the
|
|
2469
|
+
* way when {@link eventBufferTtlMs} is configured. A pruned-but-non-matching candidate does not stop
|
|
2470
|
+
* the scan; only a MATCHING hit (or exhausting the scan window) does.
|
|
2471
|
+
*/
|
|
2472
|
+
private reconcileBufferedEvent;
|
|
2378
2473
|
/**
|
|
2379
2474
|
* Lease and execute one run by id — the worker side of dispatch. Acquires the recovery lease (so
|
|
2380
2475
|
* exactly one instance runs it), then resumes the body. Returns the result, or null if another
|
|
@@ -2443,10 +2538,57 @@ declare class WorkflowEngine {
|
|
|
2443
2538
|
* 2. **Starts** a fresh run of every workflow registered with `onEvent: [name]`, passing the
|
|
2444
2539
|
* payload as input. Idempotent by `evt:<id>:<workflow>` — pass `opts.id` to dedupe redeliveries
|
|
2445
2540
|
* of the same logical event (default: a fresh uuid, so each publish triggers once).
|
|
2541
|
+
*
|
|
2542
|
+
* Semantics (deliberate — mirrors `signalWithStart`'s reliability contract, so document it exactly):
|
|
2543
|
+
* a publish that resumes ≥1 live waiter, OR routes into an `eventBatch` accumulator / starts ≥1
|
|
2544
|
+
* subscriber, behaves exactly as above and is NOT buffered — fan-out stays live-only, same as
|
|
2545
|
+
* today. A publish that touches NEITHER — no live waiter matched, no subscriber exists/started —
|
|
2546
|
+
* buffers ONE copy (via {@link StateStore.bufferEvent}) unless `opts.buffer === false`, so it isn't
|
|
2547
|
+
* silently dropped just because nobody happened to be listening yet. That buffered copy is consumed
|
|
2548
|
+
* by the FIRST future `ctx.waitForEvent(name, { match })` whose match accepts its payload —
|
|
2549
|
+
* point-to-point on redelivery, by design (only one waiter ever gets it, even though the LIVE path
|
|
2550
|
+
* above is fan-out) — never by a second, later-registered onEvent subscriber (subscriber starts stay
|
|
2551
|
+
* live-only; `opts.id` dedupe applies to those only, unchanged). Right after buffering, this re-checks
|
|
2552
|
+
* `listSignalWaiters` ONCE — sandwich parity with `signal`'s own take → buffer → re-check — so a
|
|
2553
|
+
* waiter that registers in the sliver between the initial miss and the buffer write is still paired
|
|
2554
|
+
* instead of leaving both rows stranded (see `signal`'s interleaving proof; `waitForEvent`'s own
|
|
2555
|
+
* post-registration buffer scan is the mirror-image half of this same race).
|
|
2446
2556
|
*/
|
|
2447
2557
|
publishEvent(name: string, payload: unknown, opts?: {
|
|
2448
2558
|
id?: string;
|
|
2559
|
+
buffer?: boolean;
|
|
2449
2560
|
}): Promise<number>;
|
|
2561
|
+
/**
|
|
2562
|
+
* Deliver a signal to an already-known waiter: write its resolving `signal:<token>` checkpoint and
|
|
2563
|
+
* resume the run. Shared by {@link signal}'s direct hit, its post-buffer re-check reclaim, and the
|
|
2564
|
+
* due-timer reconcile sweep ({@link reconcileSignalWaiter}) — all three resolve a waiter the exact
|
|
2565
|
+
* same way.
|
|
2566
|
+
*/
|
|
2567
|
+
private deliverSignal;
|
|
2568
|
+
/**
|
|
2569
|
+
* Deliver an external signal to the run waiting on `token`, resume it with `payload`, and return
|
|
2570
|
+
* the run result — or null if no run is (or ends up) waiting for it.
|
|
2571
|
+
*
|
|
2572
|
+
* Race with the waiter side (`waitForSignal`'s check → `putSignalWaiter` → re-check, same shape in
|
|
2573
|
+
* the remote `waitSignal` command): a waiter can register in the sliver between this method's
|
|
2574
|
+
* initial `takeSignalWaiter` miss and its `bufferSignal` write, and the naive old flow (buffer and
|
|
2575
|
+
* return) would then leave BOTH a buffered payload and a registered waiter sitting in the store with
|
|
2576
|
+
* nothing left to pair them — a lost wake, forever suspended.
|
|
2577
|
+
*
|
|
2578
|
+
* The interleaving proof: the waiter side does check → put → re-check, this side does take → buffer
|
|
2579
|
+
* → re-check. Whichever side's SECOND look runs last is guaranteed to observe the other side's
|
|
2580
|
+
* write, because it always follows that write in real time:
|
|
2581
|
+
* - If the waiter's `put` lands before this method's first `take`, the take just finds it directly
|
|
2582
|
+
* (the pre-existing, non-racy path).
|
|
2583
|
+
* - If the waiter's `put` lands after this method's `bufferSignal`, our re-check below either sees
|
|
2584
|
+
* it (if `put` also lands before the re-check) or the waiter's OWN post-put re-check sees our
|
|
2585
|
+
* buffer (if `put` lands after our re-check but the waiter hasn't re-checked yet either — its
|
|
2586
|
+
* re-check strictly follows its own put).
|
|
2587
|
+
* - If the waiter's `put` lands between this method's `take` and its re-`take`, the re-check below
|
|
2588
|
+
* catches it.
|
|
2589
|
+
* Either way, the destructive `take*` ops (on the waiter row, on the buffer) arbitrate: whichever
|
|
2590
|
+
* side actually removes the row is the one that delivers, so a signal is never delivered twice.
|
|
2591
|
+
*/
|
|
2450
2592
|
signal(token: string, payload: unknown): Promise<RunResult | null>;
|
|
2451
2593
|
/**
|
|
2452
2594
|
* Ensure a run exists for `runId`, then deliver a signal to it — atomically race-free thanks to
|
|
@@ -3155,18 +3297,46 @@ declare class CodecStateStore implements StateStore {
|
|
|
3155
3297
|
putSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
3156
3298
|
takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
|
|
3157
3299
|
listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
|
|
3300
|
+
removeSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
3158
3301
|
bufferSignal(token: string, payload: unknown): Promise<void>;
|
|
3159
3302
|
takeBufferedSignal(token: string): Promise<{
|
|
3160
3303
|
payload: unknown;
|
|
3161
3304
|
} | null>;
|
|
3305
|
+
bufferEvent(input: {
|
|
3306
|
+
name: string;
|
|
3307
|
+
payload: unknown;
|
|
3308
|
+
id: string;
|
|
3309
|
+
publishedAt: number;
|
|
3310
|
+
}): Promise<void>;
|
|
3311
|
+
listBufferedEvents(name: string, limit: number): Promise<Array<{
|
|
3312
|
+
id: string;
|
|
3313
|
+
payload: unknown;
|
|
3314
|
+
publishedAt: number;
|
|
3315
|
+
}>>;
|
|
3316
|
+
removeBufferedEvent(id: string): Promise<boolean>;
|
|
3162
3317
|
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
3163
3318
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
3164
3319
|
getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
3165
3320
|
listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
3166
3321
|
}
|
|
3167
3322
|
|
|
3323
|
+
/**
|
|
3324
|
+
* How many candidates a buffered-event scan considers (`listBufferedEvents(name, limit)`) before
|
|
3325
|
+
* giving up. An event's buffer is keyed by `name` only (see {@link eventMatches}'s match-based
|
|
3326
|
+
* consumption), so a hot name with many distinct payloads sitting unclaimed could in principle
|
|
3327
|
+
* accumulate an unbounded backlog; this caps the per-scan cost regardless. Shared by the workflow-ctx
|
|
3328
|
+
* consumer and the engine's reconcile sweep so both see the same window.
|
|
3329
|
+
*/
|
|
3330
|
+
declare const EVENT_BUFFER_SCAN_LIMIT = 50;
|
|
3331
|
+
/** The literal prefix EVERY event-waiter token starts with, regardless of `name` — `eventPrefix(name)`
|
|
3332
|
+
* always begins with this; exported so a caller that only needs to CLASSIFY a token (not fan out a
|
|
3333
|
+
* specific name) doesn't have to re-derive or duplicate the literal (see run-waiting.ts's own copy,
|
|
3334
|
+
* kept in sync with this one, and engine.ts's reconcile sweep). */
|
|
3335
|
+
declare const EVENT_TOKEN_PREFIX = "event:";
|
|
3168
3336
|
/** Token prefix for every waiter on `name` — used by `listSignalWaiters` to fan an event out. */
|
|
3169
3337
|
declare function eventPrefix(name: string): string;
|
|
3338
|
+
/** Whether `token` is an event-waiter token (vs. a plain signal / child / webhook / breakpoint one). */
|
|
3339
|
+
declare function isEventToken(token: string): boolean;
|
|
3170
3340
|
/** Build the unique waiter token for one `waitForEvent` call (name + match + run position). */
|
|
3171
3341
|
declare function eventToken(name: string, match: Record<string, unknown> | undefined, runId: string, seq: number): string;
|
|
3172
3342
|
/** The event name embedded in an event waiter token (the 2nd `:`-segment is the base64 name), so a
|
|
@@ -3522,6 +3692,7 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
3522
3692
|
putSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
3523
3693
|
takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
|
|
3524
3694
|
listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
|
|
3695
|
+
removeSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
3525
3696
|
transaction<T>(work: (tx: {
|
|
3526
3697
|
raw: unknown;
|
|
3527
3698
|
saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;
|
|
@@ -3531,6 +3702,21 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
3531
3702
|
takeBufferedSignal(token: string): Promise<{
|
|
3532
3703
|
payload: unknown;
|
|
3533
3704
|
} | null>;
|
|
3705
|
+
/** `id -> row`, so {@link removeBufferedEvent} can delete an exact row regardless of iteration
|
|
3706
|
+
* order — mirrors the SQL stores' PK-by-id delete. */
|
|
3707
|
+
private readonly bufferedEvents;
|
|
3708
|
+
bufferEvent(input: {
|
|
3709
|
+
name: string;
|
|
3710
|
+
payload: unknown;
|
|
3711
|
+
id: string;
|
|
3712
|
+
publishedAt: number;
|
|
3713
|
+
}): Promise<void>;
|
|
3714
|
+
listBufferedEvents(name: string, limit: number): Promise<Array<{
|
|
3715
|
+
id: string;
|
|
3716
|
+
payload: unknown;
|
|
3717
|
+
publishedAt: number;
|
|
3718
|
+
}>>;
|
|
3719
|
+
removeBufferedEvent(id: string): Promise<boolean>;
|
|
3534
3720
|
/**
|
|
3535
3721
|
* Candidate run ids satisfying ONE attribute predicate, served from {@link attributeIndex} — the
|
|
3536
3722
|
* pushdown step. Numeric predicates scan only `numValue` rows under the key; string/boolean ones
|
|
@@ -3569,4 +3755,4 @@ declare class InMemoryTransport implements Transport, ControlPlane {
|
|
|
3569
3755
|
onControl(handler: (msg: ControlMessage) => void): void;
|
|
3570
3756
|
}
|
|
3571
3757
|
|
|
3572
|
-
export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, CONTROL_FLOW_SIGNAL, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, type DurableTopology, type DurableWebhook, DurableWorkflow, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, EventAccumulators, type EventBatchConfig, FatalError, GatherError, type GroupHealth, type Heartbeat, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type InferSearchAttributes, type MetricsCollector, type MetricsSnapshot, type NamedTransport, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RetentionPolicy, type RunAttributeRow, type RunDetail, type RunDispatcher, type 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 WorkerHeartbeat, type WorkerStatus, 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, classifyWaiterToken, collectMetrics, createStepLogger, currentWorkflowCtx, eventMatchOf, eventMatches, eventNameOf, eventPrefix, eventToken, indexWaitersByRun, isWorkflowControlFlowSignal, matchesAttributes, normalizeAttributeRows, parseDuration, prevCronFireMs, readSearchAttributes, resolveRunWaiting, runInWorkflowCtx, runSchedules, runStepHandler, sanitizeQueueToken, scheduledRunId, sqlComparator, stepConfigOf, stepId, stepNameOf, tenantGroup, workflowName };
|
|
3758
|
+
export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, CONTROL_FLOW_SIGNAL, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, 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 HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type InferSearchAttributes, type MetricsCollector, type MetricsSnapshot, type NamedTransport, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RetentionPolicy, type RunAttributeRow, type RunDetail, type RunDispatcher, type 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 WorkerHeartbeat, type WorkerStatus, 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, classifyWaiterToken, collectMetrics, createStepLogger, currentWorkflowCtx, eventMatchOf, eventMatches, eventNameOf, eventPrefix, eventToken, indexWaitersByRun, isEventToken, isWorkflowControlFlowSignal, matchesAttributes, normalizeAttributeRows, parseDuration, prevCronFireMs, readSearchAttributes, resolveRunWaiting, runInWorkflowCtx, runSchedules, runStepHandler, sanitizeQueueToken, scheduledRunId, sqlComparator, stepConfigOf, stepId, stepNameOf, tenantGroup, workflowName };
|