@kb-labs/workflow-engine 2.118.1 → 2.119.0-canary.85d060ea6
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.d.ts +66 -6
- package/dist/index.js +274 -119
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WorkflowSpec, RunTrigger, IdempotencyKey, ConcurrencyGroup, WorkflowRun, JobRun, StepRun, RetryPolicy, ArtifactMergeConfig } from '@kb-labs/workflow-contracts';
|
|
2
2
|
import { ILogger, ICache, IEventBus, IAnalytics, ISnapshotManager, Unsubscribe, IJobScheduler, JobDefinition, JobHandle, CronExpression, JobFilter } from '@kb-labs/core-platform';
|
|
3
|
-
import { JobPriority, WorkflowEventName } from '@kb-labs/workflow-constants';
|
|
3
|
+
import { AssertTransitionOptions, JobPriority, WorkflowEventName } from '@kb-labs/workflow-constants';
|
|
4
4
|
import { ClassifiedFailure, IExecutionBackend } from '@kb-labs/core-contracts';
|
|
5
5
|
import { ArtifactClient } from '@kb-labs/workflow-artifacts';
|
|
6
6
|
import { IEntityRegistry } from '@kb-labs/core-registry';
|
|
@@ -51,9 +51,34 @@ declare class StateStore {
|
|
|
51
51
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
52
52
|
deleteRun(runId: string): Promise<void>;
|
|
53
53
|
getAllRunIds(): Promise<string[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Holds an exclusive per-run lock (see `withLock`) for the whole
|
|
56
|
+
* read-modify-write, so `mutator` runs exactly once per call — no other
|
|
57
|
+
* writer can observe or clobber the run in between. This is what actually
|
|
58
|
+
* fixes the original bug class (a job marked `failed` while its step was
|
|
59
|
+
* mid-write to `waiting_approval`): the two writes can no longer interleave.
|
|
60
|
+
*
|
|
61
|
+
* `mutator` must still not `await` — it runs synchronously against a
|
|
62
|
+
* single in-memory draft while the lock is held; async work belongs after
|
|
63
|
+
* `updateRun` resolves (and holding the lock across an `await` would just
|
|
64
|
+
* make every other writer to this run block on it needlessly).
|
|
65
|
+
*/
|
|
54
66
|
updateRun(runId: string, mutator: (draft: WorkflowRun) => WorkflowRun | void): Promise<WorkflowRun | null>;
|
|
55
67
|
updateJob(runId: string, jobId: string, mutator: (job: JobRun) => JobRun | void): Promise<JobRun | null>;
|
|
56
68
|
updateStep(runId: string, jobId: string, stepId: string, mutator: (step: StepRun) => StepRun | void): Promise<StepRun | null>;
|
|
69
|
+
/**
|
|
70
|
+
* Like `updateRun`, but validates the status transition against the
|
|
71
|
+
* workflow state machine before applying it — throws
|
|
72
|
+
* `IllegalStateTransitionError` (from `@kb-labs/workflow-constants`) if
|
|
73
|
+
* `to` is not reachable from the run's current status. `mutate` sets any
|
|
74
|
+
* *other* fields; it must not itself assign `.status` (this method owns
|
|
75
|
+
* that assignment, after the check).
|
|
76
|
+
*/
|
|
77
|
+
transitionRun(runId: string, to: WorkflowRun['status'], mutate?: (draft: WorkflowRun) => void, options?: AssertTransitionOptions): Promise<WorkflowRun | null>;
|
|
78
|
+
/** Job-level counterpart of `transitionRun` — see its docblock. */
|
|
79
|
+
transitionJob(runId: string, jobId: string, to: JobRun['status'], mutate?: (draft: JobRun) => void, options?: AssertTransitionOptions): Promise<JobRun | null>;
|
|
80
|
+
/** Step-level counterpart of `transitionRun` — see its docblock. */
|
|
81
|
+
transitionStep(runId: string, jobId: string, stepId: string, to: StepRun['status'], mutate?: (draft: StepRun) => void, options?: AssertTransitionOptions): Promise<StepRun | null>;
|
|
57
82
|
releaseBlockedJobs(runId: string, completedJobName: string): Promise<JobRun[]>;
|
|
58
83
|
}
|
|
59
84
|
|
|
@@ -111,6 +136,16 @@ declare class Scheduler {
|
|
|
111
136
|
enqueueJob(runId: string, job: JobRun, priority?: JobPriority): Promise<void>;
|
|
112
137
|
dequeueJob(): Promise<JobQueueEntry | null>;
|
|
113
138
|
reschedule(entry: JobQueueEntry, delayMs: number): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* The read (`zrangebyscore`) and the remove (`zrem`) below are two
|
|
141
|
+
* separate cache round-trips, not one atomic op — without the lock, two
|
|
142
|
+
* daemon instances racing this method could both read the same top entry
|
|
143
|
+
* before either removes it, and both would go on to execute the same job
|
|
144
|
+
* (the daemon runs multiple instances in production, so this is a live
|
|
145
|
+
* bug, not a theoretical one). `withLock` serializes dequeues against this
|
|
146
|
+
* one priority queue across every process sharing the same cache backend,
|
|
147
|
+
* the same way `StateStore.updateRun` serializes writes to one run.
|
|
148
|
+
*/
|
|
114
149
|
private dequeueFromPriority;
|
|
115
150
|
getDefaultPriority(): JobPriority;
|
|
116
151
|
}
|
|
@@ -293,10 +328,26 @@ declare class WorkflowEngine {
|
|
|
293
328
|
/**
|
|
294
329
|
* Mark step as waiting for human approval.
|
|
295
330
|
*/
|
|
331
|
+
/**
|
|
332
|
+
* Park a step waiting for human approval — and park its parent job with
|
|
333
|
+
* it, in the SAME atomic write (one `transitionJob` call touching both the
|
|
334
|
+
* job's own status and its nested step). Two separate writes (step then
|
|
335
|
+
* job) would leave a window where a reader could observe step=waiting but
|
|
336
|
+
* job=running; going through one call closes that window entirely, not
|
|
337
|
+
* just narrows it.
|
|
338
|
+
*
|
|
339
|
+
* The job-level `waiting_approval` status is what makes the daemon-restart
|
|
340
|
+
* exemption in `cleanupStaleRuns` structural: that force-fail loop only
|
|
341
|
+
* ever touches `running`/`queued` jobs, so a parked job is never in its
|
|
342
|
+
* blast radius — no bespoke "is this job actually abandoned or just
|
|
343
|
+
* waiting on a human" check needed there.
|
|
344
|
+
*/
|
|
296
345
|
markStepWaitingApproval(runId: string, jobId: string, stepId: string): Promise<void>;
|
|
297
346
|
/**
|
|
298
|
-
* Park a step
|
|
299
|
-
*
|
|
347
|
+
* Park a step (and its parent job — see `markStepWaitingApproval`'s
|
|
348
|
+
* docblock for why job+step move together in one write) while its child
|
|
349
|
+
* workflow runs. The worker returns after this transition, so parent
|
|
350
|
+
* workflows never consume the pool needed by children.
|
|
300
351
|
*/
|
|
301
352
|
markStepWaitingChild(runId: string, jobId: string, stepId: string, childRunId: string): Promise<void>;
|
|
302
353
|
/** Re-queue a parked parent job after its child workflow reaches a terminal state. */
|
|
@@ -305,6 +356,12 @@ declare class WorkflowEngine {
|
|
|
305
356
|
* Resolve a pending approval — approve or reject.
|
|
306
357
|
* On approve: marks step as success with approval outputs.
|
|
307
358
|
* On reject: marks step as failed with rejection error.
|
|
359
|
+
*
|
|
360
|
+
* Resolves the step AND un-parks the job (`waiting_approval` → `queued`)
|
|
361
|
+
* in one atomic write — same reasoning as `markStepWaitingApproval` — then
|
|
362
|
+
* re-enqueues the job. This re-enqueue is what actually resumes execution:
|
|
363
|
+
* with approval no longer polled in-process (the worker parks and returns
|
|
364
|
+
* instead of waiting), nothing else will ever pick this job back up.
|
|
308
365
|
*/
|
|
309
366
|
resolveApproval(runId: string, jobId: string, stepId: string, action: 'approve' | 'reject', data?: Record<string, unknown>, comment?: string): Promise<void>;
|
|
310
367
|
/**
|
|
@@ -316,9 +373,12 @@ declare class WorkflowEngine {
|
|
|
316
373
|
*/
|
|
317
374
|
getScheduler(): Scheduler;
|
|
318
375
|
/**
|
|
319
|
-
* Mark stale running/queued
|
|
320
|
-
*
|
|
321
|
-
*
|
|
376
|
+
* Mark stale running/queued jobs as failed on daemon startup — their
|
|
377
|
+
* executor process is gone, so they're unrecoverable. The run itself is
|
|
378
|
+
* only finalized as 'failed' if nothing else could still complete it;
|
|
379
|
+
* a run with one abandoned job and one job legitimately parked on a human
|
|
380
|
+
* approval or a child workflow stays 'running' (only the abandoned job is
|
|
381
|
+
* failed) until the parked one resolves.
|
|
322
382
|
*/
|
|
323
383
|
cleanupStaleRuns(): Promise<void>;
|
|
324
384
|
/**
|