Sequential async pipelines with first-class retry, error boundaries, and smart failure strategies. Pragmatic, direct, no heavy abstractions.
Just plain JavaScript. Eager execution. Perfect stack traces.
To stop writing the same try/catch and manual accumulation boilerplate.
## Thisisbadcodingforawait(constitemofiterable){try{constresult=awaitexecute(item)}catch(error){// OH BOYconsole.error(error)}}
## Thisdoesnothaveasynctransformationsanderrorcontrolarray.filter(predicate).map(transform)Pipelean gives you:
seriesfor sequential work over arrays and async iterables — liveonProgress,pauserate limits,take, first-class error strategiesscan/reducefor stateful accumulation across many itemsflowfor stateful accumulation across one input — each operation enriches the same statepipefor vertical compositiontryCatchandretrymiddleware you can reuse across your app- Structured results
{results, errors, sourceErrors, failure}— no silent crashes *Syncvariants for synchronous code — same error collection, no promises
Need parallel? → p-map Want lazy iterators? → iter-tools Love reactive streams? → RxJS / most.js
We believe Pipelean is a pragmatic middle path: sequential by design, with built-in error control and resiliency — so you stop rewriting the same boilerplate every time.
Pipelean focuses on sequential workflows: compose operations, process collections one item at a time, carry state when needed, and control failures with built-in retry and error policies.
Pipelean ships a small ESLint plugin that flags .forEach(), .reduce(), .map(async ...), non-generator loops, and Promise.* static combinators, suggesting pipelean equivalents. It is a separate entry point — importing it does not pull in the runtime library.
importpipeleanConfigfrom'pipelean/eslint/config'exportdefault[pipeleanConfig,// Optionally tighten individual rules:{rules: {'pipelean/no-loop-without-yield': 'warn',// loops allowed only in yielding generators'pipelean/no-promise-combinators': 'warn',// suggests series() / tryCatch()},},]Pipelean is "Agent-Ready." It ships with built-in Skills to help AI assistants (like Claude, Gemini CLI, or Cursor) write better code using this library.
The easiest way to install the skills is using the Vercel agent-skills CLI:
npx skills add https://github.com/ildella/pipelean/tree/master/skillsThis will install:
pipelean-corepipelean-functional-programming
2. (Experimental) using skills-npm
yarn add -D skills-npm
yarn skills-npm- Architecture : The philosophy and design principles.
- Guide : Core concepts and usage patterns.
- Examples - Practical usage examples for all functions
- Reference - Reference docs
import{pipe,series,collect}from'pipelean'constpipeline=pipe(downloadSomething,transformSomething,writeToDatabase,)asyncfunction*pages(){yield*items}const{results, errors, sourceErrors}=awaitseries(pages(),pipeline,{strategy: collect,pause: 200,onProgress: ({item, result, index, total})=>{updateBar(index+1,total)},})onProgress fires live after each kept item and is awaited before the next one. pause rate-limits. total is omitted when the source has no cheap length. See docs/patterns.md for paging, unknown length, and dead-source recipes.
When each step should enrich the same state object (one input, many enrichments, final accumulated value), use flow():
import{flow}from'pipelean'constprepareAlbum=state=>({title: state.rawTitle.trim()})constextractYear=state=>({year: parseYear(state.rawYear)})constextractArtists=state=>({artists: state.artists??[]})constprocessAlbum=flow([prepareAlbum,extractYear,extractArtists,])const{value, errors, failure}=awaitprocessAlbum(input)// value = {title, year, artists, ...input}flow() defines the operation pipeline upfront and returns a function that runs that flow against different inputs. Each operation receives the current accumulated state and must return an object patch that gets shallow-merged in. Errors are handled per operation using Pipelean strategies, the same as series and scan. See docs/reference.md for the full reference.