Skip to main content

farrow-pipeline

Type-Friendly middleware library.

Installation

yarn add farrow-pipeline

Usage

import { createPipeline } from "farrow-pipeline";

// 1. create
const pipeline = createPipeline<number, number>();

// 2. attach functions
pipeline.use((count, next) => {
return next(count + 1);
});
pipeline.use((count, next) => {
return count * 2;
});

// 3. run
console.log(pipeline.run(1)); // 4 = (1 + 1) * 2
console.log(pipeline.run(5)); // 12 = (5 + 1) * 2

API

Context

createContext

Type Signature:

const createContext: <T>(value: T) => Context<T>;

Example Usage:

import { createContext } from "farrow-pipeline";

const context0 = createContext(0);
const context1 = createContext<{ limit?: number }>({});

Context

type Context<T = any> = {
id: symbol;
[ContextSymbol]: T;
// create a new context equipped a new value
create: (value: T) => Context<T>;
// get context ref { value } for accessing context in current container of pipeline
use: () => {
value: T;
};
// get context value
get: () => T;
// set context value
set: (value: T) => void;
// assert context value is not null or undefined and return context value
assert: () => Exclude<T, undefined | null>;
};

Example Usage:

import { createContext } from "farrow-pipeline";

const context = createContext<number | null>(0);

context.use(); // { value: 0 }
context.get(); // 0

context.set(1);
context.use(); // { value: 1 }
context.get(); // 1

context.set(null);
context.use(); // { value: null }
context.get(); // null
context.assert(); // throw error

const newContext = context.create(2);
newContext.use(); // { value: 2 }
newContext.get(); // 2

isContext

Type Signature:

const isContext: (input: any) => input is Context<any>;

Example Usage:

import { createContext, isContext } from "farrow-pipeline";

const context = createContext(0);

isContext(context); // true
isContext(context.get()); // false
isContext(0); // false

assertContext

Type Signature:

const assertContext: (input: any) => asserts input is Context;

Example Usage:

import { createContext, assertContext } from "farrow-pipeline";

const context = createContext(0);

assertContext(context); // safe
assertContext(context.get()); // throw error
assertContext(0); // throw error

ContextStorage

type ContextStorage = {
[key: string]: Context;
};

Container

createContainer

Type Signature:

const createContainer: (ContextStorage?: ContextStorage) => Container;

Example Usage:

import { createContainer, createContext } from "farrow-pipeline";

const container0 = createContainer();

const limit = createContext(10);
const container1 = createContainer({ limit });

Container

type Container = {
read: <V>(Context: Context<V>) => V;
write: <V>(Context: Context<V>, value: V) => void;
[ContainerSymbol]: true;
};

Example Usage:

import { createContainer, createContext } from "farrow-pipeline";

const container0 = createContainer();

const limit = createContext(10);
const container1 = createContainer({ limit: limit.create(20) });

container0.read(limit); // 10
container1.read(limit); // 20

container0.write(limit, 30);
container0.read(limit); // 30

isContainer

Type Signature:

const isContainer: (input: any) => input is Container;

Example Usage:

import { createContainer, isContainer } from "farrow-pipeline";

const container = createContainer();

isContainer(container); // true
isContainer(0); // false
isContainer({}); // false

assertContainer

Type Signature:

const assertContainer: (input: any) => asserts input is Container;

Example Usage:

import { createContainer, assertContainer } from "farrow-pipeline";

const container = createContainer();

assertContainer(container); // safe
assertContainer({}); // throw error
assertContainer(0); // throw error

Pipeline

createPipeline

Type Signature:

const createPipeline: <I, O>(
options?: PipelineOptions | undefined
) => Pipeline<I, O>;

type PipelineOptions = {
// contexts for injecting to the pipeline
contexts?: ContextStorage;
};

Example Usage:

import { createPipeline } from "farrow-pipeline";

const pipeline = createPipeline<number, number>();

Create with contexts:

import { createPipeline, createContext } from "farrow-pipeline";

const limit = createContext(10);

const pipeline = createPipeline<number, number>({
contexts: {
limit,
},
});

Pipeline

type Pipeline<I = unknown, O = unknown> = {
// add middlewares to pipeline and return pipeline
use: (...inputs: MiddlewareInput<I, O>[]) => Pipeline<I, O>;
// run a pipeline by input and received its output
run: (input: I, options?: RunPipelineOptions<I, O>) => O;
// pipeline.middleware can use in another pipeline.use(...) if their type is matched
middleware: Middleware<I, O>;
};

type RunPipelineOptions<I = unknown, O = unknown> = {
// container which store some contexts.if container is not given, pipeline will use its internal container
container?: Container;
// if all middleware called next, then onLast would be called
onLast?: (input: I) => O;
};

Example Usage:

import { createPipeline } from "farrow-pipeline";

// 1. create
const foo = createPipeline<number, number>();

