@asaidimu/utils-pipeline 1.0.2 → 1.0.4
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 +117 -276
- package/index.d.mts +419 -39
- package/index.d.ts +419 -39
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -1182,22 +1182,27 @@ interface State {
|
|
|
1182
1182
|
[key: string]: any;
|
|
1183
1183
|
}
|
|
1184
1184
|
/**
|
|
1185
|
-
* Instructs the
|
|
1186
|
-
*
|
|
1185
|
+
* Instructs the factory to pause the pipeline.
|
|
1186
|
+
*
|
|
1187
|
+
* - `pause` — the `stageId` at which the pipeline should resume.
|
|
1188
|
+
* - `timeout` — how long (ms) the registry should hold the live RunContext
|
|
1189
|
+
* before running the deferred container export and clearing the
|
|
1190
|
+
* strong reference. Omit to use the registry's configured default.
|
|
1191
|
+
* A value of 0 means export immediately on pause (no deferral).
|
|
1192
|
+
* - `persist` — kept for backward compatibility; when true the registry will
|
|
1193
|
+
* always export the container bundle at expiry regardless of
|
|
1194
|
+
* whether resume was called. Ignored if timeout is 0.
|
|
1187
1195
|
*/
|
|
1188
1196
|
interface PauseInstruction {
|
|
1189
1197
|
readonly pause: string;
|
|
1190
|
-
|
|
1191
|
-
* If true, the engine will snapshot the entire ArtifactContainer
|
|
1192
|
-
* and store the bundle in the checkpoint. Defaults to false.
|
|
1193
|
-
*/
|
|
1198
|
+
readonly timeout?: number;
|
|
1194
1199
|
readonly persist?: boolean;
|
|
1195
1200
|
}
|
|
1196
1201
|
/**
|
|
1197
1202
|
* All possible outcomes of a router:
|
|
1198
|
-
* - `string`
|
|
1199
|
-
* - `null`
|
|
1200
|
-
* - `undefined`
|
|
1203
|
+
* - `string` → jump to a named stage
|
|
1204
|
+
* - `null` → terminate the pipeline
|
|
1205
|
+
* - `undefined` → advance to the next stage in order (or natural end)
|
|
1201
1206
|
* - `PauseInstruction` → suspend and write a checkpoint
|
|
1202
1207
|
*/
|
|
1203
1208
|
type RoutingInstruction = string | null | undefined | PauseInstruction;
|
|
@@ -1220,6 +1225,10 @@ type PipelineStageRouter<S extends object> = (state: S, results: Record<string,
|
|
|
1220
1225
|
*
|
|
1221
1226
|
* The checkpoint is always written atomically with the state patches of the
|
|
1222
1227
|
* stage that triggered the pause.
|
|
1228
|
+
*
|
|
1229
|
+
* `containerBundle` is intentionally absent at write time — the factory never
|
|
1230
|
+
* exports. The registry writes it into the checkpoint (via writeCheckpoint)
|
|
1231
|
+
* when the pause timeout fires and the live RunContext is about to be released.
|
|
1223
1232
|
*/
|
|
1224
1233
|
interface PipelineCheckpoint {
|
|
1225
1234
|
readonly runId: string;
|
|
@@ -1228,9 +1237,10 @@ interface PipelineCheckpoint {
|
|
|
1228
1237
|
readonly pausedAtStageId: string;
|
|
1229
1238
|
readonly pausedAtStageLabel: string;
|
|
1230
1239
|
readonly pausedOn: string;
|
|
1240
|
+
/** Written by the registry at timeout expiry, never by the factory. */
|
|
1231
1241
|
readonly containerBundle?: ExportedContainerState;
|
|
1232
1242
|
}
|
|
1233
|
-
/**
|
|
1243
|
+
/** Factory-internal namespace for all checkpoint data. */
|
|
1234
1244
|
declare const PIPELINE_DATA_KEY: "__pipeline_data__";
|
|
1235
1245
|
/**
|
|
1236
1246
|
* Run-scoped context injected into every step action as a second argument.
|
|
@@ -1391,10 +1401,14 @@ type EventPath = readonly PathNode[];
|
|
|
1391
1401
|
/**
|
|
1392
1402
|
* All events emitted during a run.
|
|
1393
1403
|
*
|
|
1394
|
-
* stage:paused (#
|
|
1404
|
+
* stage:paused (#8) is a dedicated event for stages that pause, distinct from
|
|
1395
1405
|
* stage:success. Consumers should listen to both if they want to react to all
|
|
1396
1406
|
* terminal stage outcomes. stage:success is only emitted when the stage
|
|
1397
1407
|
* advances (or terminates) — never when it pauses.
|
|
1408
|
+
*
|
|
1409
|
+
* pipeline:paused carries `pauseTimeout` forwarded from the PauseInstruction
|
|
1410
|
+
* so the PipelineRegistry can arm its cleanup timer with the correct duration
|
|
1411
|
+
* without re-reading or re-parsing the instruction.
|
|
1398
1412
|
*/
|
|
1399
1413
|
interface RunEventMap<S extends object> {
|
|
1400
1414
|
"pipeline:start": {
|
|
@@ -1417,6 +1431,12 @@ interface RunEventMap<S extends object> {
|
|
|
1417
1431
|
runId: string;
|
|
1418
1432
|
checkpoint: PipelineCheckpoint;
|
|
1419
1433
|
finalState: S;
|
|
1434
|
+
/**
|
|
1435
|
+
* The timeout value from the PauseInstruction, forwarded verbatim.
|
|
1436
|
+
* Undefined means the registry should use its configured default.
|
|
1437
|
+
* 0 means export immediately (no deferral window).
|
|
1438
|
+
*/
|
|
1439
|
+
pauseTimeout: number | undefined;
|
|
1420
1440
|
};
|
|
1421
1441
|
"pipeline:failure": {
|
|
1422
1442
|
path: EventPath;
|
|
@@ -1503,8 +1523,8 @@ interface ScopedEventBus<S extends object> {
|
|
|
1503
1523
|
on<K extends keyof RunEventMap<S>>(event: K, handler: RunEventHandler<S, K>): () => void;
|
|
1504
1524
|
}
|
|
1505
1525
|
/**
|
|
1506
|
-
* A unit of execution returned by {@link
|
|
1507
|
-
* {@link
|
|
1526
|
+
* A unit of execution returned by {@link PipelineFactory.prepare} or
|
|
1527
|
+
* {@link PipelineFactory.resume}.
|
|
1508
1528
|
*
|
|
1509
1529
|
* Subscribe to lifecycle events via {@link on}, abort via {@link abort}, and
|
|
1510
1530
|
* start execution with {@link run}.
|
|
@@ -1529,27 +1549,30 @@ interface RunContext<S extends object> {
|
|
|
1529
1549
|
*/
|
|
1530
1550
|
run(): Promise<Result<PipelineRunResult<S>>>;
|
|
1531
1551
|
}
|
|
1532
|
-
/** Minimal logger interface used by the
|
|
1552
|
+
/** Minimal logger interface used by the factory. */
|
|
1533
1553
|
interface EngineLogger {
|
|
1534
1554
|
info: (msg: string, ctx?: any) => void;
|
|
1535
1555
|
error: (msg: string, ctx?: any) => void;
|
|
1556
|
+
warn: (msg: string, ctx?: any) => void;
|
|
1536
1557
|
}
|
|
1537
1558
|
/**
|
|
1538
|
-
* Configuration for {@link
|
|
1559
|
+
* Configuration for {@link PipelineFactory}.
|
|
1539
1560
|
*
|
|
1540
1561
|
* @typeParam S - The shape of the state managed by the pipeline.
|
|
1541
1562
|
*/
|
|
1542
|
-
interface
|
|
1563
|
+
interface PipelineFactoryOptions<S extends object = State> {
|
|
1543
1564
|
/** Logger (defaults to a no‑op). */
|
|
1544
1565
|
logger?: EngineLogger;
|
|
1545
1566
|
/**
|
|
1546
1567
|
* **Required.** A factory that, given a `runId`, returns a `DataStore<S>`.
|
|
1547
|
-
* The
|
|
1548
|
-
* isolated store instance.
|
|
1568
|
+
* The factory does **not** hold a global store; each run receives its own
|
|
1569
|
+
* isolated store instance from this function.
|
|
1549
1570
|
*
|
|
1550
|
-
* For a new run (`prepare`), the
|
|
1571
|
+
* For a new run (`prepare`), the function should return an empty store.
|
|
1551
1572
|
* For a resumed run (`resume`), it must return the **same** store that was
|
|
1552
1573
|
* used during the original run (so that state and checkpoints are preserved).
|
|
1574
|
+
*
|
|
1575
|
+
* The returned store is owned by the run, not by PipelineFactory.
|
|
1553
1576
|
*/
|
|
1554
1577
|
storeFactory: (runId: string) => Promise<DataStore<S>>;
|
|
1555
1578
|
/**
|
|
@@ -1558,37 +1581,68 @@ interface PipelineEngineOptions<S extends object = State> {
|
|
|
1558
1581
|
* If omitted, the store remains empty.
|
|
1559
1582
|
*/
|
|
1560
1583
|
initialStateFactory?: () => S;
|
|
1584
|
+
/**
|
|
1585
|
+
* Optional registry to register every RunContext with after construction.
|
|
1586
|
+
* When provided, prepare() and resume() call registry.register() before
|
|
1587
|
+
* returning, passing both the RunContext and the run's DataStore so the
|
|
1588
|
+
* registry can drive deferred export without needing a reference to the factory.
|
|
1589
|
+
*/
|
|
1590
|
+
registry?: PipelineRegistryBinding<S>;
|
|
1561
1591
|
}
|
|
1562
1592
|
/**
|
|
1563
|
-
*
|
|
1593
|
+
* Narrow interface the factory uses to register runs with the registry.
|
|
1594
|
+
* Typed as an interface rather than the full PipelineRegistry class so that
|
|
1595
|
+
* the factory remains decoupled from the registry's full surface.
|
|
1596
|
+
*/
|
|
1597
|
+
interface PipelineRegistryBinding<S extends object> {
|
|
1598
|
+
has(id: string): boolean;
|
|
1599
|
+
register(context: RunContext<S>, store: DataStore<S>): void;
|
|
1600
|
+
/**
|
|
1601
|
+
* Called by resume() before constructing a new RunContext.
|
|
1602
|
+
* Returns the existing live RunContext if the registry still holds a strong
|
|
1603
|
+
* reference for this runId (i.e. the pause timeout has not yet fired).
|
|
1604
|
+
* Returns null if no live context exists and reconstruction is needed.
|
|
1605
|
+
*/
|
|
1606
|
+
getLiveContext(runId: string): RunContext<S> | null;
|
|
1607
|
+
/** Returns the checkpoint for a paused run, if available. */
|
|
1608
|
+
getCheckpoint(runId: string): PipelineCheckpoint | null;
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* A pure factory for creating and resuming pipeline runs.
|
|
1612
|
+
*
|
|
1613
|
+
* PipelineFactory is stateless between runs. It holds only the pipeline
|
|
1614
|
+
* definition (topology), a storeFactory function, and an optional logger and
|
|
1615
|
+
* registry binding. It does not own any DataStore, timer, or live run state.
|
|
1564
1616
|
*
|
|
1565
1617
|
* @typeParam S - The shape of the state object.
|
|
1566
1618
|
*
|
|
1567
1619
|
* @example
|
|
1568
1620
|
* ```ts
|
|
1569
|
-
* const
|
|
1621
|
+
* const factory = new PipelineFactory(myDefinition, {
|
|
1570
1622
|
* storeFactory: (runId) => createMyStore(runId),
|
|
1571
1623
|
* initialStateFactory: () => ({ counter: 0 }),
|
|
1624
|
+
* registry: PipelineRegistry.default,
|
|
1572
1625
|
* });
|
|
1573
1626
|
*
|
|
1574
|
-
* const ctx = await
|
|
1627
|
+
* const ctx = await factory.prepare({ stage: "validate" });
|
|
1575
1628
|
* ctx.on("pipeline:success", (e) => console.log("done", e.finalState));
|
|
1576
1629
|
* const result = await ctx.run();
|
|
1577
1630
|
* ```
|
|
1578
1631
|
*/
|
|
1579
|
-
declare class
|
|
1632
|
+
declare class PipelineFactory<S extends object> {
|
|
1580
1633
|
private readonly definition;
|
|
1581
1634
|
private readonly logger;
|
|
1582
1635
|
private readonly storeFactory;
|
|
1583
1636
|
private readonly initialStateFactory;
|
|
1637
|
+
private readonly registry;
|
|
1584
1638
|
private readonly index;
|
|
1585
1639
|
/**
|
|
1586
|
-
* Constructs
|
|
1640
|
+
* Constructs a factory tied to a fixed pipeline definition.
|
|
1587
1641
|
*
|
|
1588
1642
|
* @param definition - The pipeline topology (stages, steps, routers).
|
|
1589
|
-
* @param options -
|
|
1643
|
+
* @param options - Factory configuration. `storeFactory` is mandatory.
|
|
1590
1644
|
*/
|
|
1591
|
-
constructor(definition: RoutingPipelineDefinition<S>, options:
|
|
1645
|
+
constructor(definition: RoutingPipelineDefinition<S>, options: PipelineFactoryOptions<S>);
|
|
1592
1646
|
/**
|
|
1593
1647
|
* Prepare an isolated {@link RunContext} for a **new** run.
|
|
1594
1648
|
*
|
|
@@ -1596,6 +1650,9 @@ declare class PipelineEngine<S extends object> {
|
|
|
1596
1650
|
* initial state from `initialStateFactory`. No execution occurs until
|
|
1597
1651
|
* `runContext.run()` is called.
|
|
1598
1652
|
*
|
|
1653
|
+
* If a registry is configured, the context and store are registered before
|
|
1654
|
+
* this method returns.
|
|
1655
|
+
*
|
|
1599
1656
|
* @param entry - Optional address describing where to start execution.
|
|
1600
1657
|
* Omit to start from the lowest‑order stage.
|
|
1601
1658
|
* @param runId - Optional run identifier (UUID v7). Generated if omitted.
|
|
@@ -1605,9 +1662,18 @@ declare class PipelineEngine<S extends object> {
|
|
|
1605
1662
|
/**
|
|
1606
1663
|
* Reconstruct a {@link RunContext} for a previously paused run.
|
|
1607
1664
|
*
|
|
1608
|
-
*
|
|
1609
|
-
*
|
|
1610
|
-
*
|
|
1665
|
+
* **Fast path (registry hit):** If a registry is configured and still holds
|
|
1666
|
+
* a live strong reference for `runId` (i.e. the pause timeout has not fired),
|
|
1667
|
+
* the existing RunContext is returned directly. No store read, no
|
|
1668
|
+
* ArtifactContainer.from() — zero serialization cost.
|
|
1669
|
+
*
|
|
1670
|
+
* **Slow path (store reconstruction):** If no live context exists (timeout
|
|
1671
|
+
* expired, process restarted, or no registry), the store is read, the
|
|
1672
|
+
* checkpoint is validated, and a fresh RunContext is built from the
|
|
1673
|
+
* containerBundle (if the registry wrote one at expiry) or from scratch.
|
|
1674
|
+
*
|
|
1675
|
+
* The context is registered with the registry before this method returns
|
|
1676
|
+
* (slow path only — the fast path context is already registered).
|
|
1611
1677
|
*
|
|
1612
1678
|
* @param runId - The identifier of the paused run.
|
|
1613
1679
|
* @returns A result containing the `RunContext`, or a failure if no
|
|
@@ -1619,7 +1685,7 @@ declare class PipelineEngine<S extends object> {
|
|
|
1619
1685
|
*
|
|
1620
1686
|
* This method is package-internal. External callers use prepare() or resume().
|
|
1621
1687
|
* Sub-pipelines access it through the {@link SubPipelineFactory} interface,
|
|
1622
|
-
* which is the only reference RunContextImpl holds to the
|
|
1688
|
+
* which is the only reference RunContextImpl holds to the factory.
|
|
1623
1689
|
*
|
|
1624
1690
|
* A single {@link ArtifactContainer} is created per run at root level and
|
|
1625
1691
|
* shared across all sub-pipelines. Steps are registered under namespaced keys
|
|
@@ -1630,16 +1696,330 @@ declare class PipelineEngine<S extends object> {
|
|
|
1630
1696
|
* threaded into {@link registerSteps} so that every step factory closure
|
|
1631
1697
|
* captures the correct signal at registration time.
|
|
1632
1698
|
*
|
|
1633
|
-
* @param runId
|
|
1634
|
-
* @param pipeline
|
|
1635
|
-
* @param entry
|
|
1636
|
-
* @param store
|
|
1637
|
-
* @param parentBus
|
|
1638
|
-
* @param parentPath
|
|
1639
|
-
* @param container
|
|
1640
|
-
* @param keyPrefix
|
|
1699
|
+
* @param runId - The run identifier (same for parent and sub‑pipelines).
|
|
1700
|
+
* @param pipeline - The pipeline (sub‑)definition.
|
|
1701
|
+
* @param entry - Entry address; `undefined` means start from the beginning.
|
|
1702
|
+
* @param store - The shared store instance for this run.
|
|
1703
|
+
* @param parentBus - Parent scoped bus for event bubbling (root runs pass undefined).
|
|
1704
|
+
* @param parentPath - Event path from the root.
|
|
1705
|
+
* @param container - Shared container (created once at root; passed down to sub-pipelines).
|
|
1706
|
+
* @param keyPrefix - Namespacing prefix for artifact keys within the shared container.
|
|
1707
|
+
* @param artifactBundle - Optional exported container state for store-based resume.
|
|
1641
1708
|
*/
|
|
1642
1709
|
buildRunContext(runId: string, pipeline: RoutingPipelineDefinition<S>, entry: EntryAddress | undefined, store: DataStore<S>, parentBus: ScopedEventBus<S> | undefined, parentPath: EventPath, container?: ArtifactContainer<Record<string, DeepPartial<S>>, S>, keyPrefix?: string, artifactBundle?: ExportedContainerState): Promise<RunContext<S>>;
|
|
1643
1710
|
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Writes a checkpoint into the store using the typed EngineStoreEnvelope.
|
|
1713
|
+
* The write is synchronous from the store's perspective (the transaction
|
|
1714
|
+
* boundary lives in the caller). No `any` cast is required because
|
|
1715
|
+
* EngineStoreEnvelope fully types the factory-internal key namespace.
|
|
1716
|
+
*
|
|
1717
|
+
* Exported so the PipelineRegistry can update an existing checkpoint with a
|
|
1718
|
+
* containerBundle at timeout expiry without duplicating the write logic.
|
|
1719
|
+
*/
|
|
1720
|
+
declare function writeCheckpoint(store: DataStore<any>, checkpoint: PipelineCheckpoint): Promise<void>;
|
|
1721
|
+
/**
|
|
1722
|
+
* Reads a checkpoint from the store using the typed EngineStoreEnvelope.
|
|
1723
|
+
* Returns null if no checkpoint exists for the given pipelineId / runId pair.
|
|
1724
|
+
* Synchronous — the store's get() is a synchronous snapshot read.
|
|
1725
|
+
*/
|
|
1726
|
+
declare function readCheckpoint(store: DataStore<any>, pipelineId: string | undefined, runId: string): PipelineCheckpoint | null;
|
|
1727
|
+
/**
|
|
1728
|
+
* Removes a checkpoint from the store.
|
|
1729
|
+
* Called by the registry when a paused run expires and no resume arrived,
|
|
1730
|
+
* after the containerBundle (if any) has been written into the checkpoint.
|
|
1731
|
+
* Exported so the registry can clear stale checkpoints without reaching into
|
|
1732
|
+
* the factory's internals.
|
|
1733
|
+
*/
|
|
1734
|
+
declare function clearCheckpoint(store: DataStore<any>, pipelineId: string, runId: string): Promise<void>;
|
|
1735
|
+
|
|
1736
|
+
/**
|
|
1737
|
+
* @file pipeline-registry.ts
|
|
1738
|
+
* @description Stateful coordination layer for pipeline runs.
|
|
1739
|
+
*
|
|
1740
|
+
* The registry is the single source of truth for live run state. It owns:
|
|
1741
|
+
*
|
|
1742
|
+
* 1. Strong references to RunContext instances — it decides when a context
|
|
1743
|
+
* becomes GC-eligible by nulling the reference inside its RunRecord.
|
|
1744
|
+
*
|
|
1745
|
+
* 2. Pause timers — armed when a pipeline pauses, cancelled when resume()
|
|
1746
|
+
* is called before expiry.
|
|
1747
|
+
*
|
|
1748
|
+
* 3. Deferred container export — on timer expiry the registry exports the
|
|
1749
|
+
* ArtifactContainer and writes the bundle into the persisted checkpoint
|
|
1750
|
+
* via writeCheckpoint(), then nulls the context reference.
|
|
1751
|
+
*
|
|
1752
|
+
* 4. A WeakRef index — allows external observers to list and watch runs
|
|
1753
|
+
* without the registry preventing GC of completed or expired records.
|
|
1754
|
+
*
|
|
1755
|
+
* 5. A FinalizationRegistry — passively removes stale WeakRef entries from
|
|
1756
|
+
* the index after GC collects a context nobody else held.
|
|
1757
|
+
*
|
|
1758
|
+
* GC eligibility rules:
|
|
1759
|
+
* - Completed runs (succeeded / failed): the registry nulls the strong ref
|
|
1760
|
+
* immediately on the terminal event. If no external caller holds a reference
|
|
1761
|
+
* the context is GC-eligible right away.
|
|
1762
|
+
* - Paused runs: the registry holds the strong ref for `pauseTimeout` ms
|
|
1763
|
+
* (from the PauseInstruction, or the registry default). During that window
|
|
1764
|
+
* resume() returns the live context directly — zero reconstruction cost.
|
|
1765
|
+
* When the timer fires the registry exports, updates the checkpoint, nulls
|
|
1766
|
+
* the ref, and fires onExpired.
|
|
1767
|
+
*
|
|
1768
|
+
* Named instances:
|
|
1769
|
+
* PipelineRegistry.default — module-level singleton, importable from anywhere.
|
|
1770
|
+
* PipelineRegistry.get(name) — named registries for test isolation or domain
|
|
1771
|
+
* separation within the same process.
|
|
1772
|
+
*/
|
|
1773
|
+
|
|
1774
|
+
/**
|
|
1775
|
+
* The status of a run as tracked by the registry.
|
|
1776
|
+
* Mirrors PipelineRunResult["status"] but adds "running" for in-flight runs.
|
|
1777
|
+
*/
|
|
1778
|
+
type RunStatus = "prepared" | "running" | "paused" | "succeeded" | "failed";
|
|
1779
|
+
/**
|
|
1780
|
+
* A read-only snapshot of a run's metadata, returned by list() and get().
|
|
1781
|
+
*
|
|
1782
|
+
* `context` is the live RunContext if the registry still holds a strong
|
|
1783
|
+
* reference (i.e. the run has not completed/expired). It is null once the
|
|
1784
|
+
* context has been released. Callers who receive a non-null context should
|
|
1785
|
+
* not store it long-term — they will prevent GC if they do.
|
|
1786
|
+
*/
|
|
1787
|
+
interface RunInfo<S extends object> {
|
|
1788
|
+
readonly runId: string;
|
|
1789
|
+
readonly pipelineId: string;
|
|
1790
|
+
readonly status: RunStatus;
|
|
1791
|
+
readonly startedAt: string;
|
|
1792
|
+
readonly pausedAt: string | undefined;
|
|
1793
|
+
readonly checkpoint: PipelineCheckpoint | undefined;
|
|
1794
|
+
readonly context: RunContext<S> | null;
|
|
1795
|
+
}
|
|
1796
|
+
/**
|
|
1797
|
+
* Configuration for a {@link PipelineRegistry} instance.
|
|
1798
|
+
*/
|
|
1799
|
+
interface PipelineRegistryOptions {
|
|
1800
|
+
/**
|
|
1801
|
+
* Default pause timeout in milliseconds.
|
|
1802
|
+
* Used when a PauseInstruction carries no explicit `timeout` field.
|
|
1803
|
+
* Defaults to 5 minutes (300_000 ms).
|
|
1804
|
+
*/
|
|
1805
|
+
defaultPauseTimeout?: number;
|
|
1806
|
+
/**
|
|
1807
|
+
* Called when a paused run's timeout fires and the context is about to
|
|
1808
|
+
* be released. Receives the runId and the final checkpoint (which now
|
|
1809
|
+
* includes the containerBundle if export succeeded).
|
|
1810
|
+
*
|
|
1811
|
+
* Use this to notify external systems (e.g. send a "run expired" webhook,
|
|
1812
|
+
* update a database record, schedule a retry).
|
|
1813
|
+
*/
|
|
1814
|
+
onExpired?: (runId: string, checkpoint: PipelineCheckpoint) => void;
|
|
1815
|
+
/**
|
|
1816
|
+
* Called when a container export fails at timeout expiry.
|
|
1817
|
+
* The checkpoint is cleared from the store and the context is still
|
|
1818
|
+
* released, but without a containerBundle — a subsequent resume() would
|
|
1819
|
+
* need to rebuild the container from scratch via the factory's slow path.
|
|
1820
|
+
*/
|
|
1821
|
+
onExportFailed?: (runId: string, error: unknown) => void;
|
|
1822
|
+
/** Logger. Defaults to console. */
|
|
1823
|
+
logger?: EngineLogger;
|
|
1824
|
+
}
|
|
1825
|
+
/**
|
|
1826
|
+
* Stateful coordination layer for pipeline runs.
|
|
1827
|
+
*
|
|
1828
|
+
* @typeParam S - The state shape. Use the base `State` type when the registry
|
|
1829
|
+
* manages runs across multiple pipeline definitions with different
|
|
1830
|
+
* state shapes (the common case for a module-level singleton).
|
|
1831
|
+
*
|
|
1832
|
+
* @example
|
|
1833
|
+
* ```ts
|
|
1834
|
+
* // Module-level singleton — importable from anywhere
|
|
1835
|
+
* import { PipelineRegistry } from "./pipeline-registry";
|
|
1836
|
+
*
|
|
1837
|
+
* // In your factory setup:
|
|
1838
|
+
* const factory = new PipelineFactory(definition, {
|
|
1839
|
+
* storeFactory,
|
|
1840
|
+
* registry: PipelineRegistry.default,
|
|
1841
|
+
* });
|
|
1842
|
+
*
|
|
1843
|
+
* // From anywhere in the app:
|
|
1844
|
+
* const runs = PipelineRegistry.default.list();
|
|
1845
|
+
* const info = PipelineRegistry.default.get(runId);
|
|
1846
|
+
* ```
|
|
1847
|
+
*/
|
|
1848
|
+
declare class PipelineRegistry<S extends object = State> implements PipelineRegistryBinding<S> {
|
|
1849
|
+
private static readonly instances;
|
|
1850
|
+
/**
|
|
1851
|
+
* The module-level default registry.
|
|
1852
|
+
* Importable from anywhere in the process. Manages runs across all pipeline
|
|
1853
|
+
* factories that reference it.
|
|
1854
|
+
*/
|
|
1855
|
+
static readonly default: PipelineRegistry<any>;
|
|
1856
|
+
/**
|
|
1857
|
+
* Returns a named registry, creating it on first access.
|
|
1858
|
+
* Useful for test isolation (each test suite gets its own registry) or
|
|
1859
|
+
* domain separation (one registry per bounded context).
|
|
1860
|
+
*
|
|
1861
|
+
* @param name - A stable identifier for the registry instance.
|
|
1862
|
+
* @param options - Applied only on first creation; ignored on subsequent calls.
|
|
1863
|
+
*/
|
|
1864
|
+
static get<S extends object = State>(name: string, options?: PipelineRegistryOptions): PipelineRegistry<S>;
|
|
1865
|
+
/**
|
|
1866
|
+
* Destroys a named registry instance, cancelling all active timers and
|
|
1867
|
+
* releasing all strong references. The instance is removed from the
|
|
1868
|
+
* static map so the next call to get(name) creates a fresh registry.
|
|
1869
|
+
*
|
|
1870
|
+
* Primarily useful in tests.
|
|
1871
|
+
*/
|
|
1872
|
+
static destroy(name: string): void;
|
|
1873
|
+
private readonly defaultPauseTimeout;
|
|
1874
|
+
private readonly onExpired;
|
|
1875
|
+
private readonly onExportFailed;
|
|
1876
|
+
private readonly logger;
|
|
1877
|
+
/**
|
|
1878
|
+
* Strong-reference map. The registry is the last strong holder of each
|
|
1879
|
+
* RunContext. Nulling record.context is the moment GC eligibility begins.
|
|
1880
|
+
*/
|
|
1881
|
+
private readonly records;
|
|
1882
|
+
/**
|
|
1883
|
+
* Weak-reference index. Allows external callers to observe runs without
|
|
1884
|
+
* the registry preventing GC of completed or expired contexts.
|
|
1885
|
+
* Entries are added in register() and removed either explicitly in
|
|
1886
|
+
* releaseContext() or passively by the FinalizationRegistry callback.
|
|
1887
|
+
*/
|
|
1888
|
+
private readonly weakIndex;
|
|
1889
|
+
/**
|
|
1890
|
+
* Passively cleans up stale WeakRef entries after GC collects a context.
|
|
1891
|
+
* The held value is the runId so the callback can remove the correct entry.
|
|
1892
|
+
*/
|
|
1893
|
+
private readonly finalizer;
|
|
1894
|
+
constructor(options?: PipelineRegistryOptions);
|
|
1895
|
+
/**
|
|
1896
|
+
* Registers a new RunContext with the registry.
|
|
1897
|
+
*
|
|
1898
|
+
* Called by PipelineFactory.prepare() and PipelineFactory.resume() (slow
|
|
1899
|
+
* path) immediately after constructing a RunContext. The store is passed
|
|
1900
|
+
* here so the registry never needs to reach back into the factory.
|
|
1901
|
+
*
|
|
1902
|
+
* The registry wires pipeline:paused, pipeline:success, and pipeline:failure
|
|
1903
|
+
* listeners onto the context's event bus. These listeners drive all
|
|
1904
|
+
* subsequent state transitions (arming timers, releasing refs, etc.).
|
|
1905
|
+
*
|
|
1906
|
+
* `container` is extracted from the context via the RegistrableRunContext
|
|
1907
|
+
* interface — a narrow extension of RunContext that RunContextImpl satisfies
|
|
1908
|
+
* by exposing its container reference for registry use only.
|
|
1909
|
+
*
|
|
1910
|
+
* @param context - The RunContext to track.
|
|
1911
|
+
* @param store - The run's DataStore (owned by the run, borrowed by registry).
|
|
1912
|
+
*/
|
|
1913
|
+
register(context: RunContext<S>, store: DataStore<S>): void;
|
|
1914
|
+
getCheckpoint(runId: string): PipelineCheckpoint | null;
|
|
1915
|
+
/**
|
|
1916
|
+
* Returns the live RunContext for a paused run if the registry still holds
|
|
1917
|
+
* a strong reference (i.e. the pause timeout has not yet fired).
|
|
1918
|
+
*
|
|
1919
|
+
* Called by PipelineFactory.resume() as the fast path check. If this
|
|
1920
|
+
* returns non-null, the factory skips store reconstruction entirely.
|
|
1921
|
+
*
|
|
1922
|
+
* The timer is cancelled here — the caller (factory) takes responsibility
|
|
1923
|
+
* for the context from this point and will re-register it via register()
|
|
1924
|
+
* after wiring new event listeners.
|
|
1925
|
+
*
|
|
1926
|
+
* @param runId - The run identifier to look up.
|
|
1927
|
+
* @returns The live RunContext, or null if not found / already released.
|
|
1928
|
+
*/
|
|
1929
|
+
getLiveContext(runId: string): RunContext<S> | null;
|
|
1930
|
+
/**
|
|
1931
|
+
* Returns a snapshot of all tracked runs regardless of status.
|
|
1932
|
+
* Completed and expired runs remain in the map until explicitly pruned
|
|
1933
|
+
* via prune() or dispose().
|
|
1934
|
+
*/
|
|
1935
|
+
list(): RunInfo<S>[];
|
|
1936
|
+
/**
|
|
1937
|
+
* Returns runs matching a given status.
|
|
1938
|
+
*
|
|
1939
|
+
* @param status - Filter by run status.
|
|
1940
|
+
*/
|
|
1941
|
+
listByStatus(status: RunStatus): RunInfo<S>[];
|
|
1942
|
+
/**
|
|
1943
|
+
* Checks if a run id is registered
|
|
1944
|
+
* @param runId - The run identifier.
|
|
1945
|
+
*/
|
|
1946
|
+
has(runId: string): boolean;
|
|
1947
|
+
/**
|
|
1948
|
+
* Returns the RunInfo for a specific run, or undefined if not tracked.
|
|
1949
|
+
*
|
|
1950
|
+
* @param runId - The run identifier.
|
|
1951
|
+
*/
|
|
1952
|
+
get(runId: string): RunInfo<S> | undefined;
|
|
1953
|
+
/**
|
|
1954
|
+
* Removes all terminal runs (succeeded / failed) and expired paused runs
|
|
1955
|
+
* (context already null) from the records map.
|
|
1956
|
+
*
|
|
1957
|
+
* Call periodically or on a schedule to prevent unbounded memory growth in
|
|
1958
|
+
* long-running processes.
|
|
1959
|
+
*/
|
|
1960
|
+
prune(): void;
|
|
1961
|
+
/**
|
|
1962
|
+
* Cancels all active timers, releases all strong context references, and
|
|
1963
|
+
* unsubscribes all event listeners.
|
|
1964
|
+
*
|
|
1965
|
+
* Does not clear the records map — call prune() afterward if needed.
|
|
1966
|
+
* After dispose() the registry should not be used further.
|
|
1967
|
+
*/
|
|
1968
|
+
dispose(): void;
|
|
1969
|
+
private onRun;
|
|
1970
|
+
private onPaused;
|
|
1971
|
+
private onSucceeded;
|
|
1972
|
+
private onFailed;
|
|
1973
|
+
/**
|
|
1974
|
+
* Runs when the pause timeout fires (or immediately when timeout === 0).
|
|
1975
|
+
*
|
|
1976
|
+
* Sequence:
|
|
1977
|
+
* 1. Export the ArtifactContainer to get the bundle.
|
|
1978
|
+
* 2. Write the bundle into the persisted checkpoint via writeCheckpoint().
|
|
1979
|
+
* 3. Null the strong context reference → GC-eligible.
|
|
1980
|
+
* 4. Call onExpired callback.
|
|
1981
|
+
*
|
|
1982
|
+
* On export failure:
|
|
1983
|
+
* 1. Log the error and call onExportFailed.
|
|
1984
|
+
* 2. Clear the checkpoint from the store entirely (no partial bundle).
|
|
1985
|
+
* 3. Null the strong ref anyway — we cannot hold it indefinitely.
|
|
1986
|
+
*
|
|
1987
|
+
* The store is not closed here — the registry borrowed it from the run;
|
|
1988
|
+
* the storeFactory caller owns its lifecycle.
|
|
1989
|
+
*/
|
|
1990
|
+
private runExpirySequence;
|
|
1991
|
+
private cancelTimer;
|
|
1992
|
+
private unwire;
|
|
1993
|
+
/**
|
|
1994
|
+
* Nulls the strong context reference and removes the WeakRef from the index.
|
|
1995
|
+
* After this call the context is GC-eligible if no external holder remains.
|
|
1996
|
+
*/
|
|
1997
|
+
private releaseContext;
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
2000
|
+
* A narrow extension of RunContext that RunContextImpl satisfies internally.
|
|
2001
|
+
*
|
|
2002
|
+
* These two fields are the only things the registry needs from the context
|
|
2003
|
+
* beyond the public RunContext interface. They are not part of the public
|
|
2004
|
+
* surface — callers accessing a RunContext never see them. The registry casts
|
|
2005
|
+
* to this interface inside register() to extract them.
|
|
2006
|
+
*
|
|
2007
|
+
* RunContextImpl must be updated to expose these two fields so the registry
|
|
2008
|
+
* can access them. Since RunContextImpl is package-private, this coupling is
|
|
2009
|
+
* contained within the package and does not leak to consumers.
|
|
2010
|
+
*/
|
|
2011
|
+
interface RegistrableRunContext<S extends object> extends RunContext<S> {
|
|
2012
|
+
/**
|
|
2013
|
+
* The shared ArtifactContainer for this run.
|
|
2014
|
+
* Used by the registry for deferred export at pause timeout.
|
|
2015
|
+
* Tagged with double-underscore to signal it is registry-internal.
|
|
2016
|
+
*/
|
|
2017
|
+
readonly __registryContainer: ArtifactContainer<any, S>;
|
|
2018
|
+
/**
|
|
2019
|
+
* The pipeline definition id this run belongs to.
|
|
2020
|
+
* Used by the registry to populate RunRecord.pipelineId.
|
|
2021
|
+
*/
|
|
2022
|
+
readonly __registryPipelineId: string;
|
|
2023
|
+
}
|
|
1644
2024
|
|
|
1645
|
-
export { type EngineLogger, type EntryAddress, type EventPath, PIPELINE_DATA_KEY, type PathNode, type PauseInstruction, Pipeline, type PipelineCheckpoint, type PipelineContext, type PipelineDefinition,
|
|
2025
|
+
export { type EngineLogger, type EntryAddress, type EventPath, PIPELINE_DATA_KEY, type PathNode, type PauseInstruction, Pipeline, type PipelineCheckpoint, type PipelineContext, type PipelineDefinition, type PipelineEventMap, PipelineFactory, type PipelineFactoryOptions, PipelineRegistry, type PipelineRegistryBinding, type PipelineRegistryOptions, type PipelineRunResult, type PipelineStageRouter, type PipelineStep, type PipelineStepDefinition, type RegistrableRunContext, Result, type RoutingInstruction, type RoutingPipelineDefinition, type RunContext, type RunEventHandler, type RunEventMap, type RunInfo, type RunStatus, SequentialExecutionContext, type Stage, type StageCarry, type State, type Step, type StepStageRouter, type SubPipelineAddress, clearCheckpoint, isPauseInstruction, readCheckpoint, writeCheckpoint };
|