farrow-pipeline
Type-Friendly middleware library.
Installation
- npm
- Yarn
npm install farrow-pipeline
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
- farrow-sample/01-pipeline-base: Base usage of farrow-pipeline.
- farrow-sample/02-pipeline-more: More compelex sample of farrow-pipeline. :::
:::