{"_id":"@backendkit-labs/result","_rev":"6-059e7404d1e3f36023c969dcabab9870","name":"@backendkit-labs/result","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@backendkit-labs/result","version":"0.1.0","keywords":["result","either","monad","error-handling","functional","railway","nestjs","typescript","node"],"author":{"name":"Mairon José Cuello Martínez"},"license":"MIT","_id":"@backendkit-labs/result@0.1.0","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"homepage":"https://github.com/backendkit-dev/backendkit-monorepo/tree/master/packages/result#readme","bugs":{"url":"https://github.com/backendkit-dev/backendkit-monorepo/issues"},"dist":{"shasum":"6be586ff0556b7cc67b63af94767d5374dcf99e6","tarball":"https://registry.npmjs.org/@backendkit-labs/result/-/result-0.1.0.tgz","fileCount":16,"integrity":"sha512-s7+efDtsr7V981A8TMmLqyRvdOcBjSvcWggIDqx3/MVH1HDKc4Ii3GKU0lUYq1da4a0pUjwLD6HnMlrpJSJJWA==","signatures":[{"sig":"MEUCIQC0RhzrqOlNVKh7dXXR2pclwJ+eOVH9LToJfA5/17OzIwIgZK5Ps2dzXl5SVyB/ODSuZ8IGVeev+WwKv1vk3EAYORw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":174582},"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":"21ae9b91d456a1549e8d3df89d5499c2f48496a5","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/result"},"_npmVersion":"11.8.0","description":"Type-safe Result monad for Node.js — generic error types, observability, resilience, and optional NestJS integration","directories":{},"sideEffects":false,"_nodeVersion":"22.16.0","_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/result_0.1.0_1778707553953_0.9426157699291238","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@backendkit-labs/result","version":"0.1.1","keywords":["result","either","monad","error-handling","functional","railway","nestjs","typescript","node"],"author":{"name":"Mairon José Cuello Martínez"},"license":"MIT","_id":"@backendkit-labs/result@0.1.1","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"homepage":"https://github.com/backendkit-dev/backendkit-monorepo/tree/master/packages/result#readme","bugs":{"url":"https://github.com/backendkit-dev/backendkit-monorepo/issues"},"dist":{"shasum":"0d402948e96a7402b99b0659b53ddec1d75d97b8","tarball":"https://registry.npmjs.org/@backendkit-labs/result/-/result-0.1.1.tgz","fileCount":16,"integrity":"sha512-yxJ127c7Zxcoe8szQ29j6lh9fQgswgsfx/RhiMZwdl8tK2qcMD4fpRYKDjX4zrEpB2xyutJKoRR9vHxO7TOeIA==","signatures":[{"sig":"MEQCIDWlVpmXp3NBAt4xJM/NdYjlADQkTm2gZ+yCitxiwEQmAiBPFtClXLBhcDEcJ+AMK3uVGGDL+j9xHua9Cbe6Tqya9Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180322},"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":"eb2181f323379aa3e9c5e1a702c2523d50e86acd","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/result"},"_npmVersion":"11.8.0","description":"Type-safe Result monad for Node.js — generic error types, observability, resilience, and optional NestJS integration","directories":{},"sideEffects":false,"_nodeVersion":"22.16.0","_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/result_0.1.1_1778715050701_0.9415878427749453","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@backendkit-labs/result","version":"0.1.2","keywords":["result","either","monad","error-handling","functional","railway","nestjs","typescript","node"],"author":{"name":"Mairon José Cuello Martínez"},"license":"MIT","_id":"@backendkit-labs/result@0.1.2","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"homepage":"https://github.com/backendkit-dev/backendkit-monorepo/tree/master/packages/result#readme","bugs":{"url":"https://github.com/backendkit-dev/backendkit-monorepo/issues"},"dist":{"shasum":"deaee520fd1ea1b61924d4ab3719562659c314ed","tarball":"https://registry.npmjs.org/@backendkit-labs/result/-/result-0.1.2.tgz","fileCount":20,"integrity":"sha512-RyW3OX/BNkNNz2kvhEzgNfTc02Sirl9Dl/52sqUNGfPTnjM+8g7gwwEJIAmWihAV6/2O+Y9OEXsK7QUrgfguVg==","signatures":[{"sig":"MEYCIQCjKbkiB3MH2E/gi5w2WFy3SY6qQ8d2XZGULStl9pLIfwIhAJQLVLX76Z2Yje7aODxodUZJ6+75m1Hh+hRJPVfR/kHA","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":169302},"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":"52019e50a805021390478e92577fe2908820b973","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/result"},"_npmVersion":"11.8.0","description":"Type-safe Result monad for Node.js — generic error types, observability, resilience, 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/result_0.1.2_1778758276428_0.24148758347472854","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@backendkit-labs/result","version":"0.1.3","keywords":["result","either","monad","error-handling","functional","railway","nestjs","typescript","node"],"author":{"name":"BackendKit Labs","email":"backendkit.dev@gmail.com"},"license":"Apache-2.0","_id":"@backendkit-labs/result@0.1.3","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"homepage":"https://github.com/backendkit-dev/backendkit-monorepo/tree/master/packages/result#readme","bugs":{"url":"https://github.com/backendkit-dev/backendkit-monorepo/issues"},"dist":{"shasum":"395c58c957bc68be68387e38fdb7ca61657088d6","tarball":"https://registry.npmjs.org/@backendkit-labs/result/-/result-0.1.3.tgz","fileCount":21,"integrity":"sha512-2tRUMUb0sMrtabz5S/Zyb/1pC4A813whDgabiVrBsJydvxQ8qI7b6EWq3FnIsmjFTYnJU+PS+esxTvI5MPWvQQ==","signatures":[{"sig":"MEQCIHO9RIL2GSW/LZZH6dvM1ElMGl44iN2CUhBu1CZeXVcEAiBOn2X+E+S0DdB7yghYJnN9D2F9LmqnupjAz+PgayKCPw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":182437},"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/result"},"_npmVersion":"11.8.0","description":"Type-safe Result monad for Node.js â€” generic error types, observability, resilience, 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/result_0.1.3_1778796941831_0.08728490596361738","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@backendkit-labs/result","version":"0.2.0","keywords":["result","either","monad","error-handling","functional","railway","nestjs","typescript","node"],"author":{"name":"BackendKit Labs","email":"hello@backendkitlabs.dev"},"license":"Apache-2.0","_id":"@backendkit-labs/result@0.2.0","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"homepage":"https://backendkitlabs.dev/docs/result/","bugs":{"url":"https://github.com/BackendKit-labs/backendkit-monorepo/issues"},"dist":{"shasum":"84fd9ec02ab1bf3c1b95c201d55b8ee1e51b2049","tarball":"https://registry.npmjs.org/@backendkit-labs/result/-/result-0.2.0.tgz","fileCount":21,"integrity":"sha512-5hAlzBTeQSaeuWdDjfDJnVx8D/vuS+1D9ymzKGKd4x3pAXd4qlTam1QA7XoT9iHQyyrPehxmhDGW5GXwBNjMSg==","signatures":[{"sig":"MEUCIQCyxn/zLJg7ybNnT3nHZZw+I4j7oexBtHOPA6w5TXzqHgIgTFyB99VsEBphkibRpNlqxwK7d+r4CkmrG00i7aNApLc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":184273},"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":"f8cd39b773c199d99051bbab5d4960f3680bc86c","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/result"},"_npmVersion":"11.8.0","description":"Type-safe Result monad for Node.js â€” generic error types, observability, resilience, 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/result_0.2.0_1778972872079_0.8221218670893811","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@backendkit-labs/result","version":"0.2.1","license":"Apache-2.0","author":{"name":"BackendKit Labs","email":"hello@backendkitlabs.dev"},"description":"Type-safe Result monad for Node.js â€” generic error types, observability, resilience, 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":["result","either","monad","error-handling","functional","railway","nestjs","typescript","node"],"homepage":"https://backendkitlabs.dev/docs/result/","repository":{"type":"git","url":"git+https://github.com/BackendKit-labs/backendkit-monorepo.git","directory":"packages/result"},"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","@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/result@0.2.1","_nodeVersion":"22.16.0","_npmVersion":"11.8.0","dist":{"integrity":"sha512-r79Y9w8SoD5i/tkSHJLQsZOA/SUiywPJh2aKvB6zayO/4iPAJixzJkfNXrNHHCvSlQmKT+QTRRTHCbUXu1IyrQ==","shasum":"1fa33655b1d336b23d3b210aec9acf9caf94159b","tarball":"https://registry.npmjs.org/@backendkit-labs/result/-/result-0.2.1.tgz","fileCount":21,"unpackedSize":184790,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDKRxF8VHmHTSw22Jd18dc9IEsHcA3PKybixVVLiHzsiAiAiSNh8e2ECR6/vdsNJvJvthlD32+8bJvNZYWciY9Y3bA=="}]},"_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/result_0.2.1_1779230033295_0.8039831322705024"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-13T21:25:53.864Z","modified":"2026-05-19T22:33:53.597Z","0.1.0":"2026-05-13T21:25:54.091Z","0.1.1":"2026-05-13T23:30:50.862Z","0.1.2":"2026-05-14T11:31:16.550Z","0.1.3":"2026-05-14T22:15:42.050Z","0.2.0":"2026-05-16T23:07:52.252Z","0.2.1":"2026-05-19T22:33:53.483Z"},"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/result/","keywords":["result","either","monad","error-handling","functional","railway","nestjs","typescript","node"],"repository":{"type":"git","url":"git+https://github.com/BackendKit-labs/backendkit-monorepo.git","directory":"packages/result"},"description":"Type-safe Result monad for Node.js â€” generic error types, observability, resilience, and optional NestJS integration","maintainers":[{"name":"backendkit.dev","email":"backendkit.dev@gmail.com"}],"readme":"# @backendkit-labs/result\n\n[![npm version](https://img.shields.io/npm/v/@backendkit-labs/result?style=flat-square&color=cb3837)](https://www.npmjs.com/package/@backendkit-labs/result)\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/result?style=flat-square)](LICENSE)\n[![Node](https://img.shields.io/node/v/@backendkit-labs/result?style=flat-square)](package.json)\n[![Docs](https://img.shields.io/badge/docs-backendkitlabs.dev-4f7eff?style=flat-square)](https://backendkitlabs.dev/docs/result/)\n\n> Type-safe Result monad for Node.js. Generic error types, observability, resilience, and optional NestJS integration. Zero runtime dependencies.\n\nReplaces `try/catch` with an explicit, composable type that makes errors visible in the type system. Every operation either succeeds (`ok`) or fails (`fail`) — and the TypeScript compiler enforces that you handle both.\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-result\nnpm install && npm start\n```\n\nShows `Result<T, E>` vs `try/catch` side by side: typed product lookup with `not-found` and `db-unavailable` error variants, handled with `match()`. → [full source](https://github.com/BackendKit-labs/backendkit-monorepo/tree/master/examples/minimal-result)\n\n---\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Core Concepts](#core-concepts)\n- [Constructors](#constructors)\n- [Type Guards](#type-guards)\n- [Transformations](#transformations)\n- [Pattern Matching](#pattern-matching)\n- [Side Effects](#side-effects)\n- [Unwrapping](#unwrapping)\n- [Conversion](#conversion)\n- [Execution — run & track](#execution--run--track)\n- [Resilience](#resilience)\n- [Combinators](#combinators)\n- [Flow — Fluent Pipeline](#flow--fluent-pipeline)\n- [NestJS Integration](#nestjs-integration)\n- [Architecture](#architecture)\n\n---\n\n## Installation\n\n```bash\nnpm install @backendkit-labs/result\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{\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 or NestJS on TypeScript ≥ 5.\n\n**Legacy resolution (`\"node\"`) — add a `paths` alias:**\n\nNestJS projects generated before ~2024 default to `\"moduleResolution\": \"node\"`, which ignores the `exports` field. Add an explicit alias so TypeScript can find the types:\n\n```json\n{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"paths\": {\n      \"@backendkit-labs/result/nestjs\": [\n        \"./node_modules/@backendkit-labs/result/dist/nestjs/index\"\n      ]\n    }\n  }\n}\n```\n\n> **Why?** The `\"node\"` resolver was designed before subpath exports existed and only reads `main`/`types` at the package root — it ignores the `exports` map entirely. The `paths` alias manually points TypeScript to the correct `.d.ts` file.\n\n### NestJS decorator support\n\n```json\n{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true\n  }\n}\n```\n\nAnd import `reflect-metadata` once at application startup:\n\n```typescript\n// main.ts\nimport 'reflect-metadata';\n```\n\n> NestJS CLI scaffolds these automatically. You only need to verify them if setting up a project manually.\n\n---\n\n## Quick Start\n\n```typescript\nimport { ok, fail, run, isOk, isFail, match } from '@backendkit-labs/result';\n\n// Wrap a throwable async call\nconst result = await run(() => fetchUser(userId));\n\n// Handle both branches\nconst message = match(result, {\n  ok:   (user)  => `Welcome, ${user.name}`,\n  fail: (error) => `Error: ${error.message}`,\n});\n\n// Or guard and narrow\nif (isOk(result)) {\n  console.log(result.value.email); // TypeScript knows value exists\n}\nif (isFail(result)) {\n  console.error(result.error);     // TypeScript knows error exists\n}\n```\n\n---\n\n## Core Concepts\n\n### The Result type\n\n```typescript\ntype Result<T, E = Error> =\n  | { readonly ok: true;  readonly value: T }\n  | { readonly ok: false; readonly error: E }\n```\n\nA discriminated union — either a success with a `value` of type `T`, or a failure with an `error` of type `E`. Both branches are explicit in the type, so TypeScript will not let you access `value` without first confirming `ok === true`.\n\nThe error type `E` is fully generic. You can use anything: `Error`, `string`, a union of domain error types, or an enum.\n\n```typescript\n// Typed errors as a discriminated union\ntype UserError =\n  | { code: 'NOT_FOUND'; id: string }\n  | { code: 'FORBIDDEN' }\n  | { code: 'DB_ERROR'; cause: Error }\n\nasync function findUser(id: string): Promise<Result<User, UserError>> { ... }\n```\n\n### RichResult — with observability\n\n```typescript\ntype RichResult<T, E = Error> = Result<T, E> & {\n  readonly durationMs:     number   // execution time in ms\n  readonly timestamp:      string   // ISO 8601 start time\n  readonly operation?:     string   // logical name\n  readonly correlationId?: string   // trace/request ID\n  readonly tags?:          string[] // categorization labels\n}\n```\n\nProduced by `track()`. Carries the same `ok / value / error` shape as a plain `Result` plus timing and metadata — ready for logging, metrics dashboards, or distributed tracing.\n\n---\n\n## Constructors\n\n### `ok(value)`\n\nCreates a successful result.\n\n```typescript\nimport { ok } from '@backendkit-labs/result';\n\nconst r = ok(42);            // Result<number, never>\nconst r = ok({ id: 1 });    // Result<{ id: number }, never>\nconst r = ok(undefined);    // Result<undefined, never>\n```\n\n### `fail(error)`\n\nCreates a failed result.\n\n```typescript\nimport { fail } from '@backendkit-labs/result';\n\nconst r = fail(new Error('network error'));   // Result<never, Error>\nconst r = fail('not-found');                  // Result<never, string>\nconst r = fail({ code: 'FORBIDDEN' });        // Result<never, { code: string }>\n```\n\n### `fromThrowable(fn, errorTransform?)`\n\nWraps a synchronous function that might throw. Catches any exception and converts it to a `fail`.\n\n```typescript\nimport { fromThrowable } from '@backendkit-labs/result';\n\n// Without transform — caught value is cast to E\nconst parsed = fromThrowable(() => JSON.parse(raw));\n// Result<unknown, Error>\n\n// With transform — convert the caught value to your domain error\nconst parsed = fromThrowable<Config, string>(\n  () => JSON.parse(raw),\n  (e) => `Invalid config: ${(e as SyntaxError).message}`,\n);\n// Result<Config, string>\n\n// Practical: reading a file\nconst content = fromThrowable(\n  () => fs.readFileSync('./config.json', 'utf-8'),\n  (e) => new ConfigError('Could not read config file', { cause: e }),\n);\n```\n\n### `fromPromise(promise, errorTransform?)`\n\nConverts a Promise to a `Promise<Result<T, E>>`, catching rejections.\n\n```typescript\nimport { fromPromise } from '@backendkit-labs/result';\n\n// Wrap any existing promise\nconst result = await fromPromise(fetch(url).then(r => r.json()));\n\n// With error transform\nconst result = await fromPromise(\n  db.users.findOrThrow(id),\n  (e) => e instanceof PrismaError && e.code === 'P2025'\n    ? { code: 'NOT_FOUND' as const, id }\n    : { code: 'DB_ERROR' as const, cause: e as Error },\n);\n// Result<User, { code: 'NOT_FOUND'; id: string } | { code: 'DB_ERROR'; cause: Error }>\n```\n\n### `fromNullable(value, errorOnNull)`\n\nConverts a nullable value to a Result. Returns `ok(value)` when non-null/undefined, `fail(error)` otherwise.\n\n```typescript\nimport { fromNullable } from '@backendkit-labs/result';\n\nconst user = cache.get(userId); // User | undefined\n\nconst result = fromNullable(user, { code: 'CACHE_MISS' as const });\n// Result<User, { code: 'CACHE_MISS' }>\n\n// Chaining fromNullable in a pipeline\nconst result = fromNullable(\n  config.database?.host,\n  new ConfigError('database.host is required'),\n);\n```\n\n---\n\n## Type Guards\n\n### `isOk(result)` / `isFail(result)`\n\nNarrow the type to the success or failure branch. After the guard, TypeScript knows the exact shape.\n\n```typescript\nimport { isOk, isFail } from '@backendkit-labs/result';\n\nconst result: Result<User, UserError> = await findUser(id);\n\nif (isOk(result)) {\n  result.value.email; // ✓ TypeScript: value is User\n}\n\nif (isFail(result)) {\n  result.error.code;  // ✓ TypeScript: error is UserError\n}\n\n// Useful in array filters\nconst users = results.filter(isOk).map(r => r.value);\n```\n\n### `isRich(result)`\n\nReturns `true` if the result was produced by `track()` and carries observability metadata.\n\n```typescript\nimport { isRich } from '@backendkit-labs/result';\n\nconst result = await track(() => fetchUser(id));\nif (isRich(result)) {\n  console.log(`Took ${result.durationMs}ms`);\n}\n```\n\n---\n\n## Transformations\n\nAll transformations short-circuit on failure — they skip the function and pass the `fail` result through unchanged.\n\n### `map(result, fn)`\n\nTransform the success value into a different type.\n\n```typescript\nimport { map } from '@backendkit-labs/result';\n\nconst userResult: Result<User, Error> = await run(() => fetchUser(id));\n\nconst nameResult: Result<string, Error> = map(userResult, user => user.name);\n\n// Chain multiple maps\nconst initials = map(\n  map(nameResult, name => name.split(' ')),\n  parts => parts.map(p => p[0]).join(''),\n);\n```\n\n### `mapError(result, fn)`\n\nTransform the error value without touching the success branch.\n\n```typescript\nimport { mapError } from '@backendkit-labs/result';\n\n// Convert infrastructure errors to domain errors\nconst result = mapError(\n  await fromPromise(db.users.find(id)),\n  (dbError) => ({\n    code: 'DB_ERROR' as const,\n    message: 'Failed to fetch user',\n    cause: dbError,\n  }),\n);\n// Result<User, { code: 'DB_ERROR'; message: string; cause: unknown }>\n\n// Translate error messages\nconst localized = mapError(\n  serviceResult,\n  (e) => t(`errors.${e.code}`),\n);\n```\n\n### `flatMap(result, fn)`\n\nChain a Result-returning function. The failure from either the original result or the chained function short-circuits the pipeline.\n\n```typescript\nimport { flatMap, fromNullable } from '@backendkit-labs/result';\n\nconst orderResult = flatMap(\n  await run(() => fetchUser(userId)),\n  (user) => fromNullable(user.activeOrder, { code: 'NO_ACTIVE_ORDER' as const }),\n);\n// Result<Order, Error | { code: 'NO_ACTIVE_ORDER' }>\n```\n\n### `flatMapAsync(result, fn)`\n\nAsync version of `flatMap`.\n\n```typescript\nimport { flatMapAsync } from '@backendkit-labs/result';\n\nconst profileResult = await flatMapAsync(\n  await run(() => fetchUser(userId)),\n  async (user) => run(() => fetchProfile(user.profileId)),\n);\n// Result<Profile, Error>\n```\n\n### `mapAsync(result, fn)`\n\nMaps the success value with an async function.\n\n```typescript\nimport { mapAsync } from '@backendkit-labs/result';\n\nconst enriched = await mapAsync(\n  userResult,\n  async (user) => ({ ...user, permissions: await loadPermissions(user.id) }),\n);\n// Result<User & { permissions: string[] }, Error>\n```\n\n---\n\n## Pattern Matching\n\n### `match(result, handlers)` / `fold(result, handlers)`\n\nExhaustive pattern match — the compiler ensures both branches are handled. `fold` is an alias.\n\n```typescript\nimport { match } from '@backendkit-labs/result';\n\nconst response = match(result, {\n  ok:   (user)  => ({ status: 200, body: user }),\n  fail: (error) => ({ status: error.code === 'NOT_FOUND' ? 404 : 500, body: error }),\n});\n\n// Returning different types from each branch\nconst display = match(paymentResult, {\n  ok:   (payment) => `Payment of $${payment.amount} confirmed`,\n  fail: (error)   => `Payment failed: ${error.message}`,\n});\n\n// Logging pattern\nmatch(result, {\n  ok:   (data)  => logger.info('Operation succeeded', { data }),\n  fail: (error) => logger.error('Operation failed', { error }),\n});\n```\n\n---\n\n## Side Effects\n\n### `tap(result, fn)` / `tapError(result, fn)`\n\nRun a side effect without altering the result. Returns the original result unchanged — useful for logging in the middle of a pipeline.\n\n```typescript\nimport { tap, tapError } from '@backendkit-labs/result';\n\nconst result = tap(\n  await run(() => processPayment(dto)),\n  (payment) => {\n    analytics.track('payment.processed', { amount: payment.amount });\n    logger.info('Payment processed', payment);\n  },\n);\n// result is still Result<Payment, Error>\n\n// Log errors without breaking the chain\nconst result = tapError(\n  await run(() => fetchInventory(sku)),\n  (error) => logger.warn('Inventory fetch failed', { sku, error }),\n);\n\n// Combined\nconst result = tap(\n  tapError(\n    await run(() => fetchUser(id)),\n    (e) => logger.error('User fetch failed', e),\n  ),\n  (user) => cache.set(id, user),\n);\n```\n\n---\n\n## Unwrapping\n\nUse these when you need to extract the raw value — typically at the edge of your application (controller, CLI output, test assertions).\n\n### `unwrap(result)` — throws on failure\n\n```typescript\nimport { unwrap } from '@backendkit-labs/result';\n\nconst user = unwrap(userResult); // throws if fail\n```\n\n### `unwrapOr(result, default)` — safe fallback\n\n```typescript\nimport { unwrapOr } from '@backendkit-labs/result';\n\nconst user = unwrapOr(userResult, defaultUser);\nconst count = unwrapOr(countResult, 0);\nconst items = unwrapOr(listResult, []);\n```\n\n### `unwrapOrElse(result, fn)` — computed fallback\n\n```typescript\nimport { unwrapOrElse } from '@backendkit-labs/result';\n\nconst user = unwrapOrElse(\n  userResult,\n  (error) => error.code === 'NOT_FOUND' ? guestUser : throw error,\n);\n```\n\n### `unwrapError(result)` — extract the error\n\n```typescript\nimport { unwrapError } from '@backendkit-labs/result';\n\nconst error = unwrapError(failResult); // throws if ok\n```\n\n### `expect(result, message)` — custom error message\n\n```typescript\nimport { expect as resultExpect } from '@backendkit-labs/result';\n\nconst config = resultExpect(\n  fromThrowable(() => loadConfig()),\n  'Failed to load configuration — cannot start server',\n);\n```\n\n---\n\n## Conversion\n\n### `toPromise(result)`\n\n```typescript\nimport { toPromise } from '@backendkit-labs/result';\n\n// Bridges Result-based code with Promise-based APIs\nconst user = await toPromise(userResult); // rejects if fail\n```\n\n### `toNullable(result)` / `toUndefined(result)`\n\n```typescript\nimport { toNullable, toUndefined } from '@backendkit-labs/result';\n\nconst user: User | null      = toNullable(userResult);\nconst user: User | undefined = toUndefined(userResult);\n\n// Useful with optional chaining\nconst name = toNullable(userResult)?.name ?? 'Anonymous';\n```\n\n---\n\n## Execution — `run` & `track`\n\n### `run(fn, errorTransform?)`\n\nExecutes any async (or sync) function and captures thrown exceptions as `fail`. The cleanest way to integrate with existing throw-based code.\n\n```typescript\nimport { run } from '@backendkit-labs/result';\n\n// Wraps any async call\nconst result = await run(() => fetch(url).then(r => r.json()));\n\n// With error classification\nconst result = await run<User, UserError>(\n  () => db.users.findOrThrow(id),\n  (e) => e instanceof NotFoundError\n    ? { code: 'NOT_FOUND' as const, id }\n    : { code: 'DB_ERROR' as const, cause: e as Error },\n);\n\n// Sync functions work too\nconst result = await run(() => JSON.parse(raw));\n```\n\n### `track(fn, options?)`\n\nLike `run()` but also measures execution time and attaches metadata. Returns a `RichResult<T, E>`.\n\n```typescript\nimport { track } from '@backendkit-labs/result';\n\nconst result = await track(\n  () => db.users.findOrThrow(id),\n  {\n    operation:     'user.find',\n    correlationId: request.headers['x-correlation-id'],\n    tags:          ['db', 'users'],\n  },\n);\n\nif (result.ok) {\n  logger.info('User fetched', {\n    operation:  result.operation,    // 'user.find'\n    durationMs: result.durationMs,   // e.g. 12\n    timestamp:  result.timestamp,    // '2026-05-13T...'\n    tags:       result.tags,         // ['db', 'users']\n  });\n}\n```\n\n### `enrich(result, options?)` / `simplify(richResult)`\n\nPromote a plain `Result` to `RichResult`, or strip metadata back to a plain `Result`.\n\n```typescript\nimport { enrich, simplify } from '@backendkit-labs/result';\n\n// Attach metadata to an existing result\nconst rich = enrich(ok(user), {\n  operation:     'cache.hit',\n  correlationId: reqId,\n});\n// RichResult<User, never>\n\n// Strip metadata when you no longer need it\nconst plain = simplify(richResult);\n// Result<User, Error>\n```\n\n---\n\n## Resilience\n\n### `retry(fn, options)`\n\nRetries a Result-returning async function on failure.\n\n```typescript\nimport { retry, run } from '@backendkit-labs/result';\n\n// Basic retry\nconst result = await retry(\n  () => run(() => callExternalApi()),\n  { attempts: 3 },\n);\n\n// With delay between attempts\nconst result = await retry(\n  () => run(() => sendEmail(payload)),\n  { attempts: 5, delayMs: 1_000 },\n);\n\n// Stop retrying on specific errors\nconst result = await retry(\n  () => run(() => callApi(), classifyError),\n  {\n    attempts:    4,\n    delayMs:     500,\n    shouldRetry: (error, attempt) => {\n      console.log(`Attempt ${attempt} failed:`, error);\n      return error.code !== 'UNAUTHORIZED'; // don't retry 401\n    },\n    onRetry: (error, attempt) => {\n      metrics.increment('api.retry', { attempt });\n    },\n  },\n);\n```\n\n### `retryWithBackoff(fn, options)`\n\nExponential backoff: delay doubles on each retry, capped at `maxDelayMs`. Supports **jitter** to prevent thundering herd when multiple instances retry simultaneously.\n\n```typescript\nimport { retryWithBackoff, run } from '@backendkit-labs/result';\n\n// 100ms → 200ms → 400ms → 800ms (capped at 1000ms)\nconst result = await retryWithBackoff(\n  () => run(() => fetchWithFlakeyNetwork()),\n  {\n    attempts:   5,\n    delayMs:    100,   // initial delay\n    maxDelayMs: 1_000, // cap\n    shouldRetry: (error) => error.retryable === true,\n  },\n);\n\n// Database deadlock retry pattern\nconst result = await retryWithBackoff(\n  () => run(() => db.transaction(fn), classifyDbError),\n  {\n    attempts:    3,\n    delayMs:     50,\n    maxDelayMs:  500,\n    shouldRetry: (e) => e.code === 'DEADLOCK',\n    onRetry:     (e, n) => logger.warn(`Deadlock retry #${n}`, e),\n  },\n);\n```\n\n#### Jitter\n\nWhen many instances of your service fail at the same time (e.g. a downstream goes down), they all retry on the same schedule — creating a synchronized spike that can overwhelm the recovering service. Jitter spreads those retries across time.\n\n```typescript\n// Full jitter — delay = random(0, computedDelay)\n// Maximum spread. Best for high-concurrency scenarios (many parallel clients).\nawait retryWithBackoff(() => run(() => callApi()), {\n  attempts:   4,\n  delayMs:    500,\n  maxDelayMs: 10_000,\n  jitter:     true,\n});\n\n// Partial jitter — delay ± (computedDelay × factor)\n// Keeps delays close to the backoff curve while adding noise.\n// 0.25 = ±25%: a computed 1000ms delay becomes 750ms–1250ms.\nawait retryWithBackoff(() => run(() => callApi()), {\n  attempts:   4,\n  delayMs:    500,\n  maxDelayMs: 10_000,\n  jitter:     0.25,\n});\n```\n\n| `jitter` value | Behaviour | Use when |\n|---|---|---|\n| `false` / omitted | No randomness — deterministic delays | Tests, single-instance services |\n| `true` | Full jitter: `random(0, delay)` | Many parallel clients retrying the same service |\n| `0.0–1.0` | Partial jitter: `delay ± (delay × factor)` | You want backoff shape preserved with light noise |\n\n### `withTimeout(fn, ms, timeoutError)`\n\nRaces a Result-returning function against a deadline.\n\n```typescript\nimport { withTimeout, run } from '@backendkit-labs/result';\n\n// Enforce SLA on external calls\nconst result = await withTimeout(\n  () => run(() => callSlowApi()),\n  5_000,\n  new TimeoutError('API call exceeded 5s SLA'),\n);\n\n// With typed error\nconst result = await withTimeout<Report, ApiError>(\n  () => run(() => generateReport(params), toApiError),\n  30_000,\n  { code: 'TIMEOUT', message: 'Report generation timed out' },\n);\n\nif (isFail(result) && result.error.code === 'TIMEOUT') {\n  return servePartialReport();\n}\n```\n\n### Combining resilience primitives\n\n```typescript\n// Retry with backoff + timeout on each attempt\nconst result = await withTimeout(\n  () => retryWithBackoff(\n    () => run(() => fetchCriticalData()),\n    { attempts: 3, delayMs: 100, maxDelayMs: 500 },\n  ),\n  10_000,\n  new Error('Gave up after 10s'),\n);\n```\n\n---\n\n## Combinators\n\n### `all(results)` — all must succeed\n\nReturns `ok([...values])` or the first failure.\n\n```typescript\nimport { all, run } from '@backendkit-labs/result';\n\nconst [userResult, orderResult, inventoryResult] = await Promise.all([\n  run(() => fetchUser(userId)),\n  run(() => fetchOrder(orderId)),\n  run(() => fetchInventory(sku)),\n]);\n\nconst combined = all([userResult, orderResult, inventoryResult]);\n// Result<[User, Order, Inventory], Error>\n\nif (isOk(combined)) {\n  const [user, order, inventory] = combined.value;\n}\n```\n\n### `any(operations)` — first success wins\n\nTries operations sequentially, returns the first that succeeds.\n\n```typescript\nimport { any, run } from '@backendkit-labs/result';\n\n// Cache → DB fallback chain\nconst user = await any([\n  () => run(() => cache.get(id)),\n  () => run(() => replicaDb.findUser(id)),\n  () => run(() => primaryDb.findUser(id)),\n]);\n```\n\n### `parallel(operations, options?)` — concurrent execution\n\nRuns all operations concurrently (with optional concurrency limit). Returns all values or the first failure.\n\n```typescript\nimport { parallel, run } from '@backendkit-labs/result';\n\n// Process all at once\nconst result = await parallel(\n  userIds.map(id => () => run(() => fetchUser(id))),\n);\n// Result<User[], Error>\n\n// Limit concurrency to avoid overwhelming downstream\nconst result = await parallel(\n  imageIds.map(id => () => run(() => processImage(id))),\n  { concurrency: 5 },\n);\n\nif (isOk(result)) {\n  const users: User[] = result.value;\n}\n```\n\n### `partition(results)` — split successes and failures\n\n```typescript\nimport { partition } from '@backendkit-labs/result';\n\nconst results = await Promise.all(ids.map(id => run(() => fetchUser(id))));\nconst [users, errors] = partition(results);\n// users: User[]   — all successful values\n// errors: Error[] — all failure values\n\nlogger.info(`Fetched ${users.length} users, ${errors.length} failed`);\n```\n\n### `collect(results)` — success values only\n\nLike `partition` but silently drops failures.\n\n```typescript\nimport { collect } from '@backendkit-labs/result';\n\nconst results = await Promise.all(ids.map(id => run(() => fetchUser(id))));\nconst users = collect(results);\n// User[] — failures are discarded\n```\n\n### `traverse(items, fn)` — map array through a Result function\n\nApplies a Result-returning function to each item. Succeeds only if all items succeed (short-circuits on the first failure).\n\n```typescript\nimport { traverse, fromNullable } from '@backendkit-labs/result';\n\n// Validate every item in an array\nconst result = traverse(\n  requestBody.items,\n  (item) => fromNullable(\n    catalog.get(item.sku),\n    { code: 'SKU_NOT_FOUND' as const, sku: item.sku },\n  ),\n);\n// Result<CatalogItem[], { code: 'SKU_NOT_FOUND'; sku: string }>\n\n// Parse and validate a list of inputs\nconst result = traverse(\n  rawIds,\n  (id) => id.match(/^\\d+$/)\n    ? ok(parseInt(id, 10))\n    : fail(`Invalid ID format: ${id}`),\n);\n```\n\n### `combine2(r1, r2)` / `combine3(r1, r2, r3)` — typed tuples\n\nCombines two or three results into a precisely typed tuple. Short-circuits on the first failure.\n\n```typescript\nimport { combine2, combine3, run } from '@backendkit-labs/result';\n\nconst result = combine2(\n  await run(() => fetchUser(userId)),\n  await run(() => fetchAccount(accountId)),\n);\n// Result<[User, Account], Error>\n\nif (isOk(result)) {\n  const [user, account] = result.value; // fully typed\n}\n\n// Three results\nconst result = combine3(\n  await run(() => fetchUser(userId)),\n  await run(() => fetchPermissions(userId)),\n  await run(() => fetchSettings(userId)),\n);\n// Result<[User, Permission[], Settings], Error>\n```\n\n---\n\n## Flow — Fluent Pipeline\n\n`Flow<T, E>` is a composable wrapper that lets you build transformation pipelines. Each step is skipped if the result is already a failure.\n\n### Starting a pipeline\n\n```typescript\nimport { Flow, ok, fail } from '@backendkit-labs/result';\n\n// From an existing result\nconst flow = Flow.from(ok(42));\nFlow.from(await run(() => fetchUser(id)));\n\n// Empty pipeline (value is void)\nFlow.start().map(() => loadConfig());\n```\n\n### `.map(fn)` / `.mapError(fn)`\n\n```typescript\nconst result = Flow.from(await run(() => fetchUser(id)))\n  .map(user => user.profile)\n  .map(profile => profile.avatar ?? defaultAvatar)\n  .getResult();\n// Result<string, Error>\n\n// Transform errors along the way\nconst result = Flow.from(await run(() => callExternalApi(), toRawError))\n  .mapError(raw => new DomainError(raw.message, raw.code))\n  .getResult();\n```\n\n### `.flatMap(fn)`\n\n```typescript\nconst orderResult = Flow.from(await run(() => fetchUser(userId)))\n  .flatMap(user =>\n    user.activeOrderId\n      ? ok(user.activeOrderId)\n      : fail(new Error('No active order')),\n  )\n  .flatMap(orderId => fromNullable(ordersCache.get(orderId), new Error('Cache miss')))\n  .getResult();\n```\n\n### `.filter(predicate, error)`\n\n```typescript\nconst result = Flow.from(ok(age))\n  .filter(a => a >= 18,       new Error('Must be 18 or older'))\n  .filter(a => a <= 120,      new Error('Age value is unrealistic'))\n  .map(a => categorizeAge(a))\n  .getResult();\n```\n\n### `.tap(fn)` / `.tapError(fn)`\n\n```typescript\nconst result = Flow.from(await run(() => processPayment(dto)))\n  .tap(payment => analytics.track('payment.success', payment))\n  .tap(payment => cache.invalidate(`balance:${payment.userId}`))\n  .tapError(err => logger.error('Payment failed', err))\n  .tapError(err => metrics.increment('payment.failure'))\n  .getResult();\n```\n\n### `.recover(fn)`\n\nConvert a failure into a success — useful for providing defaults.\n\n```typescript\nconst result = Flow.from(await run(() => fetchFromPrimary(key)))\n  .recover(error => {\n    logger.warn('Primary failed, using default', error);\n    return defaultValue;\n  })\n  .getResult();\n// Result<T, never> — failure branch is eliminated\n```\n\n### `.match(handlers)`\n\nTerminate the pipeline with an exhaustive match.\n\n```typescript\nconst httpResponse = Flow.from(await run(() => processRequest(req)))\n  .map(data => ({ status: 200, body: data }))\n  .match({\n    ok:   (response) => response,\n    fail: (error)    => ({ status: 500, body: { message: error.message } }),\n  });\n```\n\n### Full pipeline example\n\n```typescript\nconst response = await Flow.from(\n    await track(\n      () => db.users.findOrThrow(userId),\n      { operation: 'user.fetch', tags: ['db'] },\n    ),\n  )\n  .tapError(e => logger.error('User not found', e))\n  .flatMap(user =>\n    user.isActive\n      ? ok(user)\n      : fail(new ForbiddenError('Account suspended')),\n  )\n  .map(user => ({\n    id:    user.id,\n    name:  user.name,\n    email: user.email,\n  }))\n  .tap(dto => cache.set(`user:${userId}`, dto, { ttl: 60 }))\n  .match({\n    ok:   (dto)   => ({ statusCode: 200, data: dto }),\n    fail: (error) => ({\n      statusCode: error instanceof ForbiddenError ? 403 : 404,\n      message:    error.message,\n    }),\n  });\n```\n\n---\n\n## NestJS Integration\n\nImport from the `/nestjs` subpath.\n\n```typescript\nimport { ResultModule } from '@backendkit-labs/result/nestjs';\n\n@Module({ imports: [ResultModule] })\nexport class AppModule {}\n```\n\n### `@AsResult(operation?)` — wrap method in `run()`\n\nAny exception thrown inside the method becomes a `fail`. The return type becomes `Promise<Result<T, E>>`.\n\n```typescript\nimport { AsResult } from '@backendkit-labs/result/nestjs';\nimport { ok, fail, isOk } from '@backendkit-labs/result';\n\n@Injectable()\nexport class UserService {\n  @AsResult('user.find')\n  async findOne(id: string): Promise<User> {\n    return this.db.users.findOrThrow(id); // throws → becomes fail()\n  }\n}\n\n// In the controller\nconst result = await this.userService.findOne(id);\n// Result<User, Error>\nif (isOk(result)) {\n  return result.value;\n}\n```\n\n### `@WithMetrics(options?)` — wrap method in `track()`\n\nLike `@AsResult()` but returns a `RichResult` with timing and metadata.\n\n```typescript\nimport { WithMetrics } from '@backendkit-labs/result/nestjs';\nimport { isOk } from '@backendkit-labs/result';\n\n@Injectable()\nexport class PaymentService {\n  @WithMetrics({ operation: 'payment.charge', tags: ['stripe'] })\n  async charge(dto: ChargeDto): Promise<Payment> {\n    return this.stripeClient.charges.create({\n      amount:   dto.amount,\n      currency: dto.currency,\n    });\n  }\n}\n\n// In the controller\nconst result = await this.paymentService.charge(dto);\n// RichResult<Payment, Error>\n\nlogger.info('Charge result', {\n  ok:         result.ok,\n  operation:  result.operation,   // 'payment.charge'\n  durationMs: result.durationMs,  // e.g. 340\n  tags:       result.tags,        // ['stripe']\n});\n```\n\n### `ResultInterceptor` — HTTP response normalization\n\nAutomatically converts `Result` and `RichResult` return values from controller methods into a consistent JSON response shape.\n\n```typescript\nimport { ResultInterceptor } from '@backendkit-labs/result/nestjs';\n\n// Global — applies to every controller\napp.useGlobalInterceptors(app.get(ResultInterceptor));\n\n// Or per-controller / per-route\n@UseInterceptors(ResultInterceptor)\n@Controller('users')\nexport class UsersController { ... }\n```\n\n**Response shape for plain `Result`:**\n\n```json\n// Ok\n{ \"ok\": true, \"data\": { \"id\": 1, \"name\": \"Alice\" } }\n\n// Fail\n{ \"ok\": false, \"error\": \"User not found\" }\n```\n\n**Response shape for `RichResult`:**\n\n```json\n// Ok\n{\n  \"ok\": true,\n  \"data\": { \"id\": 1, \"name\": \"Alice\" },\n  \"meta\": {\n    \"operation\":     \"user.find\",\n    \"durationMs\":    12,\n    \"timestamp\":     \"2026-05-13T20:00:00.000Z\",\n    \"correlationId\": \"req-abc-123\",\n    \"tags\":          [\"db\", \"users\"]\n  }\n}\n\n// Fail\n{\n  \"ok\": false,\n  \"error\": \"User not found\",\n  \"meta\": {\n    \"operation\":  \"user.find\",\n    \"durationMs\": 3,\n    \"timestamp\":  \"2026-05-13T20:00:00.001Z\"\n  }\n}\n```\n\nNon-Result return values (plain objects, arrays, primitives) pass through unchanged.\n\n### Full NestJS controller example\n\n```typescript\nimport { Controller, Get, Post, Param, Body, UseInterceptors } from '@nestjs/common';\nimport { ResultInterceptor } from '@backendkit-labs/result/nestjs';\nimport { ok, fail, run, match, isOk } from '@backendkit-labs/result';\n\n@UseInterceptors(ResultInterceptor)\n@Controller('payments')\nexport class PaymentsController {\n  constructor(private readonly paymentService: PaymentService) {}\n\n  @Post()\n  async charge(@Body() dto: ChargeDto) {\n    // RichResult normalized automatically by ResultInterceptor\n    return this.paymentService.charge(dto);\n  }\n\n  @Get(':id')\n  async findOne(@Param('id') id: string) {\n    const result = await this.paymentService.findOne(id);\n\n    // Handle 404 before returning — interceptor normalizes the rest\n    return match(result, {\n      ok:   (payment) => ok(payment),\n      fail: (error)   => error.code === 'NOT_FOUND'\n        ? fail(`Payment ${id} not found`)\n        : fail('Internal error'),\n    });\n  }\n}\n```\n\n---\n\n## Architecture\n\n```\n@backendkit-labs/result                (core — zero runtime dependencies)\n  Result<T, E>                         discriminated union, fully generic error type\n  RichResult<T, E>                     Result + durationMs, timestamp, operation, tags\n  ok() / fail()                        constructors\n  fromThrowable() / fromPromise()      exception capture\n  fromNullable()                       null/undefined coercion\n  isOk() / isFail() / isRich()         type guards\n  map() / mapError() / flatMap()       transformations\n  match() / fold()                     pattern matching\n  tap() / tapError()                   side effects\n  unwrap() / unwrapOr() / expect()     unwrapping\n  toPromise() / toNullable()           conversion\n  run() / track()                      async execution with error capture\n  enrich() / simplify()                RichResult promotion / demotion\n  retry() / retryWithBackoff()         resilience — retries\n  withTimeout()                        resilience — deadline enforcement\n  all() / any() / parallel()           combinators — multiple results\n  partition() / collect() / traverse() combinators — array operations\n  combine2() / combine3()              combinators — typed tuples\n  Flow<T, E>                           fluent pipeline builder\n\n@backendkit-labs/result/nestjs         (optional NestJS layer)\n  @AsResult()                          method decorator → run()\n  @WithMetrics()                       method decorator → track()\n  ResultInterceptor                    HTTP response normalization\n  ResultModule                         NestJS module\n```\n\nThe core is a pure TypeScript library with no runtime dependencies. The NestJS layer lives in a separate subpath export (`/nestjs`) and is tree-shaken from the core bundle.\n\n---\n\n## License\n\nApache-2.0 — [BackendKit Labs](https://github.com/BackendKit-labs)\n","readmeFilename":"README.md"}