@asaidimu/utils-pipeline 1.0.0 → 1.0.2
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/index.d.mts +1232 -89
- package/index.d.ts +1232 -89
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/package.json +2 -2
package/index.d.mts
CHANGED
|
@@ -282,94 +282,6 @@ type PipelineStepDefinition = Omit<PipelineStep, "order">;
|
|
|
282
282
|
* Outer array index → stage `order`. Inner arrays → parallel slots within a stage.
|
|
283
283
|
*/
|
|
284
284
|
type PipelineDefinition = Array<PipelineStepDefinition | PipelineStepDefinition[]>;
|
|
285
|
-
/**
|
|
286
|
-
* Returned by a routing step action to redirect execution to another step.
|
|
287
|
-
* Use the `routeTo()` helper to construct this.
|
|
288
|
-
*/
|
|
289
|
-
interface RoutingInstruction {
|
|
290
|
-
/** Key of the step to jump to. May refer to any registered step, including earlier ones. */
|
|
291
|
-
next: string;
|
|
292
|
-
/** Value to place in the carry under the target step's key before jumping. */
|
|
293
|
-
value: any;
|
|
294
|
-
/** Maximum number of times this loop point may be visited before the engine hard-fails. */
|
|
295
|
-
maxIterations?: number;
|
|
296
|
-
/**
|
|
297
|
-
* Optional guard evaluated after each iteration increment.
|
|
298
|
-
* Return false to terminate the loop cleanly (emits `loop:terminated` with reason
|
|
299
|
-
* `"condition_failed"` and exits with the last successful carry).
|
|
300
|
-
*/
|
|
301
|
-
condition?: (carry: StageCarry, iteration: number) => boolean | Promise<boolean>;
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Context injected into every routing step action.
|
|
305
|
-
* Analogous to `ArtifactFactoryContext` — the step interacts with the engine through this.
|
|
306
|
-
*/
|
|
307
|
-
interface PipelineStepContext {
|
|
308
|
-
/**
|
|
309
|
-
* Abort signal scoped to this execution run.
|
|
310
|
-
* Check this inside long-running actions to respect cancellation.
|
|
311
|
-
*/
|
|
312
|
-
signal: AbortSignal;
|
|
313
|
-
/**
|
|
314
|
-
* Reads a value from the current carry AND registers a dependency edge in the
|
|
315
|
-
* execution graph so the engine knows which carry keys this step depends on.
|
|
316
|
-
*
|
|
317
|
-
* Steps with no `use()` calls have no declared dependencies and will always
|
|
318
|
-
* re-execute when their stage is revisited — the safe conservative fallback.
|
|
319
|
-
*
|
|
320
|
-
* @param key The carry key to read (always the key of a previous step).
|
|
321
|
-
* @returns The value stored under that key in the current carry.
|
|
322
|
-
*/
|
|
323
|
-
use<K extends string>(key: K): StageCarry[K];
|
|
324
|
-
/**
|
|
325
|
-
* The number of times this specific step has been executed in the current
|
|
326
|
-
* routing chain. Zero on first execution, increments on each re-visit.
|
|
327
|
-
* Useful for backoff logic or conditional behaviour without external state.
|
|
328
|
-
*/
|
|
329
|
-
iteration: number;
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* A single executable step in a routing pipeline.
|
|
333
|
-
* May return a plain result OR a `RoutingInstruction` to redirect execution.
|
|
334
|
-
*
|
|
335
|
-
* @template TIn Shape of the incoming StageCarry this step expects to read.
|
|
336
|
-
* @template TOut Value type this step produces on a non-routing success.
|
|
337
|
-
*/
|
|
338
|
-
interface RoutingPipelineStep<TIn extends StageCarry = StageCarry, TOut = any> {
|
|
339
|
-
/** Unique identifier. Used as the carry key for this step's output. */
|
|
340
|
-
key: string;
|
|
341
|
-
/** Execution order. Steps sharing the same order run concurrently. */
|
|
342
|
-
order: number;
|
|
343
|
-
/**
|
|
344
|
-
* Business logic for this step.
|
|
345
|
-
* @param input Accumulated carry from all previous stages.
|
|
346
|
-
* @param ctx Engine-provided context: signal, use(), iteration.
|
|
347
|
-
* @returns A Result wrapping either a plain output value or a RoutingInstruction.
|
|
348
|
-
*/
|
|
349
|
-
action: (input: TIn, ctx: PipelineStepContext) => Promise<Result<TOut | RoutingInstruction>>;
|
|
350
|
-
}
|
|
351
|
-
/** Step definition without an explicit `order` — derived from array position. */
|
|
352
|
-
type RoutingPipelineStepDefinition = Omit<RoutingPipelineStep, "order">;
|
|
353
|
-
/**
|
|
354
|
-
* Declarative routing pipeline shape passed to the constructor.
|
|
355
|
-
* Outer array index → stage `order`. Inner arrays → parallel slots within a stage.
|
|
356
|
-
*/
|
|
357
|
-
type RoutingPipelineDefinition = Array<RoutingPipelineStepDefinition | RoutingPipelineStepDefinition[]>;
|
|
358
|
-
/** Per-step loop tracking state, maintained inside a RoutingExecutionContext. */
|
|
359
|
-
interface LoopContext {
|
|
360
|
-
/** Number of times this step has been visited in the current routing chain. */
|
|
361
|
-
iteration: number;
|
|
362
|
-
/** Hard ceiling on iterations for this step (if specified via routeTo options). */
|
|
363
|
-
maxIterations?: number;
|
|
364
|
-
/** Timestamp of the first visit — useful for timeout / telemetry. */
|
|
365
|
-
startTime: number;
|
|
366
|
-
/** History of values and source steps passed through this loop point. */
|
|
367
|
-
history: Array<{
|
|
368
|
-
value: any;
|
|
369
|
-
fromStep: string | undefined;
|
|
370
|
-
timestamp: number;
|
|
371
|
-
}>;
|
|
372
|
-
}
|
|
373
285
|
|
|
374
286
|
/**
|
|
375
287
|
* Isolated runtime for one sequential pipeline run.
|
|
@@ -499,4 +411,1235 @@ declare class Pipeline {
|
|
|
499
411
|
private sortObjectKeys;
|
|
500
412
|
}
|
|
501
413
|
|
|
502
|
-
|
|
414
|
+
/**
|
|
415
|
+
* Utility type for representing partial updates to the state, allowing deep nesting.
|
|
416
|
+
* It makes all properties optional and applies the same transformation recursively
|
|
417
|
+
* to nested objects and array elements, allowing for selective updates while
|
|
418
|
+
* preserving the original structure. It also includes the original type T and
|
|
419
|
+
* undefined as possibilities for the top level and nested values.
|
|
420
|
+
*/
|
|
421
|
+
type DeepPartial<T> = T extends object ? T extends readonly (infer U)[] ? readonly (DeepPartial<U> | undefined)[] | undefined | T : T extends (infer U)[] ? (DeepPartial<U> | undefined)[] | undefined | T : {
|
|
422
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> | undefined : T[K] | undefined;
|
|
423
|
+
} | undefined | T : T | undefined | symbol;
|
|
424
|
+
/**
|
|
425
|
+
* Extended store state for monitoring the current execution status (e.g., if an update is in progress).
|
|
426
|
+
*/
|
|
427
|
+
interface StoreExecutionState<T> {
|
|
428
|
+
/** Indicates if a state update process is currently executing. */
|
|
429
|
+
executing: boolean;
|
|
430
|
+
/** The changes (DeepPartial) currently being processed in the execution cycle. Null if none. */
|
|
431
|
+
changes: DeepPartial<T> | null;
|
|
432
|
+
/** A queue of pending state update functions/objects to be applied sequentially. */
|
|
433
|
+
pendingChanges: Array<StateUpdater<T>>;
|
|
434
|
+
/** Names of all currently registered middlewares. */
|
|
435
|
+
middlewares: string[];
|
|
436
|
+
/** Details of the middleware currently running. Null if none. */
|
|
437
|
+
runningMiddleware: {
|
|
438
|
+
id: string;
|
|
439
|
+
name: string;
|
|
440
|
+
startTime: number;
|
|
441
|
+
} | null;
|
|
442
|
+
/** Indicates if the store is currently within an active transaction block. */
|
|
443
|
+
transactionActive: boolean;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Event types emitted by the state store for observability and debugging.
|
|
447
|
+
*/
|
|
448
|
+
type StoreEvent = "update:start" | "update:complete" | "middleware:start" | "middleware:complete" | "middleware:error" | "middleware:blocked" | "middleware:executed" | "transaction:start" | "transaction:complete" | "transaction:error" | "persistence:ready" | "persistence:queued" | "persistence:success" | "persistence:retry" | "persistence:failed" | "persistence:queue_cleared" | "persistence:init_error" | "action:start" | "action:complete" | "action:error" | "selector:accessed" | "selector:changed";
|
|
449
|
+
/**
|
|
450
|
+
* Represents a state update, which can be:
|
|
451
|
+
* 1. The full new state (`T`).
|
|
452
|
+
* 2. A partial update object (`DeepPartial<T>`).
|
|
453
|
+
* 3. A function that receives the current state and returns a partial update (sync or async).
|
|
454
|
+
*/
|
|
455
|
+
type StateUpdater<T> = T | DeepPartial<T> | ((state: T) => DeepPartial<T> | Promise<DeepPartial<T>>);
|
|
456
|
+
/**
|
|
457
|
+
* Core types for the reactive data store
|
|
458
|
+
*/
|
|
459
|
+
/**
|
|
460
|
+
* Type for a Transform Middleware function.
|
|
461
|
+
* It modifies (transforms) the incoming changes and must return a `DeepPartial<T>`.
|
|
462
|
+
*/
|
|
463
|
+
type TransformMiddleware<T> = (state: T, changes: DeepPartial<T>) => Promise<DeepPartial<T>> | DeepPartial<T>;
|
|
464
|
+
/**
|
|
465
|
+
* Type for a Blocking Middleware function.
|
|
466
|
+
* It determines whether the state update should proceed or be blocked.
|
|
467
|
+
* It returns a boolean or an object containing a `block` boolean and an optional `error`.
|
|
468
|
+
*/
|
|
469
|
+
type BlockingMiddleware<T> = (state: T, changes: DeepPartial<T>) => Promise<boolean | {
|
|
470
|
+
block: boolean;
|
|
471
|
+
error?: Error;
|
|
472
|
+
}> | boolean | {
|
|
473
|
+
block: boolean;
|
|
474
|
+
error?: Error;
|
|
475
|
+
};
|
|
476
|
+
/**
|
|
477
|
+
* Type representing the configuration object passed to the `use` method to register a middleware.
|
|
478
|
+
*/
|
|
479
|
+
interface MiddlewareConfig<T> {
|
|
480
|
+
/** The middleware function (can be a transform or blocking middleware). */
|
|
481
|
+
action: TransformMiddleware<T> | BlockingMiddleware<T>;
|
|
482
|
+
/** An optional, human-readable name for the middleware. */
|
|
483
|
+
name?: string;
|
|
484
|
+
/** If true, the middleware is treated as a blocking middleware (must return a boolean or `{ block: boolean }`). */
|
|
485
|
+
block?: boolean;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Interface for a reactive selector result, providing access to the value and subscription capabilities.
|
|
489
|
+
*/
|
|
490
|
+
interface ReactiveSelector<S> {
|
|
491
|
+
/** Unique identifier for the selector. */
|
|
492
|
+
id: string;
|
|
493
|
+
/** Function to get the current computed value of the selector. */
|
|
494
|
+
get: () => S;
|
|
495
|
+
/**
|
|
496
|
+
* Subscribes a callback function to run whenever the selector's result changes.
|
|
497
|
+
* Returns an unsubscribe function.
|
|
498
|
+
*/
|
|
499
|
+
subscribe: (callback: (state: S) => void) => () => void;
|
|
500
|
+
}
|
|
501
|
+
interface ActionWatcher {
|
|
502
|
+
/** Unique identifier for the action */
|
|
503
|
+
name: string;
|
|
504
|
+
/** Function to get the current computed value of the action. */
|
|
505
|
+
status: () => boolean;
|
|
506
|
+
/**
|
|
507
|
+
* Subscribes a callback function to run whenever the action's status changes.
|
|
508
|
+
* Returns an unsubscribe function.
|
|
509
|
+
*/
|
|
510
|
+
subscribe: (callback: () => void) => () => void;
|
|
511
|
+
}
|
|
512
|
+
interface TransactionOptions {
|
|
513
|
+
/** If true, blocks resolution until changes are fully committed to the database row */
|
|
514
|
+
flush?: boolean;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Interface defining the contract for the core data state store.
|
|
518
|
+
* T must be an object type.
|
|
519
|
+
*/
|
|
520
|
+
interface DataStore<T extends object> {
|
|
521
|
+
/**
|
|
522
|
+
* Gets the current state of the store.
|
|
523
|
+
* @param clone If true, returns a deep clone of the state; otherwise, returns the internal state reference.
|
|
524
|
+
* @returns The current state T.
|
|
525
|
+
*/
|
|
526
|
+
get(clone?: boolean): T;
|
|
527
|
+
/**
|
|
528
|
+
* Gets a subset of the store.
|
|
529
|
+
* @param paths The paths which to include in the returned object.
|
|
530
|
+
* @param separator Optional separator for paths. Defaults to `.` .
|
|
531
|
+
* @returns An object of the shape K mapping paths to their current values.
|
|
532
|
+
*/
|
|
533
|
+
subset<K extends Record<string, any> = Record<string, any>>(paths: Array<string>, separator?: string): K;
|
|
534
|
+
/**
|
|
535
|
+
* Registers a named action function that can modify the state.
|
|
536
|
+
* @param action The action configuration object.
|
|
537
|
+
* @returns A function to unregister the action.
|
|
538
|
+
*/
|
|
539
|
+
register<R extends any[]>(action: {
|
|
540
|
+
name: string;
|
|
541
|
+
fn: (state: T, ...args: R) => DeepPartial<T> | Promise<DeepPartial<T>>;
|
|
542
|
+
debounce?: {
|
|
543
|
+
delay: number;
|
|
544
|
+
condition?: (previous: R, current: R) => boolean;
|
|
545
|
+
};
|
|
546
|
+
}): () => void;
|
|
547
|
+
/**
|
|
548
|
+
* Executes (dispatches) a previously registered action by its name.
|
|
549
|
+
* @param name The name of the action.
|
|
550
|
+
* @param args The parameters to pass to the action function.
|
|
551
|
+
* @returns A promise that resolves to the final state after the action and subsequent updates are complete.
|
|
552
|
+
*/
|
|
553
|
+
dispatch<R extends any[]>(name: string, ...args: R): Promise<T>;
|
|
554
|
+
/**
|
|
555
|
+
* Sets or updates the state using a StateUpdater.
|
|
556
|
+
* @param update The new state, partial state, or a function returning a partial state.
|
|
557
|
+
* @param options Configuration options for the set operation.
|
|
558
|
+
* @returns A promise that resolves to the current state when the update is complete.
|
|
559
|
+
*/
|
|
560
|
+
set(update: StateUpdater<T>, options?: {
|
|
561
|
+
force?: boolean;
|
|
562
|
+
actionId?: string;
|
|
563
|
+
}): Promise<T>;
|
|
564
|
+
/**
|
|
565
|
+
* Creates a reactive selector that computes a derived value and tracks dependencies.
|
|
566
|
+
* @param selector A function to compute the derived state value S from the full state T.
|
|
567
|
+
* @returns A `ReactiveSelector<S>` object.
|
|
568
|
+
*/
|
|
569
|
+
select<S>(selector: (state: T) => S): ReactiveSelector<S>;
|
|
570
|
+
/**
|
|
571
|
+
* Subscribes a callback to run when the data at the specified path(s) changes.
|
|
572
|
+
* @param path A single path string or an array of path strings to watch.
|
|
573
|
+
* @param callback The function to execute when a change occurs in the watched path(s).
|
|
574
|
+
* @param options Extra options to pass to the event bus
|
|
575
|
+
* @returns An unsubscribe function.
|
|
576
|
+
*/
|
|
577
|
+
watch(path: string | Array<string>, callback: (state: T) => void, options?: SubscribeOptions): () => void;
|
|
578
|
+
/**
|
|
579
|
+
* Subscribes to execution‑status changes of a registered action.
|
|
580
|
+
*
|
|
581
|
+
* The provided callback is called **every time** the action transitions
|
|
582
|
+
* between idle and running (i.e. on `action:start`, `action:complete`, or
|
|
583
|
+
* `action:error` for the given name). The current status can also be read
|
|
584
|
+
* synchronously with `isActionRunning(name)`.
|
|
585
|
+
*
|
|
586
|
+
* **Deferred listener teardown**
|
|
587
|
+
* To avoid unnecessary churn when subscriptions are rapidly created and
|
|
588
|
+
* destroyed, the underlying event listeners are not removed immediately
|
|
589
|
+
* on unsubscribe. Instead, a *pending reset* is queued via `queueMicrotask`.
|
|
590
|
+
* If a new subscription for the same action arrives before that microtask
|
|
591
|
+
* executes, the reset is silently cancelled and the already‑established
|
|
592
|
+
* listeners are reused. This keeps the subscription infrastructure stable
|
|
593
|
+
* and prevents cascading notifications that could otherwise arise from
|
|
594
|
+
* repeated subscribe‑unsubscribe‑subscribe cycles.
|
|
595
|
+
*
|
|
596
|
+
* @param name - The name of the action to watch.
|
|
597
|
+
* @returns An ActionWatcher
|
|
598
|
+
*/
|
|
599
|
+
watchAction(name: string): ActionWatcher;
|
|
600
|
+
/**
|
|
601
|
+
* Executes an operation function within a transaction block.
|
|
602
|
+
* All state updates (`set` or actions) within the transaction are batched and applied atomically (all or nothing).
|
|
603
|
+
* @param operation The function containing the state updates.
|
|
604
|
+
* @returns A promise that resolves to the return value of the operation function.
|
|
605
|
+
*/
|
|
606
|
+
transaction<R>(operation: () => R | Promise<R>, options?: TransactionOptions): Promise<R>;
|
|
607
|
+
/**
|
|
608
|
+
* Registers a middleware function to intercept state updates.
|
|
609
|
+
* @param props The middleware configuration.
|
|
610
|
+
* @returns A function to unregister the middleware.
|
|
611
|
+
*/
|
|
612
|
+
use(props: MiddlewareConfig<T>): () => boolean;
|
|
613
|
+
/**
|
|
614
|
+
* Subscribes a listener to a specific store event type.
|
|
615
|
+
* @param event The type of store event to listen for.
|
|
616
|
+
* @param listener The callback function to execute when the event fires.
|
|
617
|
+
* @returns An unsubscribe function.
|
|
618
|
+
*/
|
|
619
|
+
on(event: StoreEvent, listener: (data: any) => void): () => void;
|
|
620
|
+
/**
|
|
621
|
+
* Returns the unique identifier of the store instance.
|
|
622
|
+
*/
|
|
623
|
+
id(): string;
|
|
624
|
+
/**
|
|
625
|
+
* Checks whether the store is fully initialized and ready for use.
|
|
626
|
+
*/
|
|
627
|
+
isReady(): boolean;
|
|
628
|
+
/**
|
|
629
|
+
* Returns a promise that resolves once the store is fully initialised.
|
|
630
|
+
* Safe to call multiple times — all callers share the same latch.
|
|
631
|
+
*
|
|
632
|
+
* @param timeout - Optional maximum wait time in milliseconds.
|
|
633
|
+
* @throws {TimeoutError} If the store does not become ready within the timeout.
|
|
634
|
+
*
|
|
635
|
+
* @example
|
|
636
|
+
* await store.ready();
|
|
637
|
+
* const state = store.get();
|
|
638
|
+
*/
|
|
639
|
+
ready(timeout?: number): Promise<void>;
|
|
640
|
+
/**
|
|
641
|
+
* Returns a readonly snapshot of the current execution state of the store.
|
|
642
|
+
*/
|
|
643
|
+
state(): Readonly<StoreExecutionState<T>>;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Defines the lifecycle and sharing strategy for an artifact within the container.
|
|
648
|
+
*/
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Defines the lifecycle and sharing strategy for an artifact.
|
|
652
|
+
*/
|
|
653
|
+
type ArtifactScope =
|
|
654
|
+
/**
|
|
655
|
+
* **Singleton:** A single instance of the artifact is created and shared across all resolutions
|
|
656
|
+
* within the container. It is created once (often lazily) and reused until invalidated.
|
|
657
|
+
*/
|
|
658
|
+
"singleton"
|
|
659
|
+
/**
|
|
660
|
+
* **Transient:** A new instance of the artifact is created every time it is resolved.
|
|
661
|
+
* Transient artifacts do not participate in caching or shared state management.
|
|
662
|
+
*/
|
|
663
|
+
| "transient";
|
|
664
|
+
/**
|
|
665
|
+
* Represents a snapshot of an artifact's state and dependencies for debugging purposes.
|
|
666
|
+
* Provides insight into the artifact's current status and its position within the dependency graph.
|
|
667
|
+
*/
|
|
668
|
+
interface ArtifactDebugNode {
|
|
669
|
+
/** The unique identifier (key) of the artifact. */
|
|
670
|
+
id: string;
|
|
671
|
+
/** The scope of the artifact (Singleton or Transient). */
|
|
672
|
+
scope: ArtifactScope;
|
|
673
|
+
/**
|
|
674
|
+
* The current status of the artifact, indicating its lifecycle phase.
|
|
675
|
+
* - `'active'`: The artifact is successfully built and its instance is available.
|
|
676
|
+
* - `'error'`: The artifact failed to build due to an external (runtime) error.
|
|
677
|
+
* - `'idle'`: The artifact has not yet been built (for lazy singletons) or has been disposed.
|
|
678
|
+
* - `'building'`: The artifact's factory is currently executing.
|
|
679
|
+
* - `'pending'`: The artifact is waiting to be built, often after an invalidation and before any debounce.
|
|
680
|
+
*/
|
|
681
|
+
status: "active" | "error" | "idle" | "building" | "pending" | "debouncing";
|
|
682
|
+
/** A list of artifact keys that this artifact directly depends on. */
|
|
683
|
+
dependencies: string[];
|
|
684
|
+
/** A list of artifact keys that directly depend on this artifact (its consumers). */
|
|
685
|
+
dependents: string[];
|
|
686
|
+
/** A list of state paths (selectors) that this artifact depends on. */
|
|
687
|
+
stateDependencies: string[];
|
|
688
|
+
/** The number of times this artifact's factory has been successfully executed/rebuilt. */
|
|
689
|
+
buildCount: number;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Context provided to the `use` callback within an artifact factory.
|
|
693
|
+
* It allows an artifact to declare dependencies on other artifacts and state slices.
|
|
694
|
+
* @template TRegistry The type mapping artifact keys to their artifact types.
|
|
695
|
+
* @template TState The type of the global state managed by the DataStore.
|
|
696
|
+
*/
|
|
697
|
+
interface UseDependencyContext<TRegistry extends Record<string, any>, TState extends object> {
|
|
698
|
+
/**
|
|
699
|
+
* Resolves another artifact from the container and registers a dependency on it.
|
|
700
|
+
* If the dependency changes, the current artifact will be invalidated and rebuilt.
|
|
701
|
+
* @template K The key of the artifact to resolve.
|
|
702
|
+
* @param key The key of the artifact to resolve.
|
|
703
|
+
* @param params Parameters with which to resolve the artifact
|
|
704
|
+
* @returns A Promise that resolves to a `ResolvedArtifact` containing the instance
|
|
705
|
+
* or an external error.
|
|
706
|
+
* @throws {SystemError} if the key is missing or if resolving creates a circular dependency.
|
|
707
|
+
*/
|
|
708
|
+
resolve<K extends keyof TRegistry>(key: K, params?: any): Promise<ResolvedArtifact<TRegistry[K]>>;
|
|
709
|
+
/**
|
|
710
|
+
* Resolves another artifact from the container and registers a dependency on it.
|
|
711
|
+
* If the dependency changes, the current artifact will be invalidated and rebuilt.
|
|
712
|
+
* @template K The key of the artifact to resolve.
|
|
713
|
+
* @param key The key of the artifact to resolve.
|
|
714
|
+
* @param params Parameters with which to resolve the artifact
|
|
715
|
+
* @returns A Promise that resolves to a the resolved instance, unlike
|
|
716
|
+
* resolve, this will throw an error if resolution fails.
|
|
717
|
+
* @throws {SystemError} if any error occurs during resolution.
|
|
718
|
+
*/
|
|
719
|
+
require<K extends keyof TRegistry>(key: K, params?: any): Promise<TRegistry[K]>;
|
|
720
|
+
/**
|
|
721
|
+
* Selects a slice of the global state and registers a dependency on it.
|
|
722
|
+
* If the selected state slice changes (based on a deep comparison), the current
|
|
723
|
+
* artifact will be invalidated and rebuilt.
|
|
724
|
+
* @template S The type of the selected state slice.
|
|
725
|
+
* @param selector A function that takes the full state and returns a slice of state.
|
|
726
|
+
* @returns The selected slice of state.
|
|
727
|
+
*/
|
|
728
|
+
select<S>(selector: (state: TState) => S, options?: SubscribeOptions): S;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Context object provided to an artifact stream producer function (`ctx.stream` callback).
|
|
732
|
+
* It contains methods and properties necessary for managing the stream and communicating
|
|
733
|
+
* new artifact values to the container and its consumers.
|
|
734
|
+
*
|
|
735
|
+
* @template TState The type of the global state.
|
|
736
|
+
* @template TArtifact The type of the artifact being streamed.
|
|
737
|
+
*/
|
|
738
|
+
type ArtifactStreamContext<TState, TArtifact> = {
|
|
739
|
+
/**
|
|
740
|
+
* The current value of the artifact. This is `undefined` before the first value is emitted.
|
|
741
|
+
* Useful for diffing or migrating state during an update.
|
|
742
|
+
* @returns The current artifact value or `undefined`.
|
|
743
|
+
*/
|
|
744
|
+
value: () => TArtifact | undefined;
|
|
745
|
+
/**
|
|
746
|
+
* An AbortSignal that indicates if the stream has been cancelled or aborted
|
|
747
|
+
* from the consumer side (e.g., due to container disposal or artifact invalidation).
|
|
748
|
+
* Producers should check this signal and stop processing when it is set to prevent leaks.
|
|
749
|
+
*/
|
|
750
|
+
signal: AbortSignal;
|
|
751
|
+
/**
|
|
752
|
+
* A function to asynchronously emit a new value of the artifact to the container.
|
|
753
|
+
* Calling this function updates the artifact's resolved value, triggers invalidation
|
|
754
|
+
* for its dependents, and updates the `value()` property for subsequent emissions.
|
|
755
|
+
*
|
|
756
|
+
* @param value The new artifact value to emit.
|
|
757
|
+
* @returns A Promise that resolves when the value has been sent and dependents have been notified.
|
|
758
|
+
*/
|
|
759
|
+
emit: (value: TArtifact) => Promise<void>;
|
|
760
|
+
/**
|
|
761
|
+
* Dispatches a state update to the global store, analogous to a standard `setState`.
|
|
762
|
+
* **Note:** This does not automatically register a dependency for the current artifact.
|
|
763
|
+
* @param update A `StateUpdater` function or a partial state object.
|
|
764
|
+
* @param options Optional settings for the update, including `force` and `actionId`.
|
|
765
|
+
* @returns A Promise that resolves when the state update is complete.
|
|
766
|
+
*/
|
|
767
|
+
set(update: StateUpdater<TState>, options?: {
|
|
768
|
+
force?: boolean;
|
|
769
|
+
actionId?: string;
|
|
770
|
+
}): Promise<any>;
|
|
771
|
+
};
|
|
772
|
+
/**
|
|
773
|
+
* The full context provided to an artifact's factory function.
|
|
774
|
+
* This context allows the artifact to interact with the container,
|
|
775
|
+
* declare dependencies, manage its lifecycle, and update its value.
|
|
776
|
+
* @template TRegistry The type mapping artifact keys to their artifact types.
|
|
777
|
+
* @template TState The type of the global state managed by the DataStore.
|
|
778
|
+
* @template TArtifact The type of the artifact being created by the factory.
|
|
779
|
+
*/
|
|
780
|
+
type ArtifactFactoryContext<TRegistry extends Record<string, any>, TState extends object, TArtifact, TExtra extends Record<string, any> = {}> = TExtra & {
|
|
781
|
+
/**
|
|
782
|
+
* Returns the current global state object. This is a non-reactive read;
|
|
783
|
+
* changes to the state will not automatically invalidate the artifact
|
|
784
|
+
* unless explicitly selected via `ctx.use` and `ctx.select`.
|
|
785
|
+
* @returns The current global state.
|
|
786
|
+
*/
|
|
787
|
+
state(): TState;
|
|
788
|
+
/**
|
|
789
|
+
* The previous instance of the artifact if it's a Singleton and is being rebuilt
|
|
790
|
+
* after an invalidation. This is `undefined` on the initial build.
|
|
791
|
+
* Useful for diffing, migrating state, or reusing resources during an update.
|
|
792
|
+
*/
|
|
793
|
+
previous?: TArtifact;
|
|
794
|
+
/**
|
|
795
|
+
* Executes a callback within a dependency tracking context.
|
|
796
|
+
* Any `resolve` or `select` calls made inside this callback will register
|
|
797
|
+
* dependencies for the current artifact.
|
|
798
|
+
* @template R The return type of the callback.
|
|
799
|
+
* @param callback The function to execute to resolve dependencies.
|
|
800
|
+
* @returns A Promise resolving to the result of the callback.
|
|
801
|
+
*/
|
|
802
|
+
use<R>(callback: (ctx: UseDependencyContext<TRegistry, TState>) => R | Promise<R>): Promise<R>;
|
|
803
|
+
/**
|
|
804
|
+
* Registers a cleanup function to be executed when the **current instance** of the
|
|
805
|
+
* artifact is invalidated and before its new instance is built. This is useful
|
|
806
|
+
* for releasing resources (e.g., event listeners) specific to the *previous* instance.
|
|
807
|
+
* @param cleanup The cleanup function.
|
|
808
|
+
*/
|
|
809
|
+
onCleanup(cleanup: ArtifactCleanup): void;
|
|
810
|
+
/**
|
|
811
|
+
* Registers a dispose function to be executed when the artifact is
|
|
812
|
+
* permanently removed from the container or the container itself is disposed.
|
|
813
|
+
* This is for final, permanent resource release.
|
|
814
|
+
* @param callback The dispose function.
|
|
815
|
+
*/
|
|
816
|
+
onDispose(callback: ArtifactCleanup): void;
|
|
817
|
+
/**
|
|
818
|
+
* Starts a streaming process for a Singleton artifact.
|
|
819
|
+
* The callback receives an `ArtifactStreamContext` to emit values and manage the
|
|
820
|
+
* stream's lifecycle. It can return a cleanup function (or a Promise resolving
|
|
821
|
+
* to one) to handle resource disposal.
|
|
822
|
+
* * @param callback - The streaming logic implementation. Can be synchronous or
|
|
823
|
+
* asynchronous, optionally returning a cleanup function.
|
|
824
|
+
* @throws {SystemError} If called on a Transient artifact, as they do not
|
|
825
|
+
* support persistent streaming states.
|
|
826
|
+
*/
|
|
827
|
+
stream(callback: (ctx: ArtifactStreamContext<TState, TArtifact>) => (void | (() => void | Promise<void>)) | Promise<void | (() => void | Promise<void>)>): void;
|
|
828
|
+
stream(callback: (ctx: ArtifactStreamContext<TState, TArtifact>) => (void | (() => void | Promise<void>)) | Promise<void | (() => void | Promise<void>)>): void;
|
|
829
|
+
/**
|
|
830
|
+
* An AbortSignal that indicates if the artifacts has been unregistered
|
|
831
|
+
*/
|
|
832
|
+
signal: AbortSignal;
|
|
833
|
+
/** For parameterized artifacts: the parameters that were passed during resolve/watch. */
|
|
834
|
+
params?: any;
|
|
835
|
+
};
|
|
836
|
+
/**
|
|
837
|
+
* A function that performs cleanup or disposal logic for an artifact, potentially asynchronously.
|
|
838
|
+
* Used for `onCleanup` and `onDispose` callbacks.
|
|
839
|
+
*/
|
|
840
|
+
type ArtifactCleanup = () => void | Promise<void>;
|
|
841
|
+
/**
|
|
842
|
+
* Common properties shared across all possible states of a resolved artifact.
|
|
843
|
+
* @template TArtifact The type of the resolved artifact instance.
|
|
844
|
+
*/
|
|
845
|
+
interface ResolvedArtifactBase {
|
|
846
|
+
/**
|
|
847
|
+
* A function to manually trigger cleanup associated with this specific
|
|
848
|
+
* resolved instance. This is typically only relevant for Transient artifacts
|
|
849
|
+
* where the consumer is responsible for cleanup.
|
|
850
|
+
*/
|
|
851
|
+
cleanup?: ArtifactCleanup;
|
|
852
|
+
/**
|
|
853
|
+
* Manually invalidates this artifact, triggering its rebuild and
|
|
854
|
+
* cascading invalidations to its dependents.
|
|
855
|
+
* @param replace If `true`, forces immediate rebuild without debounce delay.
|
|
856
|
+
* @param fatal If `true`, the artifact will not be rebuilt until next resolve
|
|
857
|
+
* regardless of the lazy option during registration.
|
|
858
|
+
*/
|
|
859
|
+
invalidate(replace?: boolean, fatal?: boolean): Promise<void>;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* State of an artifact that is successfully built and ready for use.
|
|
863
|
+
* The instance is guaranteed to be present.
|
|
864
|
+
* @template TArtifact The type of the resolved artifact instance.
|
|
865
|
+
*/
|
|
866
|
+
interface ReadyArtifact<TArtifact> extends ResolvedArtifactBase {
|
|
867
|
+
/** The successfully resolved instance of the artifact. */
|
|
868
|
+
instance: TArtifact;
|
|
869
|
+
/** Indicates whether the artifact is ready and has an instance. */
|
|
870
|
+
ready: true;
|
|
871
|
+
error?: undefined;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* State of an artifact that failed to build due to an external error.
|
|
875
|
+
* The instance is guaranteed to be absent.
|
|
876
|
+
*/
|
|
877
|
+
interface ErrorArtifact extends ResolvedArtifactBase {
|
|
878
|
+
instance?: undefined;
|
|
879
|
+
ready: false;
|
|
880
|
+
/**
|
|
881
|
+
* Any runtime or external error that occurred during the artifact's factory
|
|
882
|
+
* execution (e.g., network fetch failed).
|
|
883
|
+
*/
|
|
884
|
+
error: any;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* State of an artifact that is pending, idle, or currently building.
|
|
888
|
+
* Both instance and error are absent.
|
|
889
|
+
*/
|
|
890
|
+
interface PendingArtifact extends ResolvedArtifactBase {
|
|
891
|
+
/** The instance is absent while pending or idle. Always undefined. */
|
|
892
|
+
instance?: undefined;
|
|
893
|
+
ready: false;
|
|
894
|
+
error?: undefined;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* The result of an artifact resolution. This is a union type representing
|
|
898
|
+
* the artifact in one of three possible states: Ready, Error, or Pending/Idle.
|
|
899
|
+
*
|
|
900
|
+
* This structure allows consuming code to narrow the type based on the
|
|
901
|
+
* boolean flag `ready` or the presence of the `error` property.
|
|
902
|
+
*
|
|
903
|
+
* @template TArtifact The type of the resolved artifact instance.
|
|
904
|
+
*/
|
|
905
|
+
type ResolvedArtifact<TArtifact> = ReadyArtifact<TArtifact> | ErrorArtifact | PendingArtifact;
|
|
906
|
+
type KeyedResolvedArtifact<TRegistry, K extends keyof TRegistry> = ResolvedArtifact<TRegistry[K]> & {
|
|
907
|
+
[P in K]?: TRegistry[K];
|
|
908
|
+
};
|
|
909
|
+
/**
|
|
910
|
+
* The factory function responsible for creating an artifact's instance.
|
|
911
|
+
* It receives an `ArtifactFactoryContext` to interact with the container
|
|
912
|
+
* and declare dependencies.
|
|
913
|
+
* @template TRegistry The type mapping artifact keys to their artifact types.
|
|
914
|
+
* @template TState The type of the global state managed by the DataStore.
|
|
915
|
+
* @template TArtifact The type of the artifact this factory produces.
|
|
916
|
+
* @param context The context for creating the artifact.
|
|
917
|
+
* @returns The artifact instance or a Promise resolving to it.
|
|
918
|
+
*/
|
|
919
|
+
type ArtifactFactory<TRegistry extends Record<string, any>, TState extends object, TArtifact, TExtra extends Record<string, any> = {}> = (context: ArtifactFactoryContext<TRegistry, TState, TArtifact, TExtra>) => TArtifact | Promise<TArtifact>;
|
|
920
|
+
/**
|
|
921
|
+
* An interface for observing changes to an artifact without direct resolution,
|
|
922
|
+
* typically used for UI binding or monitoring.
|
|
923
|
+
* Provides a way to get the current resolved artifact and subscribe to updates.
|
|
924
|
+
* @template TArtifact The type of the artifact being watched.
|
|
925
|
+
*/
|
|
926
|
+
interface ArtifactObserver<TRegistry, K extends keyof TRegistry> {
|
|
927
|
+
/** The unique identifier (key) of the artifact being watched. */
|
|
928
|
+
id: string;
|
|
929
|
+
/**
|
|
930
|
+
* Number of active references (watchers) to this observer instance.
|
|
931
|
+
* Incremented on each `watch()` call, decremented on each `dispose()` call.
|
|
932
|
+
*/
|
|
933
|
+
count: number;
|
|
934
|
+
/**
|
|
935
|
+
* Retrieves the current `ResolvedArtifact` for the watched key.
|
|
936
|
+
* @param resolve Flag indicating whether we should resolve the artifact
|
|
937
|
+
* immediately. Defaults to `false`
|
|
938
|
+
* @returns The resolved artifact, including its instance and status.
|
|
939
|
+
*/
|
|
940
|
+
get(resolve?: boolean): KeyedResolvedArtifact<TRegistry, K>;
|
|
941
|
+
/**
|
|
942
|
+
* Subscribes a callback function to be invoked whenever the artifact's
|
|
943
|
+
* state (instance, error, or readiness) changes.
|
|
944
|
+
* @param callback The function to call on updates. It receives the new `ResolvedArtifact`.
|
|
945
|
+
* @param eager a boolean indicating whether we should immediately call the
|
|
946
|
+
* callback after subscription. Defaults to `true`
|
|
947
|
+
* @returns A function to unsubscribe the callback and stop receiving updates.
|
|
948
|
+
*/
|
|
949
|
+
subscribe(callback: (artifact: KeyedResolvedArtifact<TRegistry, K>) => void, eager?: boolean): () => void;
|
|
950
|
+
/**
|
|
951
|
+
* Resolves another artifact from the container and registers a dependency on it.
|
|
952
|
+
* If the dependency changes, the current artifact will be invalidated and rebuilt.
|
|
953
|
+
* @returns A Promise that resolves to a `ResolvedArtifact` containing the instance
|
|
954
|
+
* or an external error.
|
|
955
|
+
* @throws {SystemError} if resolution fails.
|
|
956
|
+
*/
|
|
957
|
+
resolve(): Promise<KeyedResolvedArtifact<TRegistry, K>>;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Configuration options for defining an artifact. These options control
|
|
961
|
+
* its lifecycle, instantiation, and error handling behavior.
|
|
962
|
+
* @template TState The type of the global state.
|
|
963
|
+
* @template TArtifact The resolved type of the artifact instance.
|
|
964
|
+
* @template TRegistry The type mapping of all artifacts in the container.
|
|
965
|
+
*/
|
|
966
|
+
interface ArtifactTemplate<TState extends object, TArtifact, TRegistry extends Record<string, any> = Record<string, any>, TExtra extends Record<string, any> = {}> {
|
|
967
|
+
/** The unique key identifying this artifact within the registry. */
|
|
968
|
+
key: keyof TRegistry;
|
|
969
|
+
/** The factory function responsible for creating the artifact's instance. */
|
|
970
|
+
factory: ArtifactFactory<TRegistry, TState, TArtifact, TExtra>;
|
|
971
|
+
/**
|
|
972
|
+
* The scope of the artifact, determining its lifecycle and sharing strategy.
|
|
973
|
+
* Defaults to `ArtifactScopes.Singleton`.
|
|
974
|
+
*/
|
|
975
|
+
scope?: ArtifactScope;
|
|
976
|
+
/**
|
|
977
|
+
* If `true` (default), the artifact's factory is executed only when the artifact
|
|
978
|
+
* is first requested (lazy instantiation). If `false`, the artifact is built
|
|
979
|
+
* immediately upon registration (only applies to Singleton scopes).
|
|
980
|
+
*/
|
|
981
|
+
lazy?: boolean;
|
|
982
|
+
/**
|
|
983
|
+
* Maximum time in milliseconds allowed for the artifact's factory function
|
|
984
|
+
* to complete execution. If exceeded, the factory will time out.
|
|
985
|
+
*/
|
|
986
|
+
timeout?: number;
|
|
987
|
+
/**
|
|
988
|
+
* Number of times to retry the artifact's factory on failure due to an
|
|
989
|
+
* external (runtime) error (e.g., an exception in an async dependency).
|
|
990
|
+
* SystemErrors (e.g., key not found) are not retried. Defaults to `0`.
|
|
991
|
+
*/
|
|
992
|
+
retries?: number;
|
|
993
|
+
/**
|
|
994
|
+
* Base debounce time in milliseconds for invalidation events originating
|
|
995
|
+
* from this artifact's dependencies. This delays the rebuild process to
|
|
996
|
+
* aggregate multiple rapid changes.
|
|
997
|
+
*/
|
|
998
|
+
debounce?: number;
|
|
999
|
+
/** If defined, the artifact is parameterized. Receives the user‑supplied params and returns a unique string key. */
|
|
1000
|
+
paramKey?: (params: Record<string, unknown>) => string;
|
|
1001
|
+
virtual?: true;
|
|
1002
|
+
}
|
|
1003
|
+
interface ExportedArtifact {
|
|
1004
|
+
key: string;
|
|
1005
|
+
instance: any;
|
|
1006
|
+
state: {
|
|
1007
|
+
groups: Array<{
|
|
1008
|
+
paths: string[];
|
|
1009
|
+
options?: SubscribeOptions;
|
|
1010
|
+
}>;
|
|
1011
|
+
hash: string;
|
|
1012
|
+
};
|
|
1013
|
+
dependencies: string[];
|
|
1014
|
+
}
|
|
1015
|
+
interface ExportedContainerState {
|
|
1016
|
+
version: string;
|
|
1017
|
+
timestamp: number;
|
|
1018
|
+
artifacts: ExportedArtifact[];
|
|
1019
|
+
checksum: string;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
/**
|
|
1023
|
+
* A dependency injection container for managing the lifecycle, dependencies,
|
|
1024
|
+
* and instances of "artifacts" (any JavaScript/TypeScript object or value).
|
|
1025
|
+
*
|
|
1026
|
+
* Separated concerns:
|
|
1027
|
+
* - ArtifactRegistry: Stores artifact templates (factories + options)
|
|
1028
|
+
* - ArtifactCache: Stores resolved singleton instances
|
|
1029
|
+
* - ArtifactDependencyGraph: Tracks artifact dependencies using DependencyGraph
|
|
1030
|
+
* - ArtifactManager: Handles lifecycle (build, invalidate, dispose)
|
|
1031
|
+
* - ArtifactObserverManager: Manages watchers and subscriptions
|
|
1032
|
+
*
|
|
1033
|
+
* @template TRegistry A type that maps artifact keys to their types
|
|
1034
|
+
* @template TState The type of the global state object
|
|
1035
|
+
*/
|
|
1036
|
+
declare class ArtifactContainer<TRegistry extends Record<string, any> = Record<string, any>, TState extends object = any> {
|
|
1037
|
+
private readonly registry;
|
|
1038
|
+
private readonly cache;
|
|
1039
|
+
private readonly graph;
|
|
1040
|
+
private readonly manager;
|
|
1041
|
+
private readonly observer;
|
|
1042
|
+
private readonly store;
|
|
1043
|
+
/**
|
|
1044
|
+
* Creates a new ArtifactContainer instance.
|
|
1045
|
+
* @param store An object providing functions to interact with a global data store
|
|
1046
|
+
*/
|
|
1047
|
+
constructor(store: Pick<DataStore<TState>, "watch" | "get" | "set" | "subset">);
|
|
1048
|
+
/**
|
|
1049
|
+
* Provides debug information about all artifacts currently registered in this container.
|
|
1050
|
+
*
|
|
1051
|
+
* Status mapping:
|
|
1052
|
+
* - `"building"` — factory is currently executing (buildOnce.running())
|
|
1053
|
+
* - `"debouncing"` — invalidation is pending behind a debounce timer
|
|
1054
|
+
* - `"error"` — last build attempt failed
|
|
1055
|
+
* - `"active"` — successfully built and instance is available
|
|
1056
|
+
* - `"idle"` — not yet built (lazy) or has been disposed
|
|
1057
|
+
*
|
|
1058
|
+
* @returns An array of ArtifactDebugNode objects
|
|
1059
|
+
*/
|
|
1060
|
+
debugInfo(): ArtifactDebugNode[];
|
|
1061
|
+
/**
|
|
1062
|
+
* Registers a new artifact with the container.
|
|
1063
|
+
* If an artifact with the same key already exists, it will be overwritten and disposed.
|
|
1064
|
+
* For Singleton, non-lazy artifacts, the factory will be immediately invoked.
|
|
1065
|
+
*
|
|
1066
|
+
* @param params The registration parameters
|
|
1067
|
+
* @returns A cleanup function that unregisters the artifact
|
|
1068
|
+
*/
|
|
1069
|
+
register<K extends keyof TRegistry>(params: ArtifactTemplate<TState, TRegistry[K], TRegistry>): () => void;
|
|
1070
|
+
/**
|
|
1071
|
+
* Returns a boolean indicating whether a template exists for an artifact.
|
|
1072
|
+
*
|
|
1073
|
+
* @param key The unique identifier of the artifact.
|
|
1074
|
+
* @returns boolean.
|
|
1075
|
+
*/
|
|
1076
|
+
has<K extends keyof TRegistry>(key: K): boolean;
|
|
1077
|
+
/**
|
|
1078
|
+
* Unregisters an artifact from the container, disposing of its current instance
|
|
1079
|
+
* and removing all associated resources and dependency links.
|
|
1080
|
+
*
|
|
1081
|
+
* Also evicts the observer watcher cache entry so singleton watchers do not
|
|
1082
|
+
* outlive the artifact's registration.
|
|
1083
|
+
*
|
|
1084
|
+
* @param key The unique identifier of the artifact
|
|
1085
|
+
*/
|
|
1086
|
+
unregister<K extends keyof TRegistry>(key: K, params?: Record<string, unknown>): Promise<void>;
|
|
1087
|
+
/**
|
|
1088
|
+
* Resolves an artifact by its key, returning its instance or an error.
|
|
1089
|
+
* Handles dependency resolution, cycle detection, caching, and retry logic.
|
|
1090
|
+
*
|
|
1091
|
+
* The keyed index property (`artifact[key]`) is set inside `cache.package()`
|
|
1092
|
+
* so it is always consistent with `artifact.instance`. No post-hoc mutation
|
|
1093
|
+
* is needed here.
|
|
1094
|
+
*
|
|
1095
|
+
* @param key The unique identifier for the artifact
|
|
1096
|
+
* @param params Parameters with which to resolve the artifact
|
|
1097
|
+
* @returns A Promise that resolves to a ResolvedArtifact
|
|
1098
|
+
* @throws {ArtifactNotFoundError} if the artifact is not found
|
|
1099
|
+
*/
|
|
1100
|
+
resolve<K extends keyof TRegistry>(key: K, params?: Record<string, unknown>): Promise<KeyedResolvedArtifact<TRegistry, K>>;
|
|
1101
|
+
/**
|
|
1102
|
+
* Resolves an artifact by its key, returning its instance directly.
|
|
1103
|
+
* Throws if resolution fails.
|
|
1104
|
+
*
|
|
1105
|
+
* @param key The unique identifier for the artifact
|
|
1106
|
+
* @param params Parameters with which to resolve the artifact
|
|
1107
|
+
* @returns A Promise that resolves to the artifact instance
|
|
1108
|
+
* @throws {ArtifactNotFoundError} if the artifact is not found
|
|
1109
|
+
* @throws the artifact's error if resolution failed
|
|
1110
|
+
*/
|
|
1111
|
+
require<K extends keyof TRegistry>(key: K, params?: Record<string, unknown>): Promise<TRegistry[K]>;
|
|
1112
|
+
/**
|
|
1113
|
+
* Returns an ArtifactObserver for a given artifact key.
|
|
1114
|
+
* The observer allows subscribing to changes in the artifact's resolved value.
|
|
1115
|
+
*
|
|
1116
|
+
* @param key The unique identifier for the artifact
|
|
1117
|
+
* @param params Parameters with which to resolve the artifact
|
|
1118
|
+
* @param ttl Delay before the watcher is cleaned up if we have no subscriber
|
|
1119
|
+
* @returns An ArtifactObserver instance
|
|
1120
|
+
*/
|
|
1121
|
+
watch<K extends keyof TRegistry>(key: K, params?: Record<string, unknown>, ttl?: number): ArtifactObserver<TRegistry, K>;
|
|
1122
|
+
/**
|
|
1123
|
+
* Peeks at the resolved instance of an artifact without triggering resolution
|
|
1124
|
+
* or registering a dependency.
|
|
1125
|
+
*
|
|
1126
|
+
* @param key The unique identifier for the artifact
|
|
1127
|
+
* @returns The artifact instance if already built, otherwise undefined
|
|
1128
|
+
*/
|
|
1129
|
+
peek<K extends keyof TRegistry>(key: K, params?: Record<string, unknown>): TRegistry[K] | undefined;
|
|
1130
|
+
/**
|
|
1131
|
+
* Invalidates an artifact, triggering rebuild and cascade to dependents.
|
|
1132
|
+
*
|
|
1133
|
+
* @param key The artifact key to invalidate
|
|
1134
|
+
* @param options.replace If true, forces immediate rebuild bypassing debounce
|
|
1135
|
+
* @param options.params for parametized artifacts
|
|
1136
|
+
*/
|
|
1137
|
+
invalidate<K extends keyof TRegistry>(key: K, options?: {
|
|
1138
|
+
replace?: boolean;
|
|
1139
|
+
params?: Record<string, unknown>;
|
|
1140
|
+
}): Promise<void>;
|
|
1141
|
+
/**
|
|
1142
|
+
* Notifies observers that an artifact has changed.
|
|
1143
|
+
* Called by ArtifactManager during stream propagation.
|
|
1144
|
+
*
|
|
1145
|
+
* @param key The artifact key
|
|
1146
|
+
*/
|
|
1147
|
+
notifyObservers(key: string): void;
|
|
1148
|
+
/**
|
|
1149
|
+
* Checks if an artifact has active watchers.
|
|
1150
|
+
* Called by ArtifactManager to determine if lazy artifacts should rebuild.
|
|
1151
|
+
*
|
|
1152
|
+
* @param key The artifact key
|
|
1153
|
+
* @returns True if the artifact has active watchers
|
|
1154
|
+
*/
|
|
1155
|
+
hasWatchers(key: string): boolean;
|
|
1156
|
+
/**
|
|
1157
|
+
* Disposes of the entire container and all artifacts registered within it.
|
|
1158
|
+
* Releases all resources, stops all watchers, and clears all internal state.
|
|
1159
|
+
*
|
|
1160
|
+
* Returns a Promise that resolves once all artifact teardowns have settled.
|
|
1161
|
+
* Callers should await this method to guarantee full resource release.
|
|
1162
|
+
*/
|
|
1163
|
+
dispose(): Promise<void>;
|
|
1164
|
+
export(): Promise<ExportedContainerState>;
|
|
1165
|
+
private restore;
|
|
1166
|
+
static from<TRegistry extends Record<string, any> = Record<string, any>, TState extends object = any>(options: {
|
|
1167
|
+
store: Pick<DataStore<TState>, "watch" | "get" | "set" | "subset">;
|
|
1168
|
+
bundle?: ExportedContainerState;
|
|
1169
|
+
templates: ArtifactTemplate<TState, any, TRegistry>[];
|
|
1170
|
+
}): Promise<ArtifactContainer<TRegistry, TState>>;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* @file routing-pipeline.ts
|
|
1175
|
+
* @description A production-grade, asynchronous, type-safe pipeline engine featuring conditional
|
|
1176
|
+
* routing, checkpoint-based pause/resume mechanisms, concurrent step/sub-pipeline execution,
|
|
1177
|
+
* atomic state transactions, and lifecycle event streaming.
|
|
1178
|
+
*/
|
|
1179
|
+
|
|
1180
|
+
/** A generic state shape used by the pipeline. */
|
|
1181
|
+
interface State {
|
|
1182
|
+
[key: string]: any;
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Instructs the engine to pause the pipeline.
|
|
1186
|
+
* The `pause` field is the `stageId` at which the pipeline should resume.
|
|
1187
|
+
*/
|
|
1188
|
+
interface PauseInstruction {
|
|
1189
|
+
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
|
+
*/
|
|
1194
|
+
readonly persist?: boolean;
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* All possible outcomes of a router:
|
|
1198
|
+
* - `string` → jump to a named stage
|
|
1199
|
+
* - `null` → terminate the pipeline
|
|
1200
|
+
* - `undefined` → advance to the next stage in order (or natural end)
|
|
1201
|
+
* - `PauseInstruction` → suspend and write a checkpoint
|
|
1202
|
+
*/
|
|
1203
|
+
type RoutingInstruction = string | null | undefined | PauseInstruction;
|
|
1204
|
+
/** Type guard for a pause instruction. */
|
|
1205
|
+
declare function isPauseInstruction(v: RoutingInstruction): v is PauseInstruction;
|
|
1206
|
+
/**
|
|
1207
|
+
* Router used inside a **steps‑mode** stage.
|
|
1208
|
+
* Receives the full state and a record of step results keyed by step id.
|
|
1209
|
+
*/
|
|
1210
|
+
type StepStageRouter<S extends object> = (state: S, results: Record<string, Result<DeepPartial<S>>>) => RoutingInstruction;
|
|
1211
|
+
/**
|
|
1212
|
+
* Router used inside a **pipelines‑mode** stage.
|
|
1213
|
+
* Receives the full state and a record of sub‑pipeline results keyed by
|
|
1214
|
+
* sub‑pipeline id.
|
|
1215
|
+
*/
|
|
1216
|
+
type PipelineStageRouter<S extends object> = (state: S, results: Record<string, Result<PipelineRunResult<S>>>) => RoutingInstruction;
|
|
1217
|
+
/**
|
|
1218
|
+
* Persisted checkpoint written to the run's store under the internal key
|
|
1219
|
+
* `__pipeline_data__.<pipelineId>.<runId>`.
|
|
1220
|
+
*
|
|
1221
|
+
* The checkpoint is always written atomically with the state patches of the
|
|
1222
|
+
* stage that triggered the pause.
|
|
1223
|
+
*/
|
|
1224
|
+
interface PipelineCheckpoint {
|
|
1225
|
+
readonly runId: string;
|
|
1226
|
+
readonly pipelineId: string;
|
|
1227
|
+
readonly resumeAt: EntryAddress;
|
|
1228
|
+
readonly pausedAtStageId: string;
|
|
1229
|
+
readonly pausedAtStageLabel: string;
|
|
1230
|
+
readonly pausedOn: string;
|
|
1231
|
+
readonly containerBundle?: ExportedContainerState;
|
|
1232
|
+
}
|
|
1233
|
+
/** Engine‑internal namespace for all checkpoint data. */
|
|
1234
|
+
declare const PIPELINE_DATA_KEY: "__pipeline_data__";
|
|
1235
|
+
/**
|
|
1236
|
+
* Run-scoped context injected into every step action as a second argument.
|
|
1237
|
+
*
|
|
1238
|
+
* All fields are immutable and captured at step registration time. Steps can
|
|
1239
|
+
* use `runId` and `pipelineId` to initiate external async operations (e.g.
|
|
1240
|
+
* storing a callback reference for webhook-driven pause/resume). The `signal`
|
|
1241
|
+
* allows long-running async work inside a step to honour pipeline abort.
|
|
1242
|
+
*
|
|
1243
|
+
* For sub-pipeline steps, `pipelineId` reflects the sub-pipeline's own id,
|
|
1244
|
+
* not the root definition id.
|
|
1245
|
+
*
|
|
1246
|
+
* @example
|
|
1247
|
+
* ```ts
|
|
1248
|
+
* const step: Step<MyState> = {
|
|
1249
|
+
* id: "send-email",
|
|
1250
|
+
* label: "Send approval email",
|
|
1251
|
+
* action: async (ctx, pcxt) => {
|
|
1252
|
+
* await emailService.send({
|
|
1253
|
+
* to: ctx.state().approverEmail,
|
|
1254
|
+
* callbackToken: pcxt.runId, // stored so the webhook can resume
|
|
1255
|
+
* });
|
|
1256
|
+
* return {};
|
|
1257
|
+
* },
|
|
1258
|
+
* };
|
|
1259
|
+
* ```
|
|
1260
|
+
*/
|
|
1261
|
+
interface PipelineContext {
|
|
1262
|
+
/** The unique identifier of the current run. */
|
|
1263
|
+
readonly runId: string;
|
|
1264
|
+
/**
|
|
1265
|
+
* The id of the pipeline definition this step belongs to.
|
|
1266
|
+
* For sub-pipeline steps this is the sub-pipeline's own id, not the root id.
|
|
1267
|
+
*/
|
|
1268
|
+
readonly pipelineId: string;
|
|
1269
|
+
/** The id of the stage this step belongs to. */
|
|
1270
|
+
readonly stageId: string;
|
|
1271
|
+
/** The id of this step. */
|
|
1272
|
+
readonly stepId: string;
|
|
1273
|
+
/**
|
|
1274
|
+
* The AbortSignal for this run. Steps performing long-running async work
|
|
1275
|
+
* should check or pass this signal so they can be cancelled promptly
|
|
1276
|
+
* when `runContext.abort()` is called.
|
|
1277
|
+
*/
|
|
1278
|
+
readonly signal: AbortSignal;
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* A single unit of work.
|
|
1282
|
+
*
|
|
1283
|
+
* The `action` receives an {@link ArtifactFactoryContext} typed to the
|
|
1284
|
+
* pipeline's state shape `S`, and a {@link PipelineContext} carrying the
|
|
1285
|
+
* run-scoped identifiers and abort signal. It must return a partial state
|
|
1286
|
+
* patch on success, or throw on failure.
|
|
1287
|
+
*
|
|
1288
|
+
* The `PipelineContext` is captured at step registration time and is always
|
|
1289
|
+
* consistent with this step's position in the pipeline tree.
|
|
1290
|
+
*/
|
|
1291
|
+
interface Step<S extends object = State> {
|
|
1292
|
+
id: string;
|
|
1293
|
+
label: string;
|
|
1294
|
+
scope?: ArtifactScope;
|
|
1295
|
+
timeout?: number;
|
|
1296
|
+
retries?: number;
|
|
1297
|
+
action: (ctx: ArtifactFactoryContext<{}, S, {}>, pcxt: PipelineContext) => Promise<DeepPartial<S>>;
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* A stage groups steps (steps mode) or sub‑pipelines (pipelines mode).
|
|
1301
|
+
* Exactly one mode is active: if `pipelines` is non‑empty, steps are ignored.
|
|
1302
|
+
*/
|
|
1303
|
+
interface Stage<S extends object = State> {
|
|
1304
|
+
id: string;
|
|
1305
|
+
order: number;
|
|
1306
|
+
label: string;
|
|
1307
|
+
timeout?: number;
|
|
1308
|
+
/** Steps defined for this stage. Used only when `pipelines` is empty. */
|
|
1309
|
+
steps?: Record<string, Step<S>>;
|
|
1310
|
+
/** Router for steps mode. */
|
|
1311
|
+
router?: StepStageRouter<S>;
|
|
1312
|
+
/** Sub‑pipelines for this stage. Non‑empty → pipelines mode. */
|
|
1313
|
+
pipelines?: RoutingPipelineDefinition<S>[];
|
|
1314
|
+
/** Router for pipelines mode. */
|
|
1315
|
+
pipelinesRouter?: PipelineStageRouter<S>;
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* A pipeline definition is a collection of stages.
|
|
1319
|
+
* Every pipeline has a unique `id` and a human‑readable `label`.
|
|
1320
|
+
*/
|
|
1321
|
+
interface RoutingPipelineDefinition<S extends object = State> {
|
|
1322
|
+
id: string;
|
|
1323
|
+
label: string;
|
|
1324
|
+
stages: Stage<S>[];
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Describes a specific entry point inside a sub‑pipeline.
|
|
1328
|
+
*/
|
|
1329
|
+
interface SubPipelineAddress {
|
|
1330
|
+
/** Zero‑based index into the parent stage's `pipelines` array. */
|
|
1331
|
+
index: number;
|
|
1332
|
+
/** Stage id within the sub‑pipeline to start from. */
|
|
1333
|
+
stage: string;
|
|
1334
|
+
/** Optional step id to run (all other steps in the entry stage are skipped). */
|
|
1335
|
+
step?: string;
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Describes where in the pipeline tree execution should begin.
|
|
1339
|
+
* Everything after the entry point executes normally.
|
|
1340
|
+
*/
|
|
1341
|
+
interface EntryAddress {
|
|
1342
|
+
/** Stage id to start from. All earlier stages are skipped. */
|
|
1343
|
+
stage: string;
|
|
1344
|
+
/** If provided, only this step runs in the entry stage. */
|
|
1345
|
+
step?: string;
|
|
1346
|
+
/** If provided, the stage must be in pipelines mode; only this sub‑pipeline is launched. */
|
|
1347
|
+
pipeline?: SubPipelineAddress;
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* The outcome of a pipeline run, as a discriminated union on `status`.
|
|
1351
|
+
*
|
|
1352
|
+
* Each variant carries exactly the fields that are always present for that
|
|
1353
|
+
* outcome. Callers narrow with `result.status` and get full type-safety for
|
|
1354
|
+
* variant-specific fields without defensive optional checks.
|
|
1355
|
+
*
|
|
1356
|
+
* - `succeeded` — pipeline ran to completion; `finalState` reflects all committed patches.
|
|
1357
|
+
* - `paused` — a router issued a pause instruction; `checkpoint` encodes the resume address.
|
|
1358
|
+
* - `failed` — a step, router, or transaction threw; `error` carries the cause.
|
|
1359
|
+
*/
|
|
1360
|
+
type PipelineRunResult<S extends object> = {
|
|
1361
|
+
status: "succeeded";
|
|
1362
|
+
runId: string;
|
|
1363
|
+
finalState: S;
|
|
1364
|
+
} | {
|
|
1365
|
+
status: "paused";
|
|
1366
|
+
runId: string;
|
|
1367
|
+
finalState: S;
|
|
1368
|
+
checkpoint: PipelineCheckpoint;
|
|
1369
|
+
} | {
|
|
1370
|
+
status: "failed";
|
|
1371
|
+
runId: string;
|
|
1372
|
+
finalState: S;
|
|
1373
|
+
error: SystemError;
|
|
1374
|
+
};
|
|
1375
|
+
/** A node in the event path tree. */
|
|
1376
|
+
type PathNode = {
|
|
1377
|
+
kind: "pipeline";
|
|
1378
|
+
id: string;
|
|
1379
|
+
label: string;
|
|
1380
|
+
} | {
|
|
1381
|
+
kind: "stage";
|
|
1382
|
+
id: string;
|
|
1383
|
+
label: string;
|
|
1384
|
+
} | {
|
|
1385
|
+
kind: "step";
|
|
1386
|
+
id: string;
|
|
1387
|
+
label: string;
|
|
1388
|
+
};
|
|
1389
|
+
/** Ordered list of ancestors from the root pipeline down to the event emitter. */
|
|
1390
|
+
type EventPath = readonly PathNode[];
|
|
1391
|
+
/**
|
|
1392
|
+
* All events emitted during a run.
|
|
1393
|
+
*
|
|
1394
|
+
* stage:paused (#6) is a dedicated event for stages that pause, distinct from
|
|
1395
|
+
* stage:success. Consumers should listen to both if they want to react to all
|
|
1396
|
+
* terminal stage outcomes. stage:success is only emitted when the stage
|
|
1397
|
+
* advances (or terminates) — never when it pauses.
|
|
1398
|
+
*/
|
|
1399
|
+
interface RunEventMap<S extends object> {
|
|
1400
|
+
"pipeline:start": {
|
|
1401
|
+
path: EventPath;
|
|
1402
|
+
pipelineId: string;
|
|
1403
|
+
pipelineLabel: string;
|
|
1404
|
+
runId: string;
|
|
1405
|
+
};
|
|
1406
|
+
"pipeline:success": {
|
|
1407
|
+
path: EventPath;
|
|
1408
|
+
pipelineId: string;
|
|
1409
|
+
pipelineLabel: string;
|
|
1410
|
+
runId: string;
|
|
1411
|
+
finalState: S;
|
|
1412
|
+
};
|
|
1413
|
+
"pipeline:paused": {
|
|
1414
|
+
path: EventPath;
|
|
1415
|
+
pipelineId: string;
|
|
1416
|
+
pipelineLabel: string;
|
|
1417
|
+
runId: string;
|
|
1418
|
+
checkpoint: PipelineCheckpoint;
|
|
1419
|
+
finalState: S;
|
|
1420
|
+
};
|
|
1421
|
+
"pipeline:failure": {
|
|
1422
|
+
path: EventPath;
|
|
1423
|
+
pipelineId: string;
|
|
1424
|
+
pipelineLabel: string;
|
|
1425
|
+
runId: string;
|
|
1426
|
+
error: SystemError;
|
|
1427
|
+
};
|
|
1428
|
+
"stage:start": {
|
|
1429
|
+
path: EventPath;
|
|
1430
|
+
stageId: string;
|
|
1431
|
+
stageLabel: string;
|
|
1432
|
+
runId: string;
|
|
1433
|
+
mode: "steps" | "pipelines";
|
|
1434
|
+
};
|
|
1435
|
+
"stage:success": {
|
|
1436
|
+
path: EventPath;
|
|
1437
|
+
stageId: string;
|
|
1438
|
+
stageLabel: string;
|
|
1439
|
+
runId: string;
|
|
1440
|
+
nextInstruction: RoutingInstruction;
|
|
1441
|
+
};
|
|
1442
|
+
/** Emitted when a stage's router returns a pause instruction. Distinct from stage:success. */
|
|
1443
|
+
"stage:paused": {
|
|
1444
|
+
path: EventPath;
|
|
1445
|
+
stageId: string;
|
|
1446
|
+
stageLabel: string;
|
|
1447
|
+
runId: string;
|
|
1448
|
+
checkpoint: PipelineCheckpoint;
|
|
1449
|
+
};
|
|
1450
|
+
"stage:failure": {
|
|
1451
|
+
path: EventPath;
|
|
1452
|
+
stageId: string;
|
|
1453
|
+
stageLabel: string;
|
|
1454
|
+
runId: string;
|
|
1455
|
+
error: SystemError;
|
|
1456
|
+
};
|
|
1457
|
+
"router:evaluated": {
|
|
1458
|
+
path: EventPath;
|
|
1459
|
+
stageId: string;
|
|
1460
|
+
stageLabel: string;
|
|
1461
|
+
runId: string;
|
|
1462
|
+
instruction: RoutingInstruction;
|
|
1463
|
+
interpretation: "jump" | "terminate" | "advance" | "natural-end" | "pause";
|
|
1464
|
+
};
|
|
1465
|
+
"step:start": {
|
|
1466
|
+
path: EventPath;
|
|
1467
|
+
stepId: string;
|
|
1468
|
+
stepLabel: string;
|
|
1469
|
+
runId: string;
|
|
1470
|
+
};
|
|
1471
|
+
"step:success": {
|
|
1472
|
+
path: EventPath;
|
|
1473
|
+
stepId: string;
|
|
1474
|
+
stepLabel: string;
|
|
1475
|
+
runId: string;
|
|
1476
|
+
};
|
|
1477
|
+
"step:failure": {
|
|
1478
|
+
path: EventPath;
|
|
1479
|
+
stepId: string;
|
|
1480
|
+
stepLabel: string;
|
|
1481
|
+
runId: string;
|
|
1482
|
+
error: unknown;
|
|
1483
|
+
};
|
|
1484
|
+
"subpipeline:fork": {
|
|
1485
|
+
path: EventPath;
|
|
1486
|
+
stageId: string;
|
|
1487
|
+
stageLabel: string;
|
|
1488
|
+
runId: string;
|
|
1489
|
+
subPipelineIds: string[];
|
|
1490
|
+
};
|
|
1491
|
+
"subpipeline:join": {
|
|
1492
|
+
path: EventPath;
|
|
1493
|
+
stageId: string;
|
|
1494
|
+
stageLabel: string;
|
|
1495
|
+
runId: string;
|
|
1496
|
+
results: Record<string, Result<PipelineRunResult<S>>>;
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
/** Callback type for event subscriptions. */
|
|
1500
|
+
type RunEventHandler<S extends object, K extends keyof RunEventMap<S>> = (payload: RunEventMap<S>[K]) => void;
|
|
1501
|
+
interface ScopedEventBus<S extends object> {
|
|
1502
|
+
emit<K extends keyof RunEventMap<S>>(event: K, payload: RunEventMap<S>[K]): void;
|
|
1503
|
+
on<K extends keyof RunEventMap<S>>(event: K, handler: RunEventHandler<S, K>): () => void;
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* A unit of execution returned by {@link PipelineEngine.prepare} or
|
|
1507
|
+
* {@link PipelineEngine.resume}.
|
|
1508
|
+
*
|
|
1509
|
+
* Subscribe to lifecycle events via {@link on}, abort via {@link abort}, and
|
|
1510
|
+
* start execution with {@link run}.
|
|
1511
|
+
*/
|
|
1512
|
+
interface RunContext<S extends object> {
|
|
1513
|
+
/** The unique identifier of this run (UUID v7). */
|
|
1514
|
+
readonly id: string;
|
|
1515
|
+
/**
|
|
1516
|
+
* Subscribe to lifecycle events for this run.
|
|
1517
|
+
* Sub‑pipeline events bubble here with full ancestry paths.
|
|
1518
|
+
* Returns an unsubscribe function.
|
|
1519
|
+
*/
|
|
1520
|
+
on<K extends keyof RunEventMap<S>>(event: K, handler: RunEventHandler<S, K>): () => void;
|
|
1521
|
+
/** Cancel the run. Propagation occurs at the next stage boundary. */
|
|
1522
|
+
abort(): void;
|
|
1523
|
+
/**
|
|
1524
|
+
* Execute the pipeline from the configured entry point.
|
|
1525
|
+
*
|
|
1526
|
+
* Once‑gated: concurrent callers join the in‑flight promise.
|
|
1527
|
+
* A failed run is permanently sealed — obtain a new `RunContext` to retry.
|
|
1528
|
+
* Never throws; failures are returned in a `Result`.
|
|
1529
|
+
*/
|
|
1530
|
+
run(): Promise<Result<PipelineRunResult<S>>>;
|
|
1531
|
+
}
|
|
1532
|
+
/** Minimal logger interface used by the engine. */
|
|
1533
|
+
interface EngineLogger {
|
|
1534
|
+
info: (msg: string, ctx?: any) => void;
|
|
1535
|
+
error: (msg: string, ctx?: any) => void;
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Configuration for {@link PipelineEngine}.
|
|
1539
|
+
*
|
|
1540
|
+
* @typeParam S - The shape of the state managed by the pipeline.
|
|
1541
|
+
*/
|
|
1542
|
+
interface PipelineEngineOptions<S extends object = State> {
|
|
1543
|
+
/** Logger (defaults to a no‑op). */
|
|
1544
|
+
logger?: EngineLogger;
|
|
1545
|
+
/**
|
|
1546
|
+
* **Required.** A factory that, given a `runId`, returns a `DataStore<S>`.
|
|
1547
|
+
* The engine does **not** hold a global store; each run receives its own
|
|
1548
|
+
* isolated store instance.
|
|
1549
|
+
*
|
|
1550
|
+
* For a new run (`prepare`), the factory should return an empty store.
|
|
1551
|
+
* For a resumed run (`resume`), it must return the **same** store that was
|
|
1552
|
+
* used during the original run (so that state and checkpoints are preserved).
|
|
1553
|
+
*/
|
|
1554
|
+
storeFactory: (runId: string) => Promise<DataStore<S>>;
|
|
1555
|
+
/**
|
|
1556
|
+
* Optional factory that produces the initial state for every **new** run.
|
|
1557
|
+
* The returned object is written to the store during `prepare()`.
|
|
1558
|
+
* If omitted, the store remains empty.
|
|
1559
|
+
*/
|
|
1560
|
+
initialStateFactory?: () => S;
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* The entry point for creating and resuming pipeline runs.
|
|
1564
|
+
*
|
|
1565
|
+
* @typeParam S - The shape of the state object.
|
|
1566
|
+
*
|
|
1567
|
+
* @example
|
|
1568
|
+
* ```ts
|
|
1569
|
+
* const engine = new PipelineEngine(myDefinition, {
|
|
1570
|
+
* storeFactory: (runId) => createMyStore(runId),
|
|
1571
|
+
* initialStateFactory: () => ({ counter: 0 }),
|
|
1572
|
+
* });
|
|
1573
|
+
*
|
|
1574
|
+
* const ctx = await engine.prepare({ stage: "validate" });
|
|
1575
|
+
* ctx.on("pipeline:success", (e) => console.log("done", e.finalState));
|
|
1576
|
+
* const result = await ctx.run();
|
|
1577
|
+
* ```
|
|
1578
|
+
*/
|
|
1579
|
+
declare class PipelineEngine<S extends object> {
|
|
1580
|
+
private readonly definition;
|
|
1581
|
+
private readonly logger;
|
|
1582
|
+
private readonly storeFactory;
|
|
1583
|
+
private readonly initialStateFactory;
|
|
1584
|
+
private readonly index;
|
|
1585
|
+
/**
|
|
1586
|
+
* Constructs an engine tied to a fixed pipeline definition.
|
|
1587
|
+
*
|
|
1588
|
+
* @param definition - The pipeline topology (stages, steps, routers).
|
|
1589
|
+
* @param options - Engine configuration. `storeFactory` is mandatory.
|
|
1590
|
+
*/
|
|
1591
|
+
constructor(definition: RoutingPipelineDefinition<S>, options: PipelineEngineOptions<S>);
|
|
1592
|
+
/**
|
|
1593
|
+
* Prepare an isolated {@link RunContext} for a **new** run.
|
|
1594
|
+
*
|
|
1595
|
+
* A fresh store is obtained via `storeFactory`, optionally seeded with the
|
|
1596
|
+
* initial state from `initialStateFactory`. No execution occurs until
|
|
1597
|
+
* `runContext.run()` is called.
|
|
1598
|
+
*
|
|
1599
|
+
* @param entry - Optional address describing where to start execution.
|
|
1600
|
+
* Omit to start from the lowest‑order stage.
|
|
1601
|
+
* @param runId - Optional run identifier (UUID v7). Generated if omitted.
|
|
1602
|
+
* @returns A promise that resolves with a ready‑to‑run `RunContext`.
|
|
1603
|
+
*/
|
|
1604
|
+
prepare(entry?: EntryAddress, runId?: string): Promise<RunContext<S>>;
|
|
1605
|
+
/**
|
|
1606
|
+
* Reconstruct a {@link RunContext} for a previously paused run.
|
|
1607
|
+
*
|
|
1608
|
+
* The store for the given `runId` must still contain the checkpoint and
|
|
1609
|
+
* state at the time of the pause. The context is returned ready to run;
|
|
1610
|
+
* no state is altered until `runContext.run()` is called.
|
|
1611
|
+
*
|
|
1612
|
+
* @param runId - The identifier of the paused run.
|
|
1613
|
+
* @returns A result containing the `RunContext`, or a failure if no
|
|
1614
|
+
* checkpoint exists or the pipeline id does not match.
|
|
1615
|
+
*/
|
|
1616
|
+
resume(runId: string): Promise<Result<RunContext<S>>>;
|
|
1617
|
+
/**
|
|
1618
|
+
* Constructs a {@link RunContextImpl} with all necessary dependencies.
|
|
1619
|
+
*
|
|
1620
|
+
* This method is package-internal. External callers use prepare() or resume().
|
|
1621
|
+
* Sub-pipelines access it through the {@link SubPipelineFactory} interface,
|
|
1622
|
+
* which is the only reference RunContextImpl holds to the engine.
|
|
1623
|
+
*
|
|
1624
|
+
* A single {@link ArtifactContainer} is created per run at root level and
|
|
1625
|
+
* shared across all sub-pipelines. Steps are registered under namespaced keys
|
|
1626
|
+
* encoding their full lineage (keyPrefix:stageId:stepId), ensuring cache
|
|
1627
|
+
* isolation without allocating separate containers per sub-pipeline.
|
|
1628
|
+
*
|
|
1629
|
+
* The {@link AbortController} is created here and its signal is immediately
|
|
1630
|
+
* threaded into {@link registerSteps} so that every step factory closure
|
|
1631
|
+
* captures the correct signal at registration time.
|
|
1632
|
+
*
|
|
1633
|
+
* @param runId - The run identifier (same for parent and sub‑pipelines).
|
|
1634
|
+
* @param pipeline - The pipeline (sub‑)definition.
|
|
1635
|
+
* @param entry - Entry address; `undefined` means start from the beginning.
|
|
1636
|
+
* @param store - The shared store instance for this run.
|
|
1637
|
+
* @param parentBus - Parent scoped bus for event bubbling (root runs pass undefined).
|
|
1638
|
+
* @param parentPath - Event path from the root.
|
|
1639
|
+
* @param container - Shared container (created once at root; passed down to sub-pipelines).
|
|
1640
|
+
* @param keyPrefix - Namespacing prefix for artifact keys within the shared container.
|
|
1641
|
+
*/
|
|
1642
|
+
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
|
+
}
|
|
1644
|
+
|
|
1645
|
+
export { type EngineLogger, type EntryAddress, type EventPath, PIPELINE_DATA_KEY, type PathNode, type PauseInstruction, Pipeline, type PipelineCheckpoint, type PipelineContext, type PipelineDefinition, PipelineEngine, type PipelineEngineOptions, type PipelineEventMap, type PipelineRunResult, type PipelineStageRouter, type PipelineStep, type PipelineStepDefinition, Result, type RoutingInstruction, type RoutingPipelineDefinition, type RunContext, type RunEventHandler, type RunEventMap, SequentialExecutionContext, type Stage, type StageCarry, type State, type Step, type StepStageRouter, type SubPipelineAddress, isPauseInstruction };
|