{"_id":"@backendkit-labs/pipeline","_rev":"6-3af049ed53fcc42074e874724429ded7","name":"@backendkit-labs/pipeline","dist-tags":{"latest":"0.3.2"},"versions":{"0.1.0":{"name":"@backendkit-labs/pipeline","version":"0.1.0","keywords":["pipeline","chain-of-responsibility","middleware","handler","async","typescript","nestjs","node","pattern"],"author":{"name":"Mairon José Cuello Martínez"},"license":"MIT","_id":"@backendkit-labs/pipeline@0.1.0","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"homepage":"https://github.com/backendkit-dev/backendkit-monorepo/tree/master/packages/pipeline#readme","bugs":{"url":"https://github.com/backendkit-dev/backendkit-monorepo/issues"},"dist":{"shasum":"8305a15ada3d19897a581fd42a65d9394bda800a","tarball":"https://registry.npmjs.org/@backendkit-labs/pipeline/-/pipeline-0.1.0.tgz","fileCount":20,"integrity":"sha512-zk2y77fNBMDfJGAISBC3gAhiL8qBiX4Jy8ZRESJpPm2Bef82HsDwHAnpLVtsxo0p0yAPZGKUZVq825UGbuOFHg==","signatures":[{"sig":"MEUCIQDctXNaSQhaGtINxkrJRqBE0RJStfEeB82RpLM/0psb2AIgNJW/qm+i2Whbarcpb59Ky6ymaVeESuIR+N0VnR20ulY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50999},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","import":"./dist/nestjs/index.js","require":"./dist/nestjs/index.cjs"}},"gitHead":"14d8bffd0c267150b7e30887a2a6b1865c21b852","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm run test && npm run lint"},"_npmUser":{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"},"repository":{"url":"git+https://github.com/backendkit-dev/backendkit-monorepo.git","type":"git","directory":"packages/pipeline"},"_npmVersion":"11.8.0","description":"Type-safe async pipeline — Chain of Responsibility with stop-on-first / collect-all modes, conditional steps, observability hooks, and optional NestJS integration","directories":{},"sideEffects":false,"_nodeVersion":"22.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.0","tsup":"^8.0.0","eslint":"^9.0.0","vitest":"^2.0.0","@eslint/js":"^9.39.4","typescript":"^5.5.0","@types/node":"^22.0.0","@nestjs/core":"^10.0.0","@nestjs/common":"^10.0.0","reflect-metadata":"^0.2.0","typescript-eslint":"^8.59.3"},"peerDependencies":{"rxjs":">=7.0.0","@nestjs/core":">=10.0.0","@nestjs/common":">=10.0.0"},"peerDependenciesMeta":{"rxjs":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/pipeline_0.1.0_1778777793821_0.03971510149844182","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@backendkit-labs/pipeline","version":"0.1.1","keywords":["pipeline","chain-of-responsibility","middleware","handler","async","typescript","nestjs","node","pattern"],"author":{"name":"BackendKit Labs","email":"backendkit.dev@gmail.com"},"license":"Apache-2.0","_id":"@backendkit-labs/pipeline@0.1.1","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"homepage":"https://github.com/backendkit-dev/backendkit-monorepo/tree/master/packages/pipeline#readme","bugs":{"url":"https://github.com/backendkit-dev/backendkit-monorepo/issues"},"dist":{"shasum":"928d15b6f3966bd1eea4eb2baa6232afa3d2fd3b","tarball":"https://registry.npmjs.org/@backendkit-labs/pipeline/-/pipeline-0.1.1.tgz","fileCount":21,"integrity":"sha512-ZMp+SS9KZ+Kwx4onUa5bpK+qAZ3Q5hrdcBOOtp9wNz4gL3bZefU6U0wjIMwJFt27dP2sBrZhtaxp9yHcChFXiQ==","signatures":[{"sig":"MEUCIHBs2nZvxqRLp2qlitF9hon6Rblv0Z1N37IWdlQtYjQeAiEAjUbOpk5dAPdDBWz4o6K3td+kdIDijbxlZb05cJZM9HU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":61731},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","import":"./dist/nestjs/index.js","require":"./dist/nestjs/index.cjs"}},"gitHead":"f6769720db5a99cfb74b9d2c46a99b3b648b86aa","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm run test && npm run lint"},"_npmUser":{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"},"repository":{"url":"git+https://github.com/backendkit-dev/backendkit-monorepo.git","type":"git","directory":"packages/pipeline"},"_npmVersion":"11.8.0","description":"Type-safe async pipeline â€” Chain of Responsibility with stop-on-first / collect-all modes, conditional steps, observability hooks, and optional NestJS integration","directories":{},"sideEffects":false,"_nodeVersion":"22.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.0","tsup":"^8.0.0","eslint":"^9.0.0","vitest":"^2.0.0","@eslint/js":"^9.39.4","typescript":"^5.5.0","@types/node":"^22.0.0","@nestjs/core":"^10.0.0","@nestjs/common":"^10.0.0","reflect-metadata":"^0.2.0","typescript-eslint":"^8.59.3"},"peerDependencies":{"rxjs":">=7.0.0","@nestjs/core":">=10.0.0","@nestjs/common":">=10.0.0"},"peerDependenciesMeta":{"rxjs":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/pipeline_0.1.1_1778797339580_0.7939712021830465","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@backendkit-labs/pipeline","version":"0.2.0","keywords":["pipeline","chain-of-responsibility","middleware","handler","async","typescript","nestjs","node","pattern"],"author":{"name":"BackendKit Labs","email":"hello@backendkitlabs.dev"},"license":"Apache-2.0","_id":"@backendkit-labs/pipeline@0.2.0","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"homepage":"https://backendkitlabs.dev/docs/pipeline/","bugs":{"url":"https://github.com/BackendKit-labs/backendkit-monorepo/issues"},"dist":{"shasum":"1eaf50bab524abe8da6d4a7acb33691eebb9e9f1","tarball":"https://registry.npmjs.org/@backendkit-labs/pipeline/-/pipeline-0.2.0.tgz","fileCount":21,"integrity":"sha512-u99TxmaCz7yh3lwt3PNUjQGKQ0Vuq2DeqyQBP7CMj1ET/Dv0aok8TSM5yJrdqDd6OW1OF4+CmtDvJ5FUCIHOSQ==","signatures":[{"sig":"MEUCIClVRSQZHJS64RTgzAytqYJneIXXTeJ2bQnjqt3/u9fCAiEAj3j6jYU44ubo2sAB8LQXYwjI66WS7IvqBO1xVebSSuc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":62304},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","import":"./dist/nestjs/index.js","require":"./dist/nestjs/index.cjs"}},"gitHead":"d211f0af129f4431674d36bd68128e6eed94aeab","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm run test && npm run lint"},"_npmUser":{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"},"repository":{"url":"git+https://github.com/BackendKit-labs/backendkit-monorepo.git","type":"git","directory":"packages/pipeline"},"_npmVersion":"11.8.0","description":"Type-safe async pipeline â€” Chain of Responsibility with stop-on-first / collect-all modes, conditional steps, observability hooks, and optional NestJS integration","directories":{},"sideEffects":false,"_nodeVersion":"22.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.0","tsup":"^8.0.0","eslint":"^9.0.0","vitest":"^2.0.0","@eslint/js":"^9.39.4","typescript":"^5.5.0","@types/node":"^22.0.0","@nestjs/core":"^10.0.0","@nestjs/common":"^10.0.0","reflect-metadata":"^0.2.0","typescript-eslint":"^8.59.3"},"peerDependencies":{"rxjs":">=7.0.0","@nestjs/core":">=10.0.0","@nestjs/common":">=10.0.0"},"peerDependenciesMeta":{"rxjs":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/pipeline_0.2.0_1778973720394_0.4720608009343623","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@backendkit-labs/pipeline","version":"0.3.0","keywords":["pipeline","chain-of-responsibility","middleware","handler","async","typescript","nestjs","node","pattern"],"author":{"name":"BackendKit Labs","email":"hello@backendkitlabs.dev"},"license":"Apache-2.0","_id":"@backendkit-labs/pipeline@0.3.0","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"homepage":"https://backendkitlabs.dev/docs/pipeline/","bugs":{"url":"https://github.com/BackendKit-labs/backendkit-monorepo/issues"},"dist":{"shasum":"3a3da380959688b82efb981b1fd4b354ef952d17","tarball":"https://registry.npmjs.org/@backendkit-labs/pipeline/-/pipeline-0.3.0.tgz","fileCount":21,"integrity":"sha512-P47JiAEsZ/3L1YycRPWeElPNazwDT8T7xPUQiy52ODT9r8oDrpyF+sg61dJwf2req1m0tphpxIBWj3tqrG9UmA==","signatures":[{"sig":"MEUCIQDKtGkALLCAh8QJQ4oTiLSixwZvQC5PTVs0pYn81gLPewIgfge1Vs7Xq3gkX7B5Pehf8n4mimllOH6kzFYa3h3THkU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":87604},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","import":"./dist/nestjs/index.js","require":"./dist/nestjs/index.cjs"}},"gitHead":"1b9f0255aa8c662fdab77574f0d83331bc809f57","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm run test && npm run lint"},"_npmUser":{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"},"repository":{"url":"git+https://github.com/BackendKit-labs/backendkit-monorepo.git","type":"git","directory":"packages/pipeline"},"_npmVersion":"11.8.0","description":"Type-safe async pipeline â€” Chain of Responsibility with stop-on-first / collect-all modes, conditional steps, observability hooks, and optional NestJS integration","directories":{},"sideEffects":false,"_nodeVersion":"22.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.0","tsup":"^8.0.0","eslint":"^9.0.0","vitest":"^2.0.0","@eslint/js":"^9.39.4","typescript":"^5.5.0","@types/node":"^22.0.0","@nestjs/core":"^10.0.0","@nestjs/common":"^10.0.0","@nestjs/testing":"^10.4.22","reflect-metadata":"^0.2.0","typescript-eslint":"^8.59.3"},"peerDependencies":{"rxjs":">=7.0.0","@nestjs/core":">=10.0.0","@nestjs/common":">=10.0.0"},"peerDependenciesMeta":{"rxjs":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/pipeline_0.3.0_1779046364760_0.528311388161012","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@backendkit-labs/pipeline","version":"0.3.1","keywords":["pipeline","chain-of-responsibility","middleware","handler","async","typescript","nestjs","node","pattern"],"author":{"name":"BackendKit Labs","email":"hello@backendkitlabs.dev"},"license":"Apache-2.0","_id":"@backendkit-labs/pipeline@0.3.1","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"homepage":"https://backendkitlabs.dev/docs/pipeline/","bugs":{"url":"https://github.com/BackendKit-labs/backendkit-monorepo/issues"},"dist":{"shasum":"752a9be4d072c7079032321e235c20996e4943ad","tarball":"https://registry.npmjs.org/@backendkit-labs/pipeline/-/pipeline-0.3.1.tgz","fileCount":21,"integrity":"sha512-AJCHeek+lyYHdCbc8ARMdO4ogsFO8JFnEIb0XgRGnUQa+9lHJxx1KB3/KzNedSlemUkXVr8SoELsyf2bvQgopw==","signatures":[{"sig":"MEUCIQD/8xl47Lq2uHCcRt10JWASmj/wSR5JAviv76NDdtVwUAIgL5GlZTlsR9sR3bL5NuWD9H8jFBs1TDkJi70HTiaY5TA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":90449},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","import":"./dist/nestjs/index.js","require":"./dist/nestjs/index.cjs"}},"gitHead":"0065ed5c1d58c3a1d7d752a0dcb71607e3ab9f50","scripts":{"dev":"tsup --watch","lint":"eslint src/","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm run test && npm run lint"},"_npmUser":{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"},"repository":{"url":"git+https://github.com/BackendKit-labs/backendkit-monorepo.git","type":"git","directory":"packages/pipeline"},"_npmVersion":"11.8.0","description":"Type-safe async pipeline â€” Chain of Responsibility with stop-on-first / collect-all modes, conditional steps, observability hooks, and optional NestJS integration","directories":{},"sideEffects":false,"_nodeVersion":"22.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.0","tsup":"^8.0.0","eslint":"^9.0.0","vitest":"^2.0.0","@eslint/js":"^9.39.4","typescript":"^5.5.0","@types/node":"^22.0.0","@nestjs/core":"^10.0.0","@nestjs/common":"^10.0.0","@nestjs/testing":"^10.4.22","reflect-metadata":"^0.2.0","typescript-eslint":"^8.59.3"},"peerDependencies":{"rxjs":">=7.0.0","@nestjs/core":">=10.0.0","@nestjs/common":">=10.0.0"},"peerDependenciesMeta":{"rxjs":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/pipeline_0.3.1_1779195858097_0.4801942310480034","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@backendkit-labs/pipeline","version":"0.3.2","license":"Apache-2.0","author":{"name":"BackendKit Labs","email":"hello@backendkitlabs.dev"},"description":"Type-safe async pipeline â€” Chain of Responsibility with stop-on-first / collect-all modes, conditional steps, observability hooks, and optional NestJS integration","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","import":"./dist/nestjs/index.js","require":"./dist/nestjs/index.cjs"}},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","typecheck":"tsc --noEmit","lint":"eslint src/","prepublishOnly":"npm run build && npm run test && npm run lint"},"keywords":["pipeline","chain-of-responsibility","middleware","handler","async","typescript","nestjs","node","pattern"],"homepage":"https://backendkitlabs.dev/docs/pipeline/","repository":{"type":"git","url":"git+https://github.com/BackendKit-labs/backendkit-monorepo.git","directory":"packages/pipeline"},"bugs":{"url":"https://github.com/BackendKit-labs/backendkit-monorepo/issues"},"publishConfig":{"access":"public"},"sideEffects":false,"engines":{"node":">=18"},"peerDependencies":{"@nestjs/common":">=10.0.0","@nestjs/core":">=10.0.0","rxjs":">=7.0.0"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true},"@nestjs/core":{"optional":true},"rxjs":{"optional":true}},"devDependencies":{"@eslint/js":"^9.39.4","@nestjs/common":"^10.0.0","@nestjs/core":"^10.0.0","@nestjs/testing":"^10.4.22","@types/node":"^22.0.0","eslint":"^9.0.0","reflect-metadata":"^0.2.0","rxjs":"^7.8.0","tsup":"^8.0.0","typescript":"^5.5.0","typescript-eslint":"^8.59.3","vitest":"^2.0.0"},"gitHead":"ef9c9f6f2ef9d8c17aef7d082c9e9179c8ece74c","_id":"@backendkit-labs/pipeline@0.3.2","_nodeVersion":"22.16.0","_npmVersion":"11.8.0","dist":{"integrity":"sha512-B14ET2MHB+OeD4bpwMxiwHad59JLZyCi/V3wFtEsAa9fbpJD3hI/25babDPem8R5MYD6Eh1ey7YOLnlXe8P+qg==","shasum":"d0cdeaec7806baf9bcbde7ac7feb71a1df9ce1ef","tarball":"https://registry.npmjs.org/@backendkit-labs/pipeline/-/pipeline-0.3.2.tgz","fileCount":21,"unpackedSize":91003,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICrG73ht1PLee0xQekha89X/efXiodGFjQIQhgg9sfFxAiBKkAW9sPKvh/6VMxXZY9poloxP5vcneADb3T0SMCdJgQ=="}]},"_npmUser":{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"},"directories":{},"maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pipeline_0.3.2_1779230286215_0.28810117856204775"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-14T16:56:33.666Z","modified":"2026-05-19T22:38:06.507Z","0.1.0":"2026-05-14T16:56:33.976Z","0.1.1":"2026-05-14T22:22:19.736Z","0.2.0":"2026-05-16T23:22:00.607Z","0.3.0":"2026-05-17T19:32:44.904Z","0.3.1":"2026-05-19T13:04:18.298Z","0.3.2":"2026-05-19T22:38:06.371Z"},"bugs":{"url":"https://github.com/BackendKit-labs/backendkit-monorepo/issues"},"author":{"name":"BackendKit Labs","email":"hello@backendkitlabs.dev"},"license":"Apache-2.0","homepage":"https://backendkitlabs.dev/docs/pipeline/","keywords":["pipeline","chain-of-responsibility","middleware","handler","async","typescript","nestjs","node","pattern"],"repository":{"type":"git","url":"git+https://github.com/BackendKit-labs/backendkit-monorepo.git","directory":"packages/pipeline"},"description":"Type-safe async pipeline â€” Chain of Responsibility with stop-on-first / collect-all modes, conditional steps, observability hooks, and optional NestJS integration","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"readme":"# @backendkit-labs/pipeline\n\n[![npm version](https://img.shields.io/npm/v/@backendkit-labs/pipeline?style=flat-square&color=cb3837)](https://www.npmjs.com/package/@backendkit-labs/pipeline)\n[![CI](https://img.shields.io/github/actions/workflow/status/BackendKit-labs/backendkit-monorepo/ci.yml?style=flat-square&label=CI)](https://github.com/BackendKit-labs/backendkit-monorepo/actions/workflows/ci.yml)\n[![License](https://img.shields.io/npm/l/@backendkit-labs/pipeline?style=flat-square)](LICENSE)\n[![Node](https://img.shields.io/node/v/@backendkit-labs/pipeline?style=flat-square)](package.json)\n[![Docs](https://img.shields.io/badge/docs-backendkitlabs.dev-4f7eff?style=flat-square)](https://backendkitlabs.dev/docs/pipeline/)\n\n> Type-safe async pipeline for Node.js — Chain of Responsibility pattern with stop-on-first / collect-all modes, conditional steps, observability hooks, and optional NestJS integration.\n\nEach step in the pipeline receives the current context, transforms it, and returns a typed result. If a step fails, the pipeline can stop immediately or continue collecting all errors — your choice per pipeline.\n\n---\n\n## Minimal Example\n\nSelf-contained runnable example — no NestJS, one file, realistic scenario.\n\n```bash\ngit clone https://github.com/BackendKit-labs/backendkit-monorepo.git\ncd backendkit-monorepo/examples/minimal-pipeline\nnpm install && npm start\n```\n\nShows a 3-step order pipeline (validate → charge → ship) in `stop-on-first` mode. Runs three scenarios: happy path, validation failure (payment never called), and flaky payment. → [full source](https://github.com/BackendKit-labs/backendkit-monorepo/tree/master/examples/minimal-pipeline)\n\n---\n\n## Installation\n\n```bash\nnpm install @backendkit-labs/pipeline\n```\n\nNestJS peer dependencies (only for the `/nestjs` subpath):\n\n```bash\nnpm install @nestjs/common @nestjs/core rxjs\n```\n\n---\n\n## TypeScript Configuration\n\n### Subpath exports (`/nestjs`)\n\nThis package uses the `exports` field in `package.json` to expose the `/nestjs` subpath. TypeScript's ability to resolve it depends on the `moduleResolution` setting in your `tsconfig.json`.\n\n**Modern resolution (recommended) — no extra config needed:**\n\n```json\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"bundler\"\n  }\n}\n```\n\n`\"bundler\"`, `\"node16\"`, and `\"nodenext\"` all understand the `exports` field natively. This is the recommended setting for any project using a bundler (Webpack, esbuild, Vite) or for NestJS projects on TypeScript ≥ 5.\n\n**Legacy resolution (`\"node\"`) — add `paths` aliases:**\n\nNestJS projects generated before ~2024 default to `\"moduleResolution\": \"node\"`, which ignores the `exports` field entirely. TypeScript won't find the types for `@backendkit-labs/pipeline/nestjs` unless you add explicit path aliases:\n\n```json\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"paths\": {\n      \"@backendkit-labs/pipeline/nestjs\": [\n        \"./node_modules/@backendkit-labs/pipeline/dist/nestjs/index\"\n      ]\n    }\n  }\n}\n```\n\n> **Why does this happen?** The `\"node\"` resolver was designed before subpath exports existed. It only knows how to find `main` and `types` at the root of a package — it does not read the `exports` map. The `paths` alias manually points TypeScript to the right `.d.ts` file for the subpath.\n>\n> The `splitting: true` tsup option (which this package uses) and this `paths` configuration solve completely different problems. `splitting` fixes a **runtime** class identity issue — ensuring there is only one copy of a class in memory across both bundles. The `paths` alias fixes a **compile-time** issue — helping TypeScript find the types. Both may be needed in a legacy project.\n\n---\n\n### NestJS decorator support\n\nNestJS requires two compiler options to be enabled:\n\n```json\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true\n  }\n}\n```\n\nAnd `reflect-metadata` must be imported once at application startup, before any NestJS module is loaded:\n\n```typescript\n// main.ts\nimport 'reflect-metadata';\nimport { NestFactory } from '@nestjs/core';\nimport { AppModule } from './app.module';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  await app.listen(3000);\n}\nbootstrap();\n```\n\n> NestJS CLI scaffolds both of these automatically. You only need to check this if you are setting up a project manually or if decorator-related DI errors appear at runtime.\n\n---\n\n## Quick Start — Framework-agnostic\n\n```typescript\nimport { pipeline, Ok, Err } from '@backendkit-labs/pipeline';\nimport type { PipelineStep, StepResult } from '@backendkit-labs/pipeline';\n\ninterface OrderCtx {\n  productId: string;\n  quantity:  number;\n  stock:     number;\n  price:     number;\n  total:     number;\n}\n\ninterface OrderError {\n  code:    string;\n  message: string;\n}\n\nclass StockStep implements PipelineStep<OrderCtx, OrderError> {\n  async handle(ctx: OrderCtx): Promise<StepResult<OrderCtx, OrderError>> {\n    if (ctx.stock < ctx.quantity) {\n      return Err({ code: 'INSUFFICIENT_STOCK', message: 'Not enough stock' });\n    }\n    return Ok(ctx);\n  }\n}\n\nclass PricingStep implements PipelineStep<OrderCtx, OrderError> {\n  async handle(ctx: OrderCtx): Promise<StepResult<OrderCtx, OrderError>> {\n    return Ok({ ...ctx, total: ctx.price * ctx.quantity });\n  }\n}\n\n// Build and run\nconst result = await pipeline<OrderCtx, OrderError>()\n  .pipe(new StockStep())\n  .pipe(new PricingStep())\n  .run({ productId: 'p1', quantity: 2, stock: 10, price: 50, total: 0 });\n\nif (result.ok) {\n  console.log(result.value.total);        // 100\n  console.log(result.executedSteps);      // ['StockStep', 'PricingStep']\n} else {\n  console.log(result.error.failedStep);   // 'StockStep'\n  console.log(result.error.cause);        // { code: 'INSUFFICIENT_STOCK', ... }\n}\n```\n\n---\n\n## Quick Start — NestJS\n\n```typescript\n// order.pipeline.ts\nimport { definePipeline } from '@backendkit-labs/pipeline';\nimport type { OrderCtx, OrderError } from './order.types';\n\nexport const ORDER_PIPELINE = definePipeline<OrderCtx, OrderError>('order');\n```\n\n```typescript\n// app.module.ts\nimport { Module } from '@nestjs/common';\nimport { PipelineModule } from '@backendkit-labs/pipeline/nestjs';\nimport { ORDER_PIPELINE } from './order.pipeline';\nimport { StockStep, PricingStep, NotifyStep } from './steps';\n\n@Module({\n  imports: [\n    PipelineModule.forRoot({\n      pipelines: [\n        {\n          token:   ORDER_PIPELINE,\n          steps:   [StockStep, PricingStep, NotifyStep],\n          options: {\n            onError: (step, err) => logger.error(`Pipeline failed at ${step}`, err),\n          },\n        },\n      ],\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n```typescript\n// order.service.ts\nimport { Injectable } from '@nestjs/common';\nimport { InjectPipeline } from '@backendkit-labs/pipeline/nestjs';\nimport { Pipeline } from '@backendkit-labs/pipeline';\nimport { ORDER_PIPELINE } from './order.pipeline';\nimport type { OrderCtx, OrderError } from './order.types';\n\n@Injectable()\nexport class OrderService {\n  constructor(\n    @InjectPipeline(ORDER_PIPELINE)\n    private readonly pipeline: Pipeline<OrderCtx, OrderError>,\n  ) {}\n\n  async processOrder(ctx: OrderCtx) {\n    return this.pipeline.run(ctx);\n  }\n}\n```\n\n---\n\n## API\n\n### `pipeline(options?)`\n\nCreates a new pipeline builder. Throws `TypeError` at `run()` time if `mode` is not `'stop-on-first'` or `'collect-all'`.\n\n```typescript\nconst p = pipeline<TContext, TError>(options?);\n```\n\n#### Options\n\n```typescript\npipeline<Ctx, Err>({\n  // 'stop-on-first' — stop and return on the first failure (default)\n  // 'collect-all'   — run all steps, accumulate every failure\n  mode: 'stop-on-first',\n\n  onStep(stepName, ctx) {\n    logger.debug(`[pipeline] → ${stepName}`);\n  },\n\n  onStepComplete(stepName, ctx, durationMs) {\n    metrics.timing(`step.${stepName}`, durationMs);\n  },\n\n  onError(stepName, error) {\n    logger.error(`[pipeline] ✗ ${stepName}`, error);\n  },\n\n  onComplete(ctx, durationMs, metadata) {\n    metrics.timing('pipeline.total', durationMs);\n    // metadata: { executedSteps: string[], failures: PipelineStepFailure<Err>[] }\n  },\n});\n```\n\n---\n\n### `.pipe(step)`\n\nAdds a step that always runs.\n\n```typescript\np.pipe(new StockStep())\n .pipe(new PricingStep());\n```\n\n---\n\n### `.pipeIf(condition, step)`\n\nAdds a step that runs only when `condition(ctx)` returns `true`. The condition receives the context **after** all previous steps have transformed it. If the condition throws, it is treated as a step failure (same behaviour as the step itself failing).\n\n```typescript\np.pipe(new BaseStep())\n .pipeIf(ctx => ctx.hasDiscount, new DiscountStep())\n .pipe(new FinalStep());\n```\n\n---\n\n### `.run(ctx)`\n\nExecutes the pipeline and returns a `PipelineRunResult`.\n\n```typescript\nconst result = await p.run(initialCtx);\n\n// Success\nresult.ok            // true\nresult.value         // final context\nresult.executedSteps // ['StockStep', 'PricingStep']\nresult.durationMs    // total duration\n\n// Failure\nresult.ok                    // false\nresult.error.failedStep      // 'StockStep'\nresult.error.cause           // original typed error\nresult.error.executedSteps   // steps that ran before the failure\nresult.error.durationMs      // total duration\nresult.error.failures        // all failures — one entry for stop-on-first, N for collect-all\nresult.error.mode            // 'stop-on-first' | 'collect-all'\n```\n\n---\n\n### `Ok(value)` / `Err(error)`\n\nHelpers for returning step results.\n\n```typescript\nimport { Ok, Err } from '@backendkit-labs/pipeline';\n\nasync handle(ctx): Promise<StepResult<Ctx, Err>> {\n  if (!valid) return Err({ code: 'INVALID' });\n  return Ok({ ...ctx, validated: true });\n}\n```\n\n---\n\n### `PipelineStep<TContext, TError>`\n\nInterface your step classes implement.\n\n```typescript\nimport type { PipelineStep, StepResult } from '@backendkit-labs/pipeline';\n\nclass MyStep implements PipelineStep<Ctx, MyError> {\n  // Optional — overrides constructor.name in error reports and hook calls\n  readonly stepName = 'MyStep';\n\n  async handle(ctx: Ctx): Promise<StepResult<Ctx, MyError>> {\n    // ...\n  }\n}\n```\n\n---\n\n## Error Modes\n\n### `stop-on-first` (default)\n\nStops at the first failure. Use when later steps depend on earlier ones being successful.\n\n```typescript\npipeline({ mode: 'stop-on-first' })\n  .pipe(new AuthStep())      // if this fails → stop, PaymentStep never runs\n  .pipe(new PaymentStep())\n  .run(ctx);\n```\n\n### `collect-all`\n\nRuns every step regardless of failures. Use when steps are independent and you want to report all errors at once — e.g., form validation.\n\n```typescript\npipeline({ mode: 'collect-all' })\n  .pipe(new ValidateNameStep())\n  .pipe(new ValidateEmailStep())\n  .pipe(new ValidatePhoneStep())\n  .run(formData);\n\n// result.error.failures → [{ step: 'ValidateEmailStep', cause: ... }, { step: 'ValidatePhoneStep', cause: ... }]\n```\n\n---\n\n## NestJS Integration\n\n### `definePipeline<TContext, TError>(name)`\n\nCreates a typed injection token. Define it once and share across module and service.\n\n```typescript\nexport const ORDER_PIPELINE = definePipeline<OrderCtx, OrderError>('order');\n// PipelineToken<OrderCtx, OrderError>\n```\n\n### `PipelineModule.forRoot(options)`\n\nRegisters pipelines globally. Each step class is resolved via NestJS DI, so steps can inject other services.\n\n```typescript\nPipelineModule.forRoot({\n  pipelines: [\n    {\n      token:   ORDER_PIPELINE,\n      steps:   [StockStep, PricingStep, NotifyStep],  // resolved via DI\n      options: { mode: 'stop-on-first', onError: ... },\n    },\n  ],\n})\n```\n\n### `@InjectPipeline(token)`\n\nParameter decorator for injecting a pipeline into a service.\n\n```typescript\nconstructor(\n  @InjectPipeline(ORDER_PIPELINE)\n  private readonly orderPipeline: Pipeline<OrderCtx, OrderError>,\n) {}\n```\n\n---\n\n## Use Cases\n\n| Scenario | Mode |\n|---|---|\n| Order processing (stock → payment → notify) | `stop-on-first` |\n| Form / DTO validation (collect all field errors) | `collect-all` |\n| User onboarding (KYC → plan → welcome email) | `stop-on-first` |\n| File processing (validate → scan → compress → upload) | `stop-on-first` |\n| Webhook processing (verify signature → parse → deduplicate → route) | `stop-on-first` |\n| Pricing pipeline (base → volume discount → tax → currency) | `stop-on-first` |\n\n---\n\n## Design Notes\n\n### Context is immutable by convention\n\nEach step returns a **new** context object rather than mutating the existing one. This makes each step's input/output explicit and easy to trace in logs.\n\n```typescript\n// Do this\nreturn Ok({ ...ctx, total: ctx.price * ctx.quantity });\n\n// Not this\nctx.total = ctx.price * ctx.quantity;\nreturn Ok(ctx);\n```\n\n### Steps are plain classes\n\nSteps don't extend a base class or require special decorators. They just implement `PipelineStep<TContext, TError>`. This makes them easy to test in isolation:\n\n```typescript\nconst result = await new StockStep().handle({ stock: 0, quantity: 5, ... });\nexpect(result.ok).toBe(false);\n```\n\n### NestJS DI class identity\n\n`PipelineModule.forRoot()` resolves step classes via NestJS DI and wires them into the pipeline at startup. All steps share the same DI context — no class identity issues.\n\n---\n\n## License\n\nApache-2.0 — [BackendKit Labs](https://github.com/BackendKit-labs)\n","readmeFilename":"README.md"}