{"_id":"@021.is/spine-errors","_rev":"2-dff664eae693b6df3ace56de1ad4076a","name":"@021.is/spine-errors","dist-tags":{"latest":"0.4.3"},"versions":{"0.4.0":{"name":"@021.is/spine-errors","version":"0.4.0","license":"MIT","_id":"@021.is/spine-errors@0.4.0","maintainers":[{"name":"edvone","email":"edvard@edvone.dev"}],"homepage":"https://github.com/021is/spine-ts#readme","bugs":{"url":"https://github.com/021is/spine-ts/issues"},"dist":{"shasum":"1db8ede2ae69570447c65fac9d28cc7149972c5e","tarball":"https://registry.npmjs.org/@021.is/spine-errors/-/spine-errors-0.4.0.tgz","fileCount":8,"integrity":"sha512-oALmbocccRO+uKs7rW5AA4K40z1k38U3CE997j4YSnNthI/AnNp0pKt5C3YFTsBHawIlBff0iKEJ4937gayzng==","signatures":[{"sig":"MEYCIQD7L3jBY88bo6d5aRt1qFugI1grP1mmIKVIjTfuhkLvygIhANpBdqBK34mjLA71x0Gx984AHQR/CSVQVrodGWvn7WEE","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":18980},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./next":{"types":"./dist/next.d.ts","import":"./dist/next.js"}},"gitHead":"e4434c6f59db8f586e0f05ffdb3b88a96e68e67b","scripts":{"build":"tsup src/index.ts src/next.ts --format esm --dts --clean --external next","typecheck":"tsc --noEmit"},"_npmUser":{"name":"edvone","email":"edvard@edvone.dev"},"repository":{"url":"git+https://github.com/021is/spine-ts.git","type":"git","directory":"packages/errors"},"_npmVersion":"10.9.4","description":"ResponseDto envelope + typed exception hierarchy + framework-agnostic handler. Modeled on a proven Kotlin shared-lib.","directories":{},"_nodeVersion":"22.22.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"next":"^16.0.0","react":"^19.0.0","react-dom":"^19.0.0"},"peerDependencies":{"next":">=15 <17"},"peerDependenciesMeta":{"next":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/spine-errors_0.4.0_1779981905993_0.009176689699451623","host":"s3://npm-registry-packages-npm-production"}},"0.4.3":{"name":"@021.is/spine-errors","version":"0.4.3","license":"MIT","description":"ResponseDto envelope + typed exception hierarchy + framework-agnostic handler. Modeled on a proven Kotlin shared-lib.","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./next":{"types":"./dist/next.d.ts","import":"./dist/next.js"}},"scripts":{"build":"tsup src/index.ts src/next.ts --format esm --dts --clean --external next","typecheck":"tsc --noEmit"},"peerDependencies":{"next":">=15 <17"},"peerDependenciesMeta":{"next":{"optional":true}},"devDependencies":{"next":"^16.0.0","react":"^19.0.0","react-dom":"^19.0.0"},"repository":{"type":"git","url":"git+https://github.com/021is/spine-ts.git","directory":"packages/errors"},"publishConfig":{"access":"public"},"_id":"@021.is/spine-errors@0.4.3","gitHead":"b9f9333776fa55686cc2feb05f851aa6000b0e05","bugs":{"url":"https://github.com/021is/spine-ts/issues"},"homepage":"https://github.com/021is/spine-ts#readme","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-CgvgzEZtSNBdwBq3NTsNq96EproYf7q5SvLNDkEsxz2VEw9hwq6OHrFj/rMOmwY5+ei5d6xNZGnimeluEWiNDQ==","shasum":"f54650326b94b27c1efc16535323eb6d3f80cb8d","tarball":"https://registry.npmjs.org/@021.is/spine-errors/-/spine-errors-0.4.3.tgz","fileCount":9,"unpackedSize":21511,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFUc5S5Taa5M+QeuUHuzSRxeBqeg0T+GmeGvNbYD8azdAiAaDLjBeJBrsmidwrY/ewh+ZnBWJtP2hu4QvQ1xE2LeZA=="}]},"_npmUser":{"name":"edvone","email":"edvard@edvone.dev"},"directories":{},"maintainers":[{"name":"edvone","email":"edvard@edvone.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/spine-errors_0.4.3_1779995187358_0.3989540400423688"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-28T15:25:05.481Z","modified":"2026-05-28T19:06:27.653Z","0.4.0":"2026-05-28T15:25:06.182Z","0.4.3":"2026-05-28T19:06:27.517Z"},"bugs":{"url":"https://github.com/021is/spine-ts/issues"},"license":"MIT","homepage":"https://github.com/021is/spine-ts#readme","repository":{"type":"git","url":"git+https://github.com/021is/spine-ts.git","directory":"packages/errors"},"description":"ResponseDto envelope + typed exception hierarchy + framework-agnostic handler. Modeled on a proven Kotlin shared-lib.","maintainers":[{"name":"edvone","email":"edvard@edvone.dev"}],"readme":"# @021.is/spine-errors\n\nThe canonical response envelope (`ResponseDto`) + typed exception hierarchy + framework-agnostic handler used by every product.\n\nModeled on a proven Kotlin shared-lib. Same contract; idiomatic TypeScript.\n\n## Why mandatory\n\nEvery HTTP endpoint and server action returns a `ResponseDto<T>`. No exceptions, no \"this small route doesn't need it\". The client and the next service both rely on the envelope's `success` / `code` / `errorMessage` / `errorKey` / `errorParams` / `timestamp` / `requestId` to render UI, branch logic, propagate traces, and localize errors.\n\nLocked. Reasoning: consistent client-side handling across every app; no per-route surprise shapes; first-class i18n via `errorKey`.\n\n## Install\n\n```bash\nbun add @021.is/spine-errors\n```\n\n## Use — domain code\n\n```ts\nimport { BadRequestException, NotFoundException, ForbiddenException } from \"@021.is/spine-errors\";\n\nexport async function publishEvent(eventId: string, userId: string) {\n  const event = await db.event.findUnique({ where: { id: eventId } });\n  if (!event) throw new NotFoundException(\"Event not found\", { translationKey: \"event.not_found\" });\n  if (event.organizerId !== userId) throw new ForbiddenException();\n  if (!event.poster) {\n    throw new BadRequestException(\"Event needs a poster before publishing\", {\n      translationKey: \"event.publish.no_poster\",\n    });\n  }\n  return db.event.update({ where: { id: eventId }, data: { status: \"PUBLISHED\" } });\n}\n```\n\n## Use — Next.js route handler\n\n```ts\nimport { withErrorHandling, ok } from \"@021.is/spine-errors/next\";\nimport { publishEvent } from \"@/server/events/publish\";\n\nexport const POST = withErrorHandling(async (req: Request) => {\n  const userId = await requireUser(req);\n  const { eventId } = await req.json();\n  const event = await publishEvent(eventId, userId);\n  return Response.json(ok(event, { successMessage: \"Event published\" }));\n});\n```\n\nAny thrown `*Exception` becomes the correct HTTP status + `ResponseDto`. Unknown errors → 500 + logged.\n\n## Use — Server action\n\n```ts\nimport { tryAction } from \"@021.is/spine-errors/next\";\nimport { publishEvent } from \"@/server/events/publish\";\n\nexport async function publishEventAction(formData: FormData) {\n  \"use server\";\n  return tryAction(async () => {\n    const userId = await requireUser();\n    return publishEvent(formData.get(\"eventId\") as string, userId);\n  });\n}\n```\n\nStatus code is in `response.code` (server actions don't have HTTP status).\n\n## Exception → HTTP status map\n\n| Exception | Status |\n|---|---|\n| `BadRequestException` | 400 |\n| `UnauthorizedException` | 401 |\n| `ForbiddenException` / `NotAllowedException` | 403 |\n| `NotFoundException` | 404 |\n| `ConflictException` | 409 |\n| `ExpectationFailedException` | 417 |\n| `RateLimitedException` | 429 (+ `Retry-After`) |\n| `SomethingWentWrongException` / anything else | 500 |\n\n## i18n\n\nEvery exception subclass accepts `translationKey` + `translationParams`. These flow into `ResponseDto.errorKey` + `errorParams`. The client's i18n runtime (`@021.is/spine-i18n`) reads them and renders the localized message, falling back to `errorMessage` when no key exists.\n\n## Testing\n\n```bash\nbun run test\n```\n\nReal unit tests, no mocks (this package has no I/O).\n","readmeFilename":"README.md"}