{"_id":"@birtalanrobert/http","_rev":"4-f7d952fb21e8a91ac94b1f2728207a7e","name":"@birtalanrobert/http","dist-tags":{"latest":"2.0.0"},"versions":{"0.1.0":{"name":"@birtalanrobert/http","version":"0.1.0","license":"AGPL-3.0-only","_id":"@birtalanrobert/http@0.1.0","maintainers":[{"name":"birtalanrobert","email":"birtalanrobert@gmail.com"}],"homepage":"https://github.com/birtalanrobert/mortar#readme","bugs":{"url":"https://github.com/birtalanrobert/mortar/issues"},"dist":{"shasum":"a59b73c76105e3a81b71fd70c8fdc92df9845635","tarball":"https://registry.npmjs.org/@birtalanrobert/http/-/http-0.1.0.tgz","fileCount":59,"integrity":"sha512-pLffMESMR3gVnLOMP5YCVMKmcnYD6eJ9QLUKISlcYy/usTO8lnUEYRZubmLPZ5dp0gQiBrjaHzOykpQH3U5PeQ==","signatures":[{"sig":"MEUCIGdRDY/vhArEDIiZAMz1xlXtEkJJbmiztQepxqUenU6zAiEA3SI7Er9gAAW72AgsaFx+xM7pQtU+uqwiBw3pXcZYfC0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180207},"main":"./dist/index.js","type":"commonjs","_from":"file:birtalanrobert-http-0.1.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"build":"tsc -p tsconfig.build.json","clean":"rm -rf dist *.tsbuildinfo","typecheck":"tsc -p tsconfig.json"},"_npmUser":{"name":"birtalanrobert","email":"birtalanrobert@gmail.com"},"_resolved":"/private/var/folders/zx/7dcyg3mn6kjfyzsymzgpx1jr0000gn/T/871e395e3093018759d9836f7c7e559f/birtalanrobert-http-0.1.0.tgz","_integrity":"sha512-pLffMESMR3gVnLOMP5YCVMKmcnYD6eJ9QLUKISlcYy/usTO8lnUEYRZubmLPZ5dp0gQiBrjaHzOykpQH3U5PeQ==","repository":{"url":"git+https://github.com/birtalanrobert/mortar.git","type":"git","directory":"packages/http"},"_npmVersion":"11.13.0","description":"Error taxonomy, problem-details, request context and health checks","directories":{},"_nodeVersion":"24.16.0","dependencies":{"@birtalanrobert/context":"0.1.0","@birtalanrobert/observability":"0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","class-validator":"^0.14.0"},"peerDependenciesMeta":{"rxjs":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true},"class-validator":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/http_0.1.0_1787664413482_0.378417665997836","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@birtalanrobert/http","version":"0.2.0","license":"AGPL-3.0-only","_id":"@birtalanrobert/http@0.2.0","maintainers":[{"name":"birtalanrobert","email":"birtalanrobert@gmail.com"}],"homepage":"https://github.com/birtalanrobert/mortar#readme","bugs":{"url":"https://github.com/birtalanrobert/mortar/issues"},"dist":{"shasum":"f7941bfb63f27613488988dd6566a307eb1da010","tarball":"https://registry.npmjs.org/@birtalanrobert/http/-/http-0.2.0.tgz","fileCount":60,"integrity":"sha512-WwGUjwuptA0KAyY1EEJNdFIhtdE2OXR6MX7JHpAU6wFiC4m5TJuL0bpanl9acXxWW3LWoMAd9E8RPzoXXfqAWQ==","signatures":[{"sig":"MEQCICr4J4bHs5136WEaM4Wf6Rhjxt8TWrhBK/X189FjHN/bAiA7FLnDPWuaVD74Ah1ZFqQF+BzuYBmhkiiAPX7eN7XpwA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":189418},"main":"./dist/index.js","type":"commonjs","_from":"file:birtalanrobert-http-0.2.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"build":"tsc -p tsconfig.build.json","clean":"rm -rf dist *.tsbuildinfo","typecheck":"tsc -p tsconfig.json"},"_npmUser":{"name":"birtalanrobert","email":"birtalanrobert@gmail.com"},"_resolved":"/private/var/folders/zx/7dcyg3mn6kjfyzsymzgpx1jr0000gn/T/df0588189ed89cb127d1dbf8a1661b7a/birtalanrobert-http-0.2.0.tgz","_integrity":"sha512-WwGUjwuptA0KAyY1EEJNdFIhtdE2OXR6MX7JHpAU6wFiC4m5TJuL0bpanl9acXxWW3LWoMAd9E8RPzoXXfqAWQ==","repository":{"url":"git+https://github.com/birtalanrobert/mortar.git","type":"git","directory":"packages/http"},"_npmVersion":"11.13.0","description":"Error taxonomy, problem-details, request context and health checks","directories":{},"_nodeVersion":"24.16.0","dependencies":{"@birtalanrobert/context":"^0.2.0","@birtalanrobert/observability":"^0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","class-validator":"^0.14.0 || ^0.15.0"},"peerDependenciesMeta":{"rxjs":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true},"class-validator":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/http_0.2.0_1787732723815_0.28698916905021643","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@birtalanrobert/http","version":"1.0.0","license":"AGPL-3.0-only","_id":"@birtalanrobert/http@1.0.0","maintainers":[{"name":"birtalanrobert","email":"birtalanrobert@gmail.com"}],"homepage":"https://github.com/birtalanrobert/mortar#readme","bugs":{"url":"https://github.com/birtalanrobert/mortar/issues"},"dist":{"shasum":"c1f4872780bafb5aa70528468afe3d29f11d200b","tarball":"https://registry.npmjs.org/@birtalanrobert/http/-/http-1.0.0.tgz","fileCount":65,"integrity":"sha512-BqeuVN1soQYYKRYMezIPmTDjrmLKqHbBrk6ksyvbPMhJVDKefKQTiTrdc18rkr02pF3PWUOAoX2rdkpjE9JxQA==","signatures":[{"sig":"MEYCIQDW8e/Dt0f69qz76iMWQRg1chjjORpJ2eLPi+Nv1F5DuQIhAKqweA3/0E0+bq+MJUh/ui+1fxZi/t8VjP1T/FYvajOG","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":196882},"main":"./dist/index.js","type":"commonjs","_from":"file:birtalanrobert-http-1.0.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"build":"tsc -p tsconfig.build.json","clean":"rm -rf dist *.tsbuildinfo","typecheck":"tsc -p tsconfig.json"},"_npmUser":{"name":"birtalanrobert","email":"birtalanrobert@gmail.com"},"_resolved":"/private/var/folders/zx/7dcyg3mn6kjfyzsymzgpx1jr0000gn/T/98230f8a6320b604be6bd784f1aca346/birtalanrobert-http-1.0.0.tgz","_integrity":"sha512-BqeuVN1soQYYKRYMezIPmTDjrmLKqHbBrk6ksyvbPMhJVDKefKQTiTrdc18rkr02pF3PWUOAoX2rdkpjE9JxQA==","repository":{"url":"git+https://github.com/birtalanrobert/mortar.git","type":"git","directory":"packages/http"},"_npmVersion":"11.13.0","description":"Error taxonomy, problem-details, request context and health checks","directories":{},"_nodeVersion":"24.16.0","dependencies":{"@birtalanrobert/context":"^1.0.0","@birtalanrobert/observability":"^1.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","class-validator":"^0.14.0 || ^0.15.0"},"peerDependenciesMeta":{"rxjs":{"optional":true},"@nestjs/core":{"optional":true},"@nestjs/common":{"optional":true},"class-validator":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/http_1.0.0_1787735903815_0.3325856784605301","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@birtalanrobert/http","version":"2.0.0","description":"Error taxonomy, problem-details, request context and health checks","license":"AGPL-3.0-only","type":"commonjs","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","default":"./dist/nestjs/index.js"},"./package.json":"./package.json"},"publishConfig":{"access":"public"},"dependencies":{"@birtalanrobert/context":"^1.0.0","@birtalanrobert/observability":"^1.1.0"},"peerDependencies":{"@nestjs/common":"^10.0.0 || ^11.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","rxjs":"^7.0.0","class-validator":"^0.14.0 || ^0.15.0"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true},"@nestjs/core":{"optional":true},"rxjs":{"optional":true},"class-validator":{"optional":true}},"repository":{"type":"git","url":"git+https://github.com/birtalanrobert/mortar.git","directory":"packages/http"},"mortar":{"entries":["nestjs"]},"scripts":{"build":"tsc -p tsconfig.build.json","clean":"rm -rf dist *.tsbuildinfo","typecheck":"tsc -p tsconfig.json"},"_id":"@birtalanrobert/http@2.0.0","bugs":{"url":"https://github.com/birtalanrobert/mortar/issues"},"homepage":"https://github.com/birtalanrobert/mortar#readme","_integrity":"sha512-DZuuP/TqeVyave/Mv2T5LCgvbtNRU6YfFdTD9gDywAIp6mw8gkuoN//qHYEaBn8MYDpmsjgDfYAqQMPO9yKZGQ==","_resolved":"/private/var/folders/zx/7dcyg3mn6kjfyzsymzgpx1jr0000gn/T/e755555ce2000b06bc8f7143c1fcf6e8/birtalanrobert-http-2.0.0.tgz","_from":"file:birtalanrobert-http-2.0.0.tgz","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-DZuuP/TqeVyave/Mv2T5LCgvbtNRU6YfFdTD9gDywAIp6mw8gkuoN//qHYEaBn8MYDpmsjgDfYAqQMPO9yKZGQ==","shasum":"466970e466f7e0aea2d35045f749fe3b976403d5","tarball":"https://registry.npmjs.org/@birtalanrobert/http/-/http-2.0.0.tgz","fileCount":76,"unpackedSize":214594,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDWjLhXP6HWAHUFrm8i9kU0pBGXHOLQMAkk/eFpi614aAiBKCy7OnOKIIOirJR3d28xJUa/227/qiKYwrfdlrRD39g=="}]},"_npmUser":{"name":"birtalanrobert","email":"birtalanrobert@gmail.com"},"directories":{},"maintainers":[{"name":"birtalanrobert","email":"birtalanrobert@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/http_2.0.0_1787818879650_0.21980274493624896"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-25T13:26:53.289Z","modified":"2026-08-27T08:21:20.008Z","0.1.0":"2026-08-25T13:26:53.640Z","0.2.0":"2026-08-26T08:25:23.956Z","1.0.0":"2026-08-26T09:18:23.938Z","2.0.0":"2026-08-27T08:21:19.777Z"},"bugs":{"url":"https://github.com/birtalanrobert/mortar/issues"},"license":"AGPL-3.0-only","homepage":"https://github.com/birtalanrobert/mortar#readme","repository":{"type":"git","url":"git+https://github.com/birtalanrobert/mortar.git","directory":"packages/http"},"description":"Error taxonomy, problem-details, request context and health checks","maintainers":[{"name":"birtalanrobert","email":"birtalanrobert@gmail.com"}],"readme":"# @birtalanrobert/http\n\nError taxonomy, RFC 9457 Problem Details, request context and health checks.\n\n## Two entry points\n\n**`@birtalanrobert/http`** is framework-free. Error classes, problem\nserialisation, header names, locale negotiation and the health registry —\nnothing here imports `@nestjs/common`, so a Next.js route handler, an edge\nfunction or a job runner raises the same errors and produces the same problem\ndocuments as the API without installing a framework to do it.\n\n**`@birtalanrobert/http/nestjs`** is the wiring: the module, the exception\nfilter, the context middleware, the validation pipe, the health controller and\n`@PublicRoute()`.\n\n> **Moved in 2.0.0.** These were previously exported from the root, which meant\n> importing `NotFoundError` pulled NestJS into an edge bundle. Anything in the\n> second list now comes from `@birtalanrobert/http/nestjs` instead of\n> `@birtalanrobert/http`; the error classes stay exactly where they were, so\n> most files need no change at all.\n\n## The error taxonomy is framework-free\n\n`MortarError` and its subclasses import nothing from NestJS. Domain modules,\nworkers, CLI tools and tests all throw them; only the HTTP layer knows how to\nturn one into a response. A domain module that had to import `@nestjs/common`\nto say \"not found\" would be coupled to a web framework for no reason.\n\n```ts\nimport { NotFoundError, ConflictError } from '@birtalanrobert/http';\n\nthrow new NotFoundError('Booking', bookingId);\nthrow new ConflictError('That seat has already been sold.');\n```\n\n## One response shape for every failure\n\nEvery error — thrown by application code, by the framework, or by accident —\ncomes out as the same problem document:\n\n```json\n{\n  \"type\": \"https://problems.mortar.dev/conflict\",\n  \"title\": \"Conflict\",\n  \"status\": 409,\n  \"code\": \"conflict\",\n  \"detail\": \"That seat has already been sold.\",\n  \"instance\": \"/events/42/seats\",\n  \"requestId\": \"b1f0…\"\n}\n```\n\nClients branch on **`code`**, not on `title` (prose, may be reworded or\ntranslated) and not on `status` (too coarse to distinguish \"seat already sold\"\nfrom \"booking window closed\").\n\n## Two guarantees the filter never breaks\n\n1. **The response is always a valid problem document** — whatever was thrown.\n   An error handler that can itself fail is not an error handler.\n2. **A 5xx never carries internal detail.** An unexpected error's message\n   routinely contains a SQL fragment, a file path or a connection string. In\n   production the client gets a generic message and a `requestId`; the detail\n   lives in the logs.\n\n## Logging levels are deliberate\n\n- **5xx** → `error`, with the exception attached. It is ours.\n- **`cross_tenant_access`** → `warn`. In a multi-tenant system this is either a\n  serious bug or an attack, and somebody should find out today.\n- **Other 4xx** → `debug`. A wall of 404 warnings trains everyone to ignore\n  warnings, which is how a real one gets missed.\n\n## Liveness and readiness are not the same endpoint\n\n`/health/live` answers _is this process running_ and touches nothing. A\nliveness probe that checks the database restarts the service every time the\ndatabase hiccups — turning a brief blip into a restart loop across every\nreplica at once.\n\n`/health/ready` answers _can this process serve traffic_ and does check\ndependencies. Non-critical indicators report `degraded` rather than `down`, so\nan unreachable metrics sink does not remove the instance from rotation.\n\nPer-indicator timeouts are enforced by the registry, not trusted to the\nindicator: the failure this endpoint most needs to survive is a dependency that\nhangs, not one that errors.\n\n## Using it in a NestJS application\n\n```ts\nimport { HttpModule, createValidationPipe } from '@birtalanrobert/http/nestjs';\n\n@Module({\n  imports: [\n    ConfigModule.forRoot({ schema: envSchema }),\n    LoggerModule.forRootAsync({/* … */}),\n    DatabaseModule.forRootAsync({/* … */}),\n\n    HttpModule.forRootAsync({\n      inject: [ConfigModule.token()],\n      useFactory: (config: AppConfig) => ({\n        context: {\n          // Only behind a proxy that overwrites the header. Trusting it while\n          // directly exposed lets any client claim any address, which defeats\n          // both rate limiting and the audit trail.\n          trustProxy: config.NODE_ENV === 'production',\n          supportedLocales: ['ro', 'hu', 'en'],\n        },\n        errors: { baseUri: config.PROBLEM_BASE_URI },\n        health: { detailed: config.HEALTH_DETAILED },\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n`@Global()`. Register it **early** — before anything that raises an error worth\nserialising, and before any module whose middleware expects a request context to\nalready be open. It applies `ContextMiddleware` and registers\n`MortarExceptionFilter` itself; neither needs adding to `APP_FILTER` by hand.\n\nIn `bootstrap.ts`:\n\n```ts\napp.useGlobalPipes(createValidationPipe());\n```\n\nThe pipe turns class-validator failures into the same problem document as\neverything else — without it, a validation failure from the framework looks\ncompletely different from one raised by application code, and every client needs\ntwo error handlers.\n\n### Health indicators\n\n```ts\nimport { HEALTH_REGISTRY } from '@birtalanrobert/http/nestjs';\nimport { HealthRegistry, createIndicator } from '@birtalanrobert/http';\n\nconstructor(@Inject(HEALTH_REGISTRY) registry: HealthRegistry) {\n  registry.add(createIndicator('database', () => dataSource.query('SELECT 1')));\n}\n```\n\n`/health/live` says the process is up; `/health/ready` says it can serve. They\nare different endpoints because a failing readiness check should stop traffic,\nwhile a failing liveness check restarts the pod — and answering one with the\nother produces a restart loop under load.\n\n## Using it outside NestJS\n\n```ts\nimport { NotFoundError, toProblemDetails, negotiateLocale } from '@birtalanrobert/http';\n\ntry {\n  // …\n} catch (error) {\n  const problem = toProblemDetails(error, { baseUri, requestId });\n  return Response.json(problem, { status: problem.status });\n}\n```\n\n`toProblemDetails` is total: a `MortarError`, a Nest `HttpException`, a plain\n`Error` or a thrown string all become the same document.\n","readmeFilename":"README.md"}