@bemedev/pipe-machine
v2.0.2
Published
A strongly-typed pipe class library for function composition with advanced TypeScript support
Maintainers
Readme
@bemedev/pipe-machine
Features
- 3-Step Builder - Separate type declaration from implementation for
maximum clarity:
createPipe → type → define - Strongly-Typed Pipes - Full TypeScript type inference and validation throughout the pipeline
- Named-Step Support - Create named pipeline steps for better code clarity and partial overrides
- Explicit Type Spec - Declare input/output types before providing implementations
- Async Support - Handle both synchronous and asynchronous function pipelines
- Duplicate Key Support - Reuse a step name to run the same function multiple times in the pipeline
Installation
npm install @bemedev/pipe-machine
# or
pnpm add @bemedev/pipe-machineQuick Start
import { createPipe } from '@bemedev/pipe-machine';
const pipe = createPipe('double', 'add10')
.type<{
double: { parameters: [number]; return: number };
add10: number;
}>()
.define({
double: x => x * 2,
add10: x => x + 10,
});
pipe(5); // 20The 3-Step Flow
Step 1 — createPipe(...keys)
Declares the ordered list of named steps in the pipeline.
const builder = createPipe('parse', 'validate', 'transform');Step 2 — .type<TypeSpec>()
Declares the TypeScript types for the pipeline — no runtime argument, pure
generic. Only the first key is required and must have the shape
{ parameters: [...], return: ... }. All other keys are optional and
specify just their return type. Unspecified keys default to identity
typing (pass-through: same input and output type as the previous step).
const typed = builder.type<{
parse: { parameters: [string]; return: number };
validate: number; // (n: number) => number
// 'transform' not listed → identity: (n: number) => number
}>();Step 3 — .define(impl)
Provides the function implementation for every unique key. Types are
enforced by the spec from .type<T>().
const pipeline = typed.define({
parse: s => parseInt(s, 10),
validate: n => Math.abs(n),
transform: n => n * 100, // (number) => number — identity-typed
});
pipeline('−42'); // 4200Advanced Usage
Duplicate keys
Repeat a key name to run that function more than once:
const fn = createPipe('add1', 'double', 'add1', 'double', 'add1')
.type<{
add1: { parameters: [number]; return: number };
double: number;
}>()
.define({ add1: x => x + 1, double: x => x * 2 });
fn(2); // ((((2+1)*2)+1)*2)+1 = 15When a key appears more than once, its DefineImpl slot is typed as
IdentityFn<PrevReturn> — an identity function (x: T) => T — enforcing
that a duplicated step passes its input value through unchanged in type.
When the first key is duplicated, its parameters type is restricted to a
single-element tuple to prevent ambiguous multi-arg signatures.
Multi-argument first step
const fn = createPipe('hypot', 'double')
.type<{
hypot: { parameters: [number, number]; return: number };
double: number;
}>()
.define({
hypot: (a, b) => Math.hypot(a, b),
double: x => x * 2,
});
fn(3, 4); // 10Async pipelines
If any step returns a Promise, the entire pipeline becomes async:
const fn = createPipe('fetch', 'parse')
.type<{
fetch: { parameters: [string]; return: Promise<string> };
parse: number;
}>()
.define({
fetch: async url => (await fetch(url)).text(),
parse: s => parseInt(s, 10),
});
await fn('https://example.com/value'); // numberPartial overrides
After building a pipeline, create variants by overriding specific steps:
const base = createPipe('add1', 'double')
.type<{
add1: { parameters: [number]; return: number };
double: number;
}>()
.define({ add1: x => x + 1, double: x => x * 2 });
base(5); // 12
const tripled = base.define({ double: x => x * 3 });
tripled(5); // 18API
createPipe(...keys: Describer[]): MachineCreated
Creates a pipeline builder with named steps. Each key can be a plain
string or a { name: string; description: string } object to attach a
human-readable description. Throws if called with no keys. Returns an
object with a .type<T>() method.
.type<T extends MachineTypeSpec>(schema?: StandardSchemaV1): MachineTyped
Declares types for the pipeline. T is a pure TypeScript generic with
shape { params: any[]; context: Record<string, any> }. An optional
StandardSchemaV1-compatible schema (e.g. a zod, a @bemedev/typings
object, or valibot object) may be passed as a runtime argument for
schema-based validation. Returns an object with only a .define(impl)
method.
.define(impl: MachineDefineInput): MachinePipeline
Provides implementations for actions and configuration. Returns the completed, callable pipeline.
pipeline(...params): Context | Promise<Context>
Calls the composed pipeline. Returns a Promise if any step is async,
otherwise returns synchronously. The return type matches the context type
from the MachineTypeSpec.
pipeline.define(overrides: Partial<MachineDefineInput>): MachinePipeline
Creates a new pipeline with some actions or guards replaced. Original pipeline is unchanged.
pipeline.build<T>(select: (ctx: Context) => T): (params) => T
Transforms the output of the pipeline using a selector function.
Exported Types
| Type | Description |
| -------------------- | ------------------------------------------------------------------ |
| MachineCreated | Returned by createPipe() |
| MachineTyped | Returned by .type<T>() |
| MachinePipeline | Completed callable pipeline |
| MachineTypeSpec | Type spec shape: { params: any[]; context: Record<string, any> } |
| MachineDefineInput | Shape of the .define(impl) argument |
| Describer | Step key type: string or { name: string; description: string } |
| FromDescriber<D> | Extracts the string key name from a Describer |
| Config | Configuration object shape for guards and delays |
| Condition | Guard condition type |
| Delayed | Delayed action configuration with delay timing |
| GuardConfig | Shape for guard configuration |
| ExtractActions<C> | Extracts action names from a config |
| ExtractGuards<C> | Extracts guard names from a config |
| ExtractDelays<C> | Extracts delay names from a config |
| SoA | Struct-of-Arrays utility type |
Licence
MIT
CHANGE_LOG
Read CHANGELOG.md for more details about the changes.
Author
chlbri ([email protected])
