UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

136 lines (135 loc) 6.7 kB
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]>;