@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
136 lines (135 loc) • 6.7 kB
TypeScript
import type { IPipelineStep, PipelineStepName, PipelineStepStage } from '../pipeline-step';
import type { DeepReadonly, UnionToIntersection } from 'ts-essentials';
/**
* A pipeline is a collection of {@link Pipeline#steps|steps} that are executed in a certain {@link Pipeline#order|order}.
* It is to be created {@link createPipeline}.
*
* If you want to get the type of all steps in the pipeline (given they are created canonically using const step names), refer to {@link PipelineStepNames}.
*/
export interface Pipeline<T extends IPipelineStep = IPipelineStep> {
readonly steps: ReadonlyMap<PipelineStepName, DeepReadonly<IPipelineStep>>;
readonly order: readonly T['name'][];
/**
* In the order, this is the index of the first step that
* is executed {@link PipelineStepStage#OncePerRequest|once per request}.
* If it is "out of bounds" (i.e., the number of steps), all steps are executed {@link PipelineStepStage#OncePerFile|once per file}.
*/
readonly firstStepPerRequest: number;
}
/**
* Returns the types of all step names in the given pipeline.
*
* @see Pipeline for details
*/
export type PipelineStepNames<P extends Pipeline> = PipelineStep<P>['name'];
/**
* Returns the steps included in the given pipeline.
* @example
* ```ts
* type Pipeline = typeof DEFAULT_DATAFLOW_PIPELINE
* // Pipeline is now Pipeline<step1 | step2 | ...>
* type Steps = PipelineStep<typeof DEFAULT_DATAFLOW_PIPELINE>
* // Steps is now just step1 | step2 | ...
* ```
*/
export type PipelineStep<P extends Pipeline> = P extends Pipeline<infer U> ? U : never;
/**
* Meta-information attached to every step result
*/
export interface PipelinePerStepMetaInformation {
readonly '.meta': {
/**
* The time it took to execute the step in milliseconds,
* the required accuracy is dependent on the measuring system, but usually at around 1 ms.
*/
readonly timing: number;
};
}
/**
* Returns the step with the given name from the given pipeline.
*
* @example
* ```ts
* type Foo = PipelineStepWithName<typeof DEFAULT_DATAFLOW_PIPELINE, 'parse'>
* // Foo is now only the "parse" step from the DEFAULT_DATAFLOW_PIPELINE
* ```
*/
export type PipelineStepWithName<P extends Pipeline, Name extends PipelineStepName> = P extends Pipeline<infer U> ? U extends IPipelineStep<Name> ? U : never : never;
/**
* Returns the processor function of the step with the given name from the given pipeline.
* @see {@link PipelineStepWithName}
*/
export type PipelineStepProcessorWithName<P extends Pipeline, Name extends PipelineStepName> = PipelineStepWithName<P, Name>['processor'];
/**
* Returns the printer function of the step with the given name from the given pipeline.
* @see {@link PipelineStepWithName}
*/
export type PipelineStepPrintersWithName<P extends Pipeline, Name extends PipelineStepName> = PipelineStepWithName<P, Name>['printer'];
/**
* Returns the output type of the step with the given name from the given pipeline.
*
* @example
* ```ts
* type Foo = PipelineStepOutputWithName<typeof DEFAULT_DATAFLOW_PIPELINE, 'parse'>
* // Foo contains the ParseStepOutput & PipelinePerStepMetaInformation type (ie the parse output and meta information)
* @see {@link PipelineStepWithName}
* ```
*/
export type PipelineStepOutputWithName<P extends Pipeline, Name extends PipelineStepName> = Awaited<ReturnType<PipelineStepProcessorWithName<P, Name>>> & PipelinePerStepMetaInformation;
/**
* Returns a union type that represents the required inputs to be passed to the given pipeline.
*
* @example
* ```ts
* type Foo = PipelineInput<typeof DEFAULT_DATAFLOW_PIPELINE>
* // Foo contains ParseRequiredInput & NormalizeRequiredInput
* ```
*
* In short, this can be useful whenever you want to describe _all_ inputs a complete
* pipeline needs to run through (i.e., the union of all inputs required by the individual steps).
*
* @see {@link PipelineOutput}
*/
export type PipelineInput<P extends Pipeline> = UnionToIntersection<PipelineStep<P>['requiredInput']>;
/**
* Only gets the union of 'requiredInput' of those PipelineSteps which have a 'execute' field of type 'OncePerRequest'.
* In other words, information that you may want to change for another request (e.g., another slice) with the same file.
*/
export type PipelinePerRequestInput<P extends Pipeline> = {
[K in PipelineStepNames<P>]: PipelineStepWithName<P, K>['executed'] extends PipelineStepStage.OncePerFile ? never : PipelineStepWithName<P, K>['requiredInput'];
}[PipelineStepNames<P>];
/**
* Returns an object type that represents the types of the outputs that will result from running the given pipeline, each as the step's name mapped to its PipelineStepOutputWithName.
* @example
* ```ts
* type Foo = PipelineOutput<typeof DEFAULT_DATAFLOW_PIPELINE>
* // Foo contains {
* // parse: ParseStepOutput & PipelinePerStepMetaInformation,
* // normalize: NormalizeStepOutput & PipelinePerStepMetaInformation,
* // ...
* // }
* ```
*/
export type PipelineOutput<P extends Pipeline> = {
[K in PipelineStepNames<P>]: PipelineStepOutputWithName<P, K>;
};
/**
* Creates a {@link Pipeline|pipeline} from a given collection of {@link IPipelineStep|steps}.
* To be valid, the collection of {@link IPipelineStep|steps} must satisfy the following set of constraints
* (which should be logical, when you consider what a pipeline should achieve):
*
* 0) the collection of {@link IPipelineStep|steps} is not empty
* 1) all {@link IPipelineStepOrder#name|names} of {@link IPipelineStep|steps} are unique for the given pipeline
* 2) all {@link IPipelineStepOrder#dependencies|dependencies} of all {@link IPipelineStep|steps} exist
* 3) there are no cycles in the dependency graph
* 4) the target of a {@link IPipelineStepOrder#decorates|step's decoration} exists
* 5) if a {@link IPipelineStepOrder#decorates|decoration} applies, all of its {@link IPipelineStepOrder#dependencies|dependencies} are already in the pipeline
* 6) in the resulting {@link Pipeline|pipeline}, there is a strict cut between {@link IPipelineStep|steps} that are executed
* {@link PipelineStepStage#OncePerFile|once per file} and {@link PipelineStepStage#OncePerRequest|once per request}.
*
* @returns The function will try to order your collection steps so that all the constraints hold.
* If it succeeds it will return the resulting {@link Pipeline|pipeline}, otherwise it will throw an {@link InvalidPipelineError}.
*
* @throws InvalidPipelineError If any of the constraints listed above are not satisfied.
*/
export declare function createPipeline<T extends readonly IPipelineStep[]>(...steps: T): Pipeline<T[number]>;