@lostgradient/weft 0.19.0 → 0.21.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/README.md +2 -2
- package/dist/cli/generated/operation-catalog.snapshot.json +1334 -0
- package/dist/cli/generated/operation-client.generated.d.ts +9 -2
- package/dist/cli/generated/operation-client.generated.js +2 -0
- package/dist/core/engine/activity-reconciliation.d.ts +2 -2
- package/dist/core/engine/activity-reconciliation.js +12 -8
- package/dist/core/engine/async-activity-completion.js +12 -4
- package/dist/core/engine/async-activity-records.d.ts +11 -1
- package/dist/core/engine/async-activity-records.js +4 -3
- package/dist/core/engine/attributes-tags.d.ts +11 -1
- package/dist/core/engine/attributes-tags.js +4 -3
- package/dist/core/engine/bulk-operations-purge.js +5 -2
- package/dist/core/engine/bulk-operations.js +17 -5
- package/dist/core/engine/callback-creators-bundles.js +1 -1
- package/dist/core/engine/callback-creators-core.js +4 -2
- package/dist/core/engine/checkpoint-io.js +1 -1
- package/dist/core/engine/child-workflow.js +2 -1
- package/dist/core/engine/completed-review-storage.js +1 -1
- package/dist/core/engine/condition-waiters.d.ts +45 -3
- package/dist/core/engine/condition-waiters.js +12 -3
- package/dist/core/engine/disposal.js +6 -0
- package/dist/core/engine/engine-internal-types.d.ts +12 -4
- package/dist/core/engine/fenced-write.d.ts +46 -15
- package/dist/core/engine/fenced-write.js +39 -6
- package/dist/core/engine/finalizer-state.d.ts +3 -1
- package/dist/core/engine/handle-result.d.ts +49 -1
- package/dist/core/engine/handle-result.js +95 -20
- package/dist/core/engine/index.d.ts +13 -5
- package/dist/core/engine/index.js +155 -5
- package/dist/core/engine/inline-launch-queue.js +9 -0
- package/dist/core/engine/internals.d.ts +73 -0
- package/dist/core/engine/invoke-update-handler.d.ts +19 -0
- package/dist/core/engine/invoke-update-handler.js +7 -0
- package/dist/core/engine/lease-deposition.d.ts +132 -0
- package/dist/core/engine/lease-deposition.js +31 -0
- package/dist/core/engine/lease-errors.d.ts +80 -9
- package/dist/core/engine/lease-errors.js +28 -2
- package/dist/core/engine/lifecycle/resume.js +3 -1
- package/dist/core/engine/lifecycle/standalone-claim-acquire.d.ts +33 -0
- package/dist/core/engine/lifecycle/standalone-claim-acquire.js +25 -0
- package/dist/core/engine/lifecycle/start-commit.js +20 -6
- package/dist/core/engine/lifecycle/transition.d.ts +15 -1
- package/dist/core/engine/lifecycle/transition.js +6 -3
- package/dist/core/engine/operations-activity.js +1 -1
- package/dist/core/engine/operations-time.js +21 -7
- package/dist/core/engine/owner-side-signal-poll.d.ts +132 -0
- package/dist/core/engine/owner-side-signal-poll.js +22 -0
- package/dist/core/engine/owner-side-update-poll.d.ts +116 -0
- package/dist/core/engine/owner-side-update-poll.js +22 -0
- package/dist/core/engine/ownership-bootstrap.d.ts +280 -0
- package/dist/core/engine/ownership-bootstrap.js +89 -0
- package/dist/core/engine/ownership-mode-marker.d.ts +82 -0
- package/dist/core/engine/ownership-mode-marker.js +52 -0
- package/dist/core/engine/ownership-options.d.ts +22 -6
- package/dist/core/engine/ownership-options.js +42 -12
- package/dist/core/engine/pending-updates.js +35 -24
- package/dist/core/engine/queries.d.ts +70 -0
- package/dist/core/engine/queries.js +58 -4
- package/dist/core/engine/schedules.js +1 -1
- package/dist/core/engine/signals.d.ts +11 -1
- package/dist/core/engine/signals.js +11 -7
- package/dist/core/engine/sleep-timer-acknowledgements.d.ts +68 -0
- package/dist/core/engine/sleep-timer-acknowledgements.js +19 -0
- package/dist/core/engine/storage-io.d.ts +58 -10
- package/dist/core/engine/storage-io.js +27 -9
- package/dist/core/engine/termination/cleanup.d.ts +5 -4
- package/dist/core/engine/termination/complete.js +23 -4
- package/dist/core/engine/termination/finalizer-claim.js +4 -4
- package/dist/core/engine/termination/state-commit-callbacks.d.ts +15 -0
- package/dist/core/engine/termination/state-commit-callbacks.js +0 -0
- package/dist/core/engine/termination/suspend.js +1 -1
- package/dist/core/engine/update-validation.d.ts +28 -0
- package/dist/core/engine/update-validation.js +37 -0
- package/dist/core/engine/updates.d.ts +17 -16
- package/dist/core/engine/updates.js +24 -47
- package/dist/core/engine/wake-ownership-check.d.ts +86 -0
- package/dist/core/engine/wake-ownership-check.js +19 -0
- package/dist/core/engine/wake-ownership-guard.d.ts +46 -0
- package/dist/core/engine/wake-ownership-guard.js +23 -0
- package/dist/core/engine/workflow-claim-codec.d.ts +64 -0
- package/dist/core/engine/workflow-claim-codec.js +44 -0
- package/dist/core/engine/workflow-claim-cooldown.d.ts +60 -0
- package/dist/core/engine/workflow-claim-cooldown.js +17 -0
- package/dist/core/engine/workflow-claim-fold.d.ts +82 -0
- package/dist/core/engine/workflow-claim-fold.js +53 -0
- package/dist/core/engine/workflow-claim-metrics.d.ts +131 -0
- package/dist/core/engine/workflow-claim-metrics.js +62 -0
- package/dist/core/engine/workflow-claim-reclaim-scan.d.ts +96 -0
- package/dist/core/engine/workflow-claim-reclaim-scan.js +66 -0
- package/dist/core/engine/workflow-claim-reclaim-target.d.ts +107 -0
- package/dist/core/engine/workflow-claim-reclaim-target.js +155 -0
- package/dist/core/engine/workflow-claim-registry.d.ts +227 -0
- package/dist/core/engine/workflow-claim-registry.js +198 -0
- package/dist/core/engine/workflow-claim-renewal-interval.d.ts +50 -0
- package/dist/core/engine/workflow-claim-renewal-interval.js +132 -0
- package/dist/core/engine/workflow-claim-renewal-subpasses.d.ts +265 -0
- package/dist/core/engine/workflow-claim-renewal-subpasses.js +104 -0
- package/dist/core/engine/workflow-claim-renewal-task.d.ts +159 -0
- package/dist/core/engine/workflow-claim-renewal-task.js +65 -0
- package/dist/core/engine/workflow-claim-transitions.d.ts +186 -0
- package/dist/core/engine/workflow-claim-transitions.js +120 -0
- package/dist/core/inline-execution-strategy.d.ts +20 -0
- package/dist/core/inline-execution-strategy.js +15 -4
- package/dist/core/scheduler/scheduler-class.js +3 -3
- package/dist/core/scheduler/timer-sources.d.ts +13 -0
- package/dist/core/scheduler/timer-sources.js +1 -1
- package/dist/core/types/options.d.ts +17 -1
- package/dist/core/weft-error.d.ts +1 -1
- package/dist/core/weft-error.js +2 -1
- package/dist/http.js +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +8 -0
- package/dist/indexeddb.js +1 -1
- package/dist/json-schema.js +3 -3
- package/dist/observability/index.js +2 -2
- package/dist/server/operations/get-task-detail-schema.d.ts +341 -0
- package/dist/server/operations/get-task-detail-schema.js +118 -0
- package/dist/server/operations/get-task-detail.d.ts +346 -0
- package/dist/server/operations/get-task-detail.js +215 -0
- package/dist/server/operations/static-registrations.js +3 -0
- package/dist/storage/auto.js +1 -1
- package/dist/storage/bun-sql.js +113 -0
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/index.d.ts +38 -35
- package/dist/storage/interface.d.ts +87 -125
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-encoding.d.ts +51 -0
- package/dist/storage/key-encoding.js +13 -0
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +3 -0
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.js +2 -2
- package/dist/storage/node-sqlite.js +113 -0
- package/dist/storage/ownership-keys.d.ts +55 -0
- package/dist/storage/ownership-keys.js +6 -0
- package/dist/storage/postgres.js +2 -2
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/testing.js +1 -1
- package/dist/storage/turso.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web-extension.js +1 -1
- package/dist/worker/protocol.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three individual sub-pass implementations `workflow-claim-renewal-task.ts`
|
|
3
|
+
* composes into its combined `runOnce()` pass and, in interval mode, its two
|
|
4
|
+
* independently single-flight-guarded tick passes (see that module's
|
|
5
|
+
* "Renewal cadence is independent of the reclaim scan and signal poll in
|
|
6
|
+
* interval mode" doc section, WFT-79 Finding 2). Split into its own module so
|
|
7
|
+
* `workflow-claim-renewal-task.ts` stays under the repository's
|
|
8
|
+
* implementation-file-size ceiling; these are pure, target-driven functions
|
|
9
|
+
* with no dependency on the renewal task's own scheduling/single-flight
|
|
10
|
+
* state.
|
|
11
|
+
*
|
|
12
|
+
* Also home to every structural target/result type these sub-passes and
|
|
13
|
+
* `workflow-claim-renewal-task.ts` share, for the same reason —
|
|
14
|
+
* `ownership-bootstrap.ts` imports several of these types directly from
|
|
15
|
+
* `workflow-claim-renewal-task.ts` (not from here), so that module re-exports
|
|
16
|
+
* them; this module is the one place they are actually defined.
|
|
17
|
+
*
|
|
18
|
+
* @module core/engine/workflow-claim-renewal-subpasses
|
|
19
|
+
*/
|
|
20
|
+
import { type OwnerSideSignalPollResult, type OwnerSideSignalPollTarget } from './owner-side-signal-poll.ts';
|
|
21
|
+
import { type OwnerSideUpdatePollResult, type OwnerSideUpdatePollTarget } from './owner-side-update-poll.ts';
|
|
22
|
+
/**
|
|
23
|
+
* The minimal structural shape the renewal sub-pass needs from a per-workflow
|
|
24
|
+
* claim holder. A `WorkflowClaimRegistry` (built separately) is expected to
|
|
25
|
+
* satisfy this interface; it is defined locally, rather than imported, so
|
|
26
|
+
* this module has no dependency on that registry's concrete shape or module
|
|
27
|
+
* path.
|
|
28
|
+
*/
|
|
29
|
+
export type WorkflowClaimRenewalTarget = {
|
|
30
|
+
/**
|
|
31
|
+
* Every workflow id this engine currently holds a live claim for, active or
|
|
32
|
+
* parked. Read fresh at the start of every pass — implementations may
|
|
33
|
+
* return a live or a defensive-copy array; the caller never mutates it and
|
|
34
|
+
* takes its own snapshot before iterating.
|
|
35
|
+
*/
|
|
36
|
+
listHeldWorkflowIds(): readonly string[];
|
|
37
|
+
/**
|
|
38
|
+
* Renew this engine's claim for one workflow. Resolves when the renewal
|
|
39
|
+
* committed; rejects (with any error shape) when it did not — a lost-race
|
|
40
|
+
* CAS failure, a storage error, or anything else. The implementation is
|
|
41
|
+
* responsible for its own per-workflow in-flight-renewal guard against a
|
|
42
|
+
* concurrent `release`, and for reacting to a lost claim (aborting
|
|
43
|
+
* in-flight work, emitting `WeftWorkflowClaimLostWarning`). The caller only
|
|
44
|
+
* calls it, catches whatever it throws, and continues to the next
|
|
45
|
+
* workflow.
|
|
46
|
+
*/
|
|
47
|
+
renewWorkflowClaim(workflowId: string): Promise<void>;
|
|
48
|
+
};
|
|
49
|
+
/** One workflow's outcome within a single renewal pass. */
|
|
50
|
+
export type WorkflowClaimRenewalOutcome = {
|
|
51
|
+
workflowId: string;
|
|
52
|
+
status: 'renewed';
|
|
53
|
+
} | {
|
|
54
|
+
workflowId: string;
|
|
55
|
+
status: 'failed';
|
|
56
|
+
error: unknown;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* The minimal structural shape the reclaim-scan sub-pass needs. Defined
|
|
60
|
+
* locally for the same decoupling reason as {@link WorkflowClaimRenewalTarget}
|
|
61
|
+
* — expected to be satisfied by an adapter over
|
|
62
|
+
* `listWorkflowClaimReclaimCandidates` (`workflow-claim-reclaim-scan.ts`) and
|
|
63
|
+
* `WorkflowClaimRegistry.takeover`, built by `ownership-bootstrap.ts`.
|
|
64
|
+
*/
|
|
65
|
+
export type WorkflowClaimReclaimTarget = {
|
|
66
|
+
/**
|
|
67
|
+
* Every workflow id with a currently-persisted holder record this engine
|
|
68
|
+
* does not itself already hold. Read fresh at the start of every pass.
|
|
69
|
+
*/
|
|
70
|
+
listReclaimCandidateWorkflowIds(): Promise<readonly string[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Attempt to reclaim one candidate. Retrying a lost-race CAS (bounded, per
|
|
73
|
+
* the ADR, at 5 attempts within this call) and gating on the per-workflow-id
|
|
74
|
+
* anti-thrash cooldown are the implementation's responsibility — the caller
|
|
75
|
+
* calls it exactly once per candidate, catches whatever it throws, and
|
|
76
|
+
* continues to the next one.
|
|
77
|
+
*/
|
|
78
|
+
attemptWorkflowClaimTakeover(workflowId: string): Promise<WorkflowClaimReclaimAttemptResult>;
|
|
79
|
+
};
|
|
80
|
+
/** One candidate's non-throwing outcome from {@link WorkflowClaimReclaimTarget.attemptWorkflowClaimTakeover}. */
|
|
81
|
+
export type WorkflowClaimReclaimAttemptResult = {
|
|
82
|
+
status: 'reclaimed';
|
|
83
|
+
} | {
|
|
84
|
+
status: 'not-eligible';
|
|
85
|
+
} | {
|
|
86
|
+
status: 'backoff-skipped';
|
|
87
|
+
} | {
|
|
88
|
+
status: 'lost-race';
|
|
89
|
+
};
|
|
90
|
+
/** One workflow's outcome within a single reclaim-scan pass. */
|
|
91
|
+
export type WorkflowClaimReclaimOutcome = ({
|
|
92
|
+
workflowId: string;
|
|
93
|
+
} & WorkflowClaimReclaimAttemptResult) | {
|
|
94
|
+
workflowId: string;
|
|
95
|
+
status: 'error';
|
|
96
|
+
error: unknown;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* The reclaim-scan sub-pass's result. `'discovery-failed'` covers
|
|
100
|
+
* `listReclaimCandidateWorkflowIds()` itself throwing — without a candidate
|
|
101
|
+
* list there is no per-workflow loop to run, but that must not fail the rest
|
|
102
|
+
* of an enclosing combined pass (renewals already committed by then, and a
|
|
103
|
+
* `backgroundTasks: 'manual'` host awaiting `runMaintenance()` must not see a
|
|
104
|
+
* rejected promise for a problem isolated to this one sub-step).
|
|
105
|
+
*/
|
|
106
|
+
export type WorkflowClaimReclaimPassResult = {
|
|
107
|
+
status: 'completed';
|
|
108
|
+
outcomes: WorkflowClaimReclaimOutcome[];
|
|
109
|
+
reclaimedCount: number;
|
|
110
|
+
} | {
|
|
111
|
+
status: 'discovery-failed';
|
|
112
|
+
error: unknown;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* The owner-side signal-poll sub-pass's result. `'failed'` covers
|
|
116
|
+
* {@link runOwnerSideSignalPoll} itself rejecting (e.g. its target's
|
|
117
|
+
* `hasBufferedSignal` throwing) — same non-fatal-to-the-enclosing-pass
|
|
118
|
+
* treatment as {@link WorkflowClaimReclaimPassResult}'s `'discovery-failed'`.
|
|
119
|
+
*/
|
|
120
|
+
export type WorkflowClaimSignalPollOutcome = {
|
|
121
|
+
status: 'completed';
|
|
122
|
+
result: OwnerSideSignalPollResult;
|
|
123
|
+
} | {
|
|
124
|
+
status: 'failed';
|
|
125
|
+
error: unknown;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* The owner-side update-poll sub-pass's result (WFT-79). `'failed'` covers
|
|
129
|
+
* {@link runOwnerSideUpdatePoll} itself rejecting (e.g. its target's
|
|
130
|
+
* `hasPendingUpdates` throwing) — same non-fatal-to-the-enclosing-pass
|
|
131
|
+
* treatment as {@link WorkflowClaimSignalPollOutcome}'s `'failed'`.
|
|
132
|
+
*/
|
|
133
|
+
export type WorkflowClaimUpdatePollOutcome = {
|
|
134
|
+
status: 'completed';
|
|
135
|
+
result: OwnerSideUpdatePollResult;
|
|
136
|
+
} | {
|
|
137
|
+
status: 'failed';
|
|
138
|
+
error: unknown;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* The result of one full claim-renewal pass
|
|
142
|
+
* (`workflow-claim-renewal-task.ts`'s `WorkflowClaimRenewalTask.runOnce`, or
|
|
143
|
+
* one of `workflow-claim-renewal-interval.ts`'s interval-mode sub-passes).
|
|
144
|
+
* `reclaim`/`signalPoll`/`updatePoll` are `undefined` exactly when the
|
|
145
|
+
* matching target option was omitted — that omission is how a caller (or a
|
|
146
|
+
* test exercising renewal alone) opts out of running that sub-step at all.
|
|
147
|
+
* Defined here (rather than in `workflow-claim-renewal-task.ts`) so both that
|
|
148
|
+
* module and `workflow-claim-renewal-interval.ts` can depend on it without a
|
|
149
|
+
* cycle between them; `workflow-claim-renewal-task.ts` re-exports it for
|
|
150
|
+
* backward-compatible import paths.
|
|
151
|
+
*/
|
|
152
|
+
export type WorkflowClaimRenewalPassResult = {
|
|
153
|
+
/** `getNow()` read at the start of the pass, before any renewal call. */
|
|
154
|
+
startedAt: number;
|
|
155
|
+
/** `getNow()` read after renewal, reclaim, and signal-poll have all settled. */
|
|
156
|
+
finishedAt: number;
|
|
157
|
+
/** One entry per workflow id the pass attempted, in iteration order. */
|
|
158
|
+
outcomes: WorkflowClaimRenewalOutcome[];
|
|
159
|
+
/** `outcomes.filter(o => o.status === 'renewed').length`, precomputed for observability consumers. */
|
|
160
|
+
renewedCount: number;
|
|
161
|
+
/** `outcomes.filter(o => o.status === 'failed').length`, precomputed for observability consumers. */
|
|
162
|
+
failedCount: number;
|
|
163
|
+
/** Present only when this task was constructed with a `reclaimTarget`. */
|
|
164
|
+
reclaim?: WorkflowClaimReclaimPassResult;
|
|
165
|
+
/** Present only when this task was constructed with a `signalPollTarget`. */
|
|
166
|
+
signalPoll?: WorkflowClaimSignalPollOutcome;
|
|
167
|
+
/** Present only when this task was constructed with an `updatePollTarget`. */
|
|
168
|
+
updatePoll?: WorkflowClaimUpdatePollOutcome;
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* The interval-scheduling seam `workflow-claim-renewal-interval.ts` drives
|
|
172
|
+
* its interval-mode cadence through. The handle type is deliberately
|
|
173
|
+
* `unknown` on this public interface — nothing inspects a handle, only
|
|
174
|
+
* round-trips whatever `setInterval` returned back into `clearInterval` — so
|
|
175
|
+
* a test double can use a plain number, object, or anything else as its
|
|
176
|
+
* handle without either side needing to know the real timer type. Defined
|
|
177
|
+
* here for the same cross-module-without-a-cycle reason as
|
|
178
|
+
* {@link WorkflowClaimRenewalPassResult}; `workflow-claim-renewal-task.ts`
|
|
179
|
+
* re-exports it.
|
|
180
|
+
*/
|
|
181
|
+
export type WorkflowClaimRenewalIntervalScheduler = {
|
|
182
|
+
setInterval(callback: () => void, intervalMs: number): unknown;
|
|
183
|
+
clearInterval(handle: unknown): void;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* How many reclaim attempts may be in flight at once within a single pass.
|
|
187
|
+
*
|
|
188
|
+
* A serial loop lets one stuck candidate block every later one indefinitely —
|
|
189
|
+
* `attemptWorkflowClaimTakeover` can await an `onReclaimed` drive that never
|
|
190
|
+
* settles (e.g. a stalled storage read during replay), and a serial `for`
|
|
191
|
+
* loop never reaches the next candidate until that await resolves. Each
|
|
192
|
+
* candidate's takeover/acquire CAS and drive are independent per-workflow
|
|
193
|
+
* operations, so running them through the same bounded pool
|
|
194
|
+
* `runRenewalSubPass` uses for renewals is safe here too — see that
|
|
195
|
+
* function's doc for why a fixed-width pool is the right middle ground
|
|
196
|
+
* between full serialization and an unbounded stampede.
|
|
197
|
+
*/
|
|
198
|
+
export declare const WORKFLOW_CLAIM_RECLAIM_CONCURRENCY = 16;
|
|
199
|
+
/**
|
|
200
|
+
* Run one reclaim-scan sub-pass: list candidates, attempt each through a
|
|
201
|
+
* bounded pool (see {@link WORKFLOW_CLAIM_RECLAIM_CONCURRENCY}), and catch
|
|
202
|
+
* both a per-candidate throw and the listing call itself throwing. See
|
|
203
|
+
* {@link WorkflowClaimReclaimPassResult}'s doc for why discovery failure is a
|
|
204
|
+
* result, not a rejection. `outcomes` stays in `candidates` order regardless
|
|
205
|
+
* of the order attempts actually settle, matching `runRenewalSubPass`'s own
|
|
206
|
+
* positional-result discipline.
|
|
207
|
+
*/
|
|
208
|
+
export declare function runReclaimPass(target: WorkflowClaimReclaimTarget): Promise<WorkflowClaimReclaimPassResult>;
|
|
209
|
+
/**
|
|
210
|
+
* How many claim renewals may be in flight at once within a single pass.
|
|
211
|
+
*
|
|
212
|
+
* A serial loop costs one storage round trip per held claim before returning to
|
|
213
|
+
* the first one, so with many claims on a high-latency shared store the pass
|
|
214
|
+
* itself can outlast `workflowClaimTtl`: later claims expire before their first
|
|
215
|
+
* renewal, and earlier ones expire before the next pass. Separating the reclaim
|
|
216
|
+
* scan out of the renewal single-flight does not help — this loop is unbounded
|
|
217
|
+
* in the number of claims, independently of what else shares the tick.
|
|
218
|
+
*
|
|
219
|
+
* The opposite extreme is just as wrong: renewing every claim at once turns
|
|
220
|
+
* starvation into a storage stampede that the store may then rate-limit or
|
|
221
|
+
* queue, reproducing the latency it was meant to avoid. So renewals run through
|
|
222
|
+
* a fixed-width pool.
|
|
223
|
+
*
|
|
224
|
+
* Sixteen is chosen to be wide enough that per-request latency dominates rather
|
|
225
|
+
* than accumulates — it cuts a 1000-claim pass from 1000 sequential round trips
|
|
226
|
+
* to 63 — while staying within the connection budget a modest remote store
|
|
227
|
+
* offers. It is deliberately a constant rather than an option: it trades two
|
|
228
|
+
* failure modes against each other and neither is something a caller is well
|
|
229
|
+
* placed to tune. Revisit it with measurements, not intuition.
|
|
230
|
+
*/
|
|
231
|
+
export declare const WORKFLOW_CLAIM_RENEWAL_CONCURRENCY = 16;
|
|
232
|
+
/**
|
|
233
|
+
* Renew every id in `workflowIds`, continuing past a per-workflow failure.
|
|
234
|
+
*
|
|
235
|
+
* Renewals run through a bounded pool (see
|
|
236
|
+
* {@link WORKFLOW_CLAIM_RENEWAL_CONCURRENCY}) rather than one at a time, so a
|
|
237
|
+
* large claim set cannot push the pass past the claim validity window. Losing
|
|
238
|
+
* one claim still stops only that workflow: each renewal keeps its own
|
|
239
|
+
* `try`/`catch`, and `outcomes` stays in `workflowIds` order regardless of the
|
|
240
|
+
* order results actually arrive, so callers and tests see a stable, positional
|
|
241
|
+
* result.
|
|
242
|
+
*
|
|
243
|
+
* Pure — no clock reads, no `onPassComplete` — so it is shared verbatim by
|
|
244
|
+
* `workflow-claim-renewal-task.ts`'s combined `runOnce()` pass and interval
|
|
245
|
+
* mode's standalone renewal sub-pass.
|
|
246
|
+
*/
|
|
247
|
+
export declare function runRenewalSubPass(target: WorkflowClaimRenewalTarget, workflowIds: readonly string[]): Promise<{
|
|
248
|
+
outcomes: WorkflowClaimRenewalOutcome[];
|
|
249
|
+
renewedCount: number;
|
|
250
|
+
failedCount: number;
|
|
251
|
+
}>;
|
|
252
|
+
/**
|
|
253
|
+
* Run the owner-side signal-poll sub-step, translating a throw into the
|
|
254
|
+
* `'failed'` result shape rather than letting it reject. Shared by
|
|
255
|
+
* `workflow-claim-renewal-task.ts`'s combined `runOnce()` pass and interval
|
|
256
|
+
* mode's standalone reclaim-plus-poll sub-pass.
|
|
257
|
+
*/
|
|
258
|
+
export declare function runSignalPollSubPass(signalPollTarget: OwnerSideSignalPollTarget, getNow: () => number): Promise<WorkflowClaimSignalPollOutcome>;
|
|
259
|
+
/**
|
|
260
|
+
* Run the owner-side update-poll sub-step (WFT-79), translating a throw into
|
|
261
|
+
* the `'failed'` result shape rather than letting it reject. Shared by
|
|
262
|
+
* `workflow-claim-renewal-task.ts`'s combined `runOnce()` pass and interval
|
|
263
|
+
* mode's standalone update-poll sub-pass.
|
|
264
|
+
*/
|
|
265
|
+
export declare function runUpdatePollSubPass(updatePollTarget: OwnerSideUpdatePollTarget, getNow: () => number): Promise<WorkflowClaimUpdatePollOutcome>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runOwnerSideSignalPoll
|
|
3
|
+
} from "./owner-side-signal-poll.js";
|
|
4
|
+
import {
|
|
5
|
+
runOwnerSideUpdatePoll
|
|
6
|
+
} from "./owner-side-update-poll.js";
|
|
7
|
+
export const WORKFLOW_CLAIM_RECLAIM_CONCURRENCY = 16;
|
|
8
|
+
export async function runReclaimPass(target) {
|
|
9
|
+
let candidates;
|
|
10
|
+
try {
|
|
11
|
+
candidates = await target.listReclaimCandidateWorkflowIds();
|
|
12
|
+
} catch (error) {
|
|
13
|
+
return { status: "discovery-failed", error };
|
|
14
|
+
}
|
|
15
|
+
const outcomes = Array.from({ length: candidates.length });
|
|
16
|
+
async function attemptOne(index) {
|
|
17
|
+
const workflowId = candidates[index];
|
|
18
|
+
try {
|
|
19
|
+
const attempt = await target.attemptWorkflowClaimTakeover(workflowId);
|
|
20
|
+
outcomes[index] = { workflowId, ...attempt };
|
|
21
|
+
} catch (error) {
|
|
22
|
+
outcomes[index] = { workflowId, status: "error", error };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (candidates.length <= 1)
|
|
26
|
+
for (const [index] of candidates.entries())
|
|
27
|
+
await attemptOne(index);
|
|
28
|
+
else {
|
|
29
|
+
let nextIndex = 0;
|
|
30
|
+
async function attemptFromQueue() {
|
|
31
|
+
for (;; ) {
|
|
32
|
+
const index = nextIndex;
|
|
33
|
+
nextIndex += 1;
|
|
34
|
+
if (index >= candidates.length)
|
|
35
|
+
return;
|
|
36
|
+
await attemptOne(index);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const workerCount = Math.min(WORKFLOW_CLAIM_RECLAIM_CONCURRENCY, candidates.length);
|
|
40
|
+
await Promise.all(Array.from({ length: workerCount }, attemptFromQueue));
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
status: "completed",
|
|
44
|
+
outcomes,
|
|
45
|
+
reclaimedCount: outcomes.filter((outcome) => outcome.status === "reclaimed").length
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export const WORKFLOW_CLAIM_RENEWAL_CONCURRENCY = 16;
|
|
49
|
+
export async function runRenewalSubPass(target, workflowIds) {
|
|
50
|
+
const outcomes = Array.from({ length: workflowIds.length });
|
|
51
|
+
let nextIndex = 0;
|
|
52
|
+
async function renewFromQueue() {
|
|
53
|
+
for (;; ) {
|
|
54
|
+
const index = nextIndex;
|
|
55
|
+
nextIndex += 1;
|
|
56
|
+
if (index >= workflowIds.length)
|
|
57
|
+
return;
|
|
58
|
+
const workflowId = workflowIds[index];
|
|
59
|
+
try {
|
|
60
|
+
await target.renewWorkflowClaim(workflowId);
|
|
61
|
+
outcomes[index] = { workflowId, status: "renewed" };
|
|
62
|
+
} catch (error) {
|
|
63
|
+
outcomes[index] = { workflowId, status: "failed", error };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (workflowIds.length <= 1)
|
|
68
|
+
for (const [index, workflowId] of workflowIds.entries())
|
|
69
|
+
try {
|
|
70
|
+
await target.renewWorkflowClaim(workflowId);
|
|
71
|
+
outcomes[index] = { workflowId, status: "renewed" };
|
|
72
|
+
} catch (error) {
|
|
73
|
+
outcomes[index] = { workflowId, status: "failed", error };
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const workerCount = Math.min(WORKFLOW_CLAIM_RENEWAL_CONCURRENCY, workflowIds.length);
|
|
77
|
+
await Promise.all(Array.from({ length: workerCount }, renewFromQueue));
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
outcomes,
|
|
81
|
+
renewedCount: outcomes.filter((outcome) => outcome.status === "renewed").length,
|
|
82
|
+
failedCount: outcomes.filter((outcome) => outcome.status === "failed").length
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export async function runSignalPollSubPass(signalPollTarget, getNow) {
|
|
86
|
+
try {
|
|
87
|
+
return {
|
|
88
|
+
status: "completed",
|
|
89
|
+
result: await runOwnerSideSignalPoll({ target: signalPollTarget, getNow })
|
|
90
|
+
};
|
|
91
|
+
} catch (error) {
|
|
92
|
+
return { status: "failed", error };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export async function runUpdatePollSubPass(updatePollTarget, getNow) {
|
|
96
|
+
try {
|
|
97
|
+
return {
|
|
98
|
+
status: "completed",
|
|
99
|
+
result: await runOwnerSideUpdatePoll({ target: updatePollTarget, getNow })
|
|
100
|
+
};
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return { status: "failed", error };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recurring driver for the per-workflow claim-renewal lifecycle task described
|
|
3
|
+
* in [ADR 0002 § Reclaiming stranded claims](../../../documentation/contributing/architecture-decisions/0002-multiengine-per-workflow-ownership.md#reclaiming-stranded-claims)
|
|
4
|
+
* and its `renew` transition row.
|
|
5
|
+
*
|
|
6
|
+
* **Why this must be its own driver, independent of the durable-timer
|
|
7
|
+
* scheduler.** Renewal keeps a *parked* workflow's claim alive — sleep,
|
|
8
|
+
* wait-signal, wait-update, wait-condition, wait-async-activity, and
|
|
9
|
+
* await-child-completion all keep the claim held rather than releasing it (see
|
|
10
|
+
* the ADR's `wf-owner-holder` row). A parked workflow has no pending durable
|
|
11
|
+
* timer of its own to piggyback on, and `startScheduler: false` is an
|
|
12
|
+
* explicitly supported, orthogonal configuration — tying renewal to
|
|
13
|
+
* `Scheduler#tick` would silently expire every parked claim the moment the
|
|
14
|
+
* scheduler is disabled, even though the owning engine is perfectly healthy.
|
|
15
|
+
* This task runs on its own cadence, or on an explicitly awaited host tick
|
|
16
|
+
* under `backgroundTasks: 'manual'` (see `Engine#runMaintenance()`), and never
|
|
17
|
+
* depends on the scheduler being started.
|
|
18
|
+
*
|
|
19
|
+
* **What this module owns.** `runOnce()` — the manual/`backgroundTasks:
|
|
20
|
+
* 'manual'` combined pass — plus the public options/result/task types. The
|
|
21
|
+
* actual claim storage transitions (`buildWorkflowClaimRenewTransition`), the
|
|
22
|
+
* in-flight-renewal guard *per workflow* that serializes a renewal against a
|
|
23
|
+
* concurrent `release` (ADR `renew` row), and the deposition side effects of
|
|
24
|
+
* a failed renewal — aborting the workflow's in-flight work and emitting
|
|
25
|
+
* {@link WeftWorkflowClaimLostWarning} — all belong to the claim holder this
|
|
26
|
+
* task is handed ({@link WorkflowClaimRenewalTarget}), typically a
|
|
27
|
+
* `WorkflowClaimRegistry`. This module never imports that registry directly
|
|
28
|
+
* and never imports `lease-deposition.ts`: coupling to the registry's exact
|
|
29
|
+
* shape here would tie two patches built in parallel together, and duplicating
|
|
30
|
+
* its warning-emission would double-emit once the two are wired together.
|
|
31
|
+
* Interval-mode driving (four independent single-flight sub-pass slots) lives
|
|
32
|
+
* in `workflow-claim-renewal-interval.ts`, split out to keep this file under
|
|
33
|
+
* the repository's implementation-file-size ceiling — see that module's doc
|
|
34
|
+
* for the full "each sub-pass gets its own slot" rationale (WFT-79 Finding 2
|
|
35
|
+
* and its extensions).
|
|
36
|
+
*
|
|
37
|
+
* **`runOnce()` still runs every configured sub-step sequentially in one
|
|
38
|
+
* awaited call**, exactly as ADR 0002 describes — a host calling
|
|
39
|
+
* `runMaintenance()` gets one coherent pass (serialized against any other
|
|
40
|
+
* concurrent `runOnce()` call — see `inFlightManualPass`). Interval mode's
|
|
41
|
+
* `start()`/`stop()` (delegated to `workflow-claim-renewal-interval.ts`)
|
|
42
|
+
* gives renewal, reclaim, signal poll, and update poll each their own
|
|
43
|
+
* cadence instead.
|
|
44
|
+
*
|
|
45
|
+
* **Three more ADR 0002 responsibilities besides renewal itself**, each
|
|
46
|
+
* OPTIONAL: a reclaim scan that attempts `takeover` for workflows whose
|
|
47
|
+
* holders have passed the grace-adjusted `expire` judgment (ADR § Reclaiming
|
|
48
|
+
* stranded claims), driven through the {@link WorkflowClaimReclaimTarget}
|
|
49
|
+
* structural seam — expected to be satisfied by
|
|
50
|
+
* `listWorkflowClaimReclaimCandidates` (`workflow-claim-reclaim-scan.ts`)
|
|
51
|
+
* plus `WorkflowClaimRegistry.takeover`, adapted by `ownership-bootstrap.ts`
|
|
52
|
+
* — owner-side polling of the durable signal buffer for parked
|
|
53
|
+
* `waitForSignal` workflows (ADR § signal delivery, "owner-side polling"),
|
|
54
|
+
* and owner-side polling of pending coordinated updates (WFT-79). All three
|
|
55
|
+
* are `undefined` in {@link WorkflowClaimRenewalPassResult} when their target
|
|
56
|
+
* option is omitted, which is how a caller with only the renewal target (or
|
|
57
|
+
* a test) opts out of running them at all.
|
|
58
|
+
*
|
|
59
|
+
* @module core/engine/workflow-claim-renewal-task
|
|
60
|
+
*/
|
|
61
|
+
import type { OwnerSideSignalPollTarget } from './owner-side-signal-poll.ts';
|
|
62
|
+
import type { OwnerSideUpdatePollTarget } from './owner-side-update-poll.ts';
|
|
63
|
+
import { type WorkflowClaimReclaimTarget, type WorkflowClaimRenewalIntervalScheduler, type WorkflowClaimRenewalPassResult, type WorkflowClaimRenewalTarget } from './workflow-claim-renewal-subpasses.ts';
|
|
64
|
+
export type { WorkflowClaimRenewalIntervalScheduler, WorkflowClaimRenewalPassResult, } from './workflow-claim-renewal-subpasses.ts';
|
|
65
|
+
/** Options for {@link createWorkflowClaimRenewalTask}. */
|
|
66
|
+
export type WorkflowClaimRenewalTaskOptions = {
|
|
67
|
+
/** The claim holder this task renews on behalf of. */
|
|
68
|
+
target: WorkflowClaimRenewalTarget;
|
|
69
|
+
/**
|
|
70
|
+
* Wall-clock source (ms), injected so tests never depend on real time. There
|
|
71
|
+
* is no default — every call site (the engine) already threads an injected
|
|
72
|
+
* clock through the rest of the ownership machinery (see `lease-manager.ts`),
|
|
73
|
+
* and an implicit `Date.now()` default would be a code path tests could
|
|
74
|
+
* never exercise deterministically.
|
|
75
|
+
*/
|
|
76
|
+
getNow: () => number;
|
|
77
|
+
/** Interval-mode cadence (ms). Should be the configured `workflowClaimRenewInterval`. */
|
|
78
|
+
intervalMs: number;
|
|
79
|
+
/**
|
|
80
|
+
* Interval-scheduling seam. Defaults to the real `setInterval`/`clearInterval`,
|
|
81
|
+
* with the created interval `unref()`'d (when supported) so it never keeps an
|
|
82
|
+
* otherwise-idle process alive — mirroring `lease-manager.ts`'s `startRenewal`.
|
|
83
|
+
*/
|
|
84
|
+
scheduler?: WorkflowClaimRenewalIntervalScheduler;
|
|
85
|
+
/**
|
|
86
|
+
* Optional reclaim-scan target. Omitted (the default) runs renewal alone —
|
|
87
|
+
* `result.reclaim` stays `undefined`. See {@link WorkflowClaimReclaimTarget}.
|
|
88
|
+
*/
|
|
89
|
+
reclaimTarget?: WorkflowClaimReclaimTarget;
|
|
90
|
+
/**
|
|
91
|
+
* Optional owner-side signal-poll target. Omitted (the default) leaves
|
|
92
|
+
* `result.signalPoll` `undefined`. See `owner-side-signal-poll.ts`'s
|
|
93
|
+
* `OwnerSideSignalPollTarget`.
|
|
94
|
+
*/
|
|
95
|
+
signalPollTarget?: OwnerSideSignalPollTarget;
|
|
96
|
+
/**
|
|
97
|
+
* Optional owner-side update-poll target (WFT-79). Omitted (the default)
|
|
98
|
+
* leaves `result.updatePoll` `undefined`. See `owner-side-update-poll.ts`'s
|
|
99
|
+
* `OwnerSideUpdatePollTarget`.
|
|
100
|
+
*/
|
|
101
|
+
updatePollTarget?: OwnerSideUpdatePollTarget;
|
|
102
|
+
/**
|
|
103
|
+
* Called after every completed pass, interval-driven or explicit. This is
|
|
104
|
+
* the reporting seam a later observability stage hangs a metrics counter
|
|
105
|
+
* off of (e.g. `weft_workflow_claim_renewal_failures_total`); this task does
|
|
106
|
+
* not touch any metrics registry itself.
|
|
107
|
+
*/
|
|
108
|
+
onPassComplete?: (result: WorkflowClaimRenewalPassResult) => void;
|
|
109
|
+
};
|
|
110
|
+
/** A running claim-renewal task. */
|
|
111
|
+
export type WorkflowClaimRenewalTask = {
|
|
112
|
+
/**
|
|
113
|
+
* Run exactly one pass: snapshot the currently-held workflow ids, renew each
|
|
114
|
+
* in turn, and continue past a failure on any one of them so a single lost
|
|
115
|
+
* claim never stops the rest from renewing. A per-workflow renewal failure is
|
|
116
|
+
* captured as a `'failed'` outcome rather than a rejection; the pass itself
|
|
117
|
+
* rejects only if `listHeldWorkflowIds()` throws, since without a snapshot
|
|
118
|
+
* there is no pass to run. A throwing `onPassComplete` sink never fails the
|
|
119
|
+
* pass — the renewal work already committed by then.
|
|
120
|
+
* This is the entry point `backgroundTasks: 'manual'` hosts drive from an
|
|
121
|
+
* awaited host tick (`Engine#runMaintenance()`), and the one tests use to
|
|
122
|
+
* step the task deterministically. Bypasses the interval-mode single-flight
|
|
123
|
+
* slots by design — same as `renewOnce()` does relative to
|
|
124
|
+
* `startRenewal()`'s timer in `lease-manager.ts` — so an explicit caller
|
|
125
|
+
* is never silently skipped because an interval-driven pass happens to be
|
|
126
|
+
* in flight. It IS, however, deduped against another concurrent `runOnce()`
|
|
127
|
+
* call: `Engine.create()` documents concurrent `runMaintenance()` calls as
|
|
128
|
+
* safe, and two independent combined passes sharing this task's targets
|
|
129
|
+
* could otherwise both act on the same stale reclaim candidate. An
|
|
130
|
+
* overlapping caller receives the already-running pass's result rather
|
|
131
|
+
* than starting a second one.
|
|
132
|
+
*/
|
|
133
|
+
runOnce(): Promise<WorkflowClaimRenewalPassResult>;
|
|
134
|
+
/**
|
|
135
|
+
* Start interval-driven passes. Idempotent — a second call while already
|
|
136
|
+
* started does not create a second interval. Each interval tick is guarded
|
|
137
|
+
* by a single-flight lock: if the previous interval-driven pass is still
|
|
138
|
+
* running when the next tick fires, that tick is skipped rather than
|
|
139
|
+
* starting an overlapping pass.
|
|
140
|
+
*/
|
|
141
|
+
start(): void;
|
|
142
|
+
/**
|
|
143
|
+
* Stop interval-driven passes. Idempotent — a second call, or a call before
|
|
144
|
+
* `start()`, does not throw. Only clears the interval; a pass already in
|
|
145
|
+
* flight (started by the last tick before `stop()`) still runs to
|
|
146
|
+
* completion, but no further tick fires afterward.
|
|
147
|
+
*/
|
|
148
|
+
stop(): void;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Create a claim-renewal task. Does not start any timer and does not run any
|
|
152
|
+
* pass itself — the caller drives it, either via `start()` for interval mode
|
|
153
|
+
* (delegated to `workflow-claim-renewal-interval.ts`) or by awaiting
|
|
154
|
+
* `runOnce()` directly under `backgroundTasks: 'manual'`. The sub-pass
|
|
155
|
+
* implementations both this module and the interval driver compose
|
|
156
|
+
* (`runReclaimPass`, `runRenewalSubPass`, `runSignalPollSubPass`,
|
|
157
|
+
* `runUpdatePollSubPass`) live in `workflow-claim-renewal-subpasses.ts`.
|
|
158
|
+
*/
|
|
159
|
+
export declare function createWorkflowClaimRenewalTask(options: WorkflowClaimRenewalTaskOptions): WorkflowClaimRenewalTask;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createWorkflowClaimRenewalIntervalDriver,
|
|
3
|
+
defaultWorkflowClaimRenewalScheduler
|
|
4
|
+
} from "./workflow-claim-renewal-interval.js";
|
|
5
|
+
import {
|
|
6
|
+
runReclaimPass,
|
|
7
|
+
runRenewalSubPass,
|
|
8
|
+
runSignalPollSubPass,
|
|
9
|
+
runUpdatePollSubPass
|
|
10
|
+
} from "./workflow-claim-renewal-subpasses.js";
|
|
11
|
+
export function createWorkflowClaimRenewalTask(options) {
|
|
12
|
+
const {
|
|
13
|
+
target,
|
|
14
|
+
getNow,
|
|
15
|
+
intervalMs,
|
|
16
|
+
reclaimTarget,
|
|
17
|
+
signalPollTarget,
|
|
18
|
+
updatePollTarget,
|
|
19
|
+
onPassComplete
|
|
20
|
+
} = options, scheduler = options.scheduler ?? defaultWorkflowClaimRenewalScheduler();
|
|
21
|
+
let inFlightManualPass = null;
|
|
22
|
+
function emitPassComplete(result) {
|
|
23
|
+
try {
|
|
24
|
+
onPassComplete?.(result);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error("weft: workflow claim renewal onPassComplete sink threw", error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function runCombinedPass() {
|
|
30
|
+
const startedAt = getNow(), workflowIds = [...target.listHeldWorkflowIds()], renewal = await runRenewalSubPass(target, workflowIds), reclaim = reclaimTarget === void 0 ? void 0 : await runReclaimPass(reclaimTarget), signalPoll = signalPollTarget === void 0 ? void 0 : await runSignalPollSubPass(signalPollTarget, getNow), updatePoll = updatePollTarget === void 0 ? void 0 : await runUpdatePollSubPass(updatePollTarget, getNow), finishedAt = getNow(), result = {
|
|
31
|
+
startedAt,
|
|
32
|
+
finishedAt,
|
|
33
|
+
...renewal,
|
|
34
|
+
...reclaim === void 0 ? {} : { reclaim },
|
|
35
|
+
...signalPoll === void 0 ? {} : { signalPoll },
|
|
36
|
+
...updatePoll === void 0 ? {} : { updatePoll }
|
|
37
|
+
};
|
|
38
|
+
emitPassComplete(result);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
async function runOnce() {
|
|
42
|
+
if (inFlightManualPass !== null)
|
|
43
|
+
return inFlightManualPass;
|
|
44
|
+
const pass = runCombinedPass().finally(() => {
|
|
45
|
+
inFlightManualPass = null;
|
|
46
|
+
});
|
|
47
|
+
inFlightManualPass = pass;
|
|
48
|
+
return pass;
|
|
49
|
+
}
|
|
50
|
+
const intervalDriver = createWorkflowClaimRenewalIntervalDriver({
|
|
51
|
+
target,
|
|
52
|
+
getNow,
|
|
53
|
+
intervalMs,
|
|
54
|
+
scheduler,
|
|
55
|
+
...reclaimTarget === void 0 ? {} : { reclaimTarget },
|
|
56
|
+
...signalPollTarget === void 0 ? {} : { signalPollTarget },
|
|
57
|
+
...updatePollTarget === void 0 ? {} : { updatePollTarget },
|
|
58
|
+
...onPassComplete === void 0 ? {} : { onPassComplete }
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
runOnce,
|
|
62
|
+
start: () => intervalDriver.start(),
|
|
63
|
+
stop: () => intervalDriver.stop()
|
|
64
|
+
};
|
|
65
|
+
}
|