// 2. attach middleware
foo.use((count, next) => {
return next(count + 1);
});
foo.use((count, next) => {
return count * 2;
});

// 3. run
console.log(foo.run(1)); // 4 = (1 + 1) * 2
console.log(foo.run(5)); // 12 = (5 + 1) * 2

Run with Container:

import {
createPipeline,
createContext,
createContainer,
} from "farrow-pipeline";

// 1. create
const foo = createPipeline<number, number>();
const limit = createContext(10);
const container = createContainer({ limit });

// 2. attach middleware
foo.use((count, next) => {
return next(count + 1);
});
foo.use((count, next) => {
if (count > 10) return count;
return count * 2;
});

// 3. run
console.log(foo.run(1, { container })); // 4 = (1 + 1) * 2
console.log(foo.run(5, { container })); // 12 = (5 + 1) * 2
console.log(foo.run(10, { container })); // 11 = (10 + 1) > 10 ? 10 + 1 : (10 + 1) * 2

Run with onLast(onLast will run when all middleware called the next):

import { createPipeline } from "farrow-pipeline";

// 1. create
const foo = createPipeline<number, number>();

// 2. attach middleware
foo.use((count, next) => {
return next(count + 1);
});
foo.use((count, next) => {
if (count > 10) return next(count);
return count * 2;
});

// 3. run
console.log(foo.run(1)); // 4 = (1 + 1) * 2
console.log(foo.run(5)); // 12 = (5 + 1) * 2
console.log(foo.run(10, { onLast: (count) => count + 5 })); // 16 = (10 + 1) > 10 ? 10 + 1 + 5 : (10 + 1) * 2

Pipeline as middleware

import { createPipeline } from "farrow-pipeline";

// 1. create
const foo = createPipeline<number, number>();

// 2. attach middleware
foo.use((count, next) => {
return next(count + 1);
});
foo.use((count, next) => {
return count * 2;
});

const bar = createPipeline<number, number>();

bar.use(foo);

console.log(bar.run(1)); // 4 = (1 + 1) * 2
console.log(bar.run(5)); // 12 = (5 + 1) * 2

Middleware

type Middleware<I = unknown, O = unknown> = (input: I, next: Next<I, O>) => O;

type Middlewares<I = unknown, O = unknown> = Middleware<I, O>[];

type MiddlewareInput<I = unknown, O = unknown> =
| Middleware<I, O>
| { middleware: Middleware<I, O> };

useContainer

Type Signature:

const useContainer: () => Container;

Example Usage:

import {
createPipeline,
createContext,
createContainer,
useContainer,
} from "farrow-pipeline";

// 1. create
const foo = createPipeline<number, number>();
const limit = createContext(10);
const container = createContainer({ limit });

// 2. attach middleware
foo.use((count, next) => {
const innerContainer = useContainer();
innerContainer === container; // true
return next(count + 1);
});

// 3. run
console.log(foo.run(1, { container })); // 4 = (1 + 1) * 2

createAsyncPipeline

Type Signature:

const createAsyncPipeline: <I, O>(
options?: PipelineOptions | undefined
) => AsyncPipeline<I, O>;

Example Usage:

import { createAsyncPipeline } from "farrow-pipeline";

const pipeline = createAsyncPipeline<number, number>();

Create with contexts:

import { createAsyncPipeline, createContext } from "farrow-pipeline";

const limit = createContext(10);

const pipeline = createAsyncPipeline<number, number>({
contexts: {
limit,
},
});

AsyncPipeline

Type Signature:

type AsyncPipeline<I = unknown, O = unknown> = Pipeline<I, MaybeAsync<O>> & {
useLazy: (thunk: ThunkMiddlewareInput<I, O>) => AsyncPipeline<I, O>;
};

Example Usage:

import { createAsyncPipeline } from "farrow-pipeline";

// 1. create
const foo = createAsyncPipeline<number, number>();

// 2. attach middleware
foo.use((count, next) => {
return next(count + 1);
});
foo.useLazy(async () => {
return (count, next) => count * 2;
});

// 3. run
console.log(await foo.run(1)); // 4 = (1 + 1) * 2
console.log(await foo.run(5)); // 12 = (5 + 1) * 2

Async Hook

Open/close the feature: Async Hook with function.

import { enable, disable } from "farrow-pipeline/asyncHooks.node";

enable

Type Signature:

const enable: () => void;

Example Usage:

import { enable } from "farrow-pipeline/asyncHooks.node";

enable();

disable

Type Signature:

const disable: () => void;

Example Usage:

import { disable } from "farrow-pipeline/asyncHooks.node";

disable();
caution

Node.js performance will be worse with this on, with Promise taking two to three times longer to run, see bmeurer/async-hooks-performance-impact for details.

Learn more

Relative Module
  • farrow-http: A Type-Friendly Web Framework based on farrow-pipeline.
Sample

:::