Skip to content

Commit cbcad0a

Browse files
author
Nebutra Mirror Bot
committed
chore: sync from Nebutra-Sailor@c519685367963c04db5d05a5e2893faa682920ec
0 parents  commit cbcad0a

12 files changed

Lines changed: 748 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [main]
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
package:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
16+
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1
17+
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
18+
with:
19+
node-version: 22
20+
cache: pnpm
21+
- run: pnpm install --no-frozen-lockfile --ignore-scripts
22+
- name: Build
23+
run: node -e "process.exit(require('./package.json').scripts?.build ? 0 : 1)" && pnpm build || echo 'no build script'
24+
- name: Typecheck
25+
run: node -e "process.exit(require('./package.json').scripts?.typecheck ? 0 : 1)" && pnpm typecheck || echo 'no typecheck script'
26+
- name: Test
27+
run: node -e "process.exit(require('./package.json').scripts?.test ? 0 : 1)" && pnpm test || echo 'no test script'

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
dist
3+
coverage
4+
.turbo
5+
.DS_Store

AGENTS.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# AGENTS.md — packages/errors
2+
3+
Execution contract for Nebutra's shared error semantics package.
4+
5+
## Scope
6+
7+
Applies to everything under `packages/platform/errors/`.
8+
9+
This package owns canonical application error codes, typed error classes,
10+
API-safe serialization helpers, and the framework-agnostic error middleware
11+
shape. It is the shared error vocabulary layer, not the place for service-local
12+
logging policy or provider-specific exception translation.
13+
14+
## Source Of Truth
15+
16+
- Public package surface: `package.json`, `src/index.ts`
17+
- Canonical error code catalog: `ERROR_CODES`
18+
- Base error class and serialization behavior: `AppError`, `AppErrorOptions`,
19+
`AppError.toJSON()`
20+
- Specific error subclasses and their default status semantics:
21+
`ValidationError`, `UnauthorizedError`, `ForbiddenError`, `NotFoundError`,
22+
`ConflictError`, `RateLimitError`, `QuotaExceededError`,
23+
`ExternalServiceError`, `DatabaseError`
24+
- API response and status helpers: `toApiError`, `getStatusCode`
25+
- Framework boundary for request-safe error responses: `errorHandler`
26+
- Assertion and wrapper helpers: `tryCatch`, `assert`, `assertFound`
27+
28+
Treat `README.md` as descriptive only. If docs drift, update `src/index.ts`
29+
instead of preserving outdated examples.
30+
31+
## Contract Boundaries
32+
33+
- Keep `ERROR_CODES` as the canonical shared vocabulary. Additive changes are
34+
safest; renames or removals are compatibility changes for callers, handlers,
35+
and logs.
36+
- Treat `AppError` and `ApiErrorResponse` as the stable serialization boundary.
37+
Do not leak raw unknown errors or provider-specific details through
38+
`toApiError`.
39+
- Preserve default status-code mapping in `getDefaultStatusCode()` unless the
40+
compatibility change is deliberate and coordinated with consumers.
41+
- Keep `errorHandler()` framework-agnostic and request-safe. Its job is to map
42+
errors into structured JSON and invoke the optional `onError` callback, not
43+
to own logging destinations or transport-specific side effects.
44+
- Preserve the distinction between operational and non-operational errors.
45+
`DatabaseError` and other server faults should not be quietly normalized into
46+
benign client semantics.
47+
- Keep assertion helpers thin wrappers over the shared error types. Do not
48+
embed app-specific policy or database lookups here.
49+
50+
## Generated And Derived Files
51+
52+
- This package currently exports source directly and has no checked-in
53+
generated source of truth.
54+
- Do not hand-edit future build output, coverage artifacts, or transient
55+
TypeScript output.
56+
- If packaging changes later, update the source files above rather than derived
57+
artifacts.
58+
59+
## Validation
60+
61+
- Error type, code, or middleware changes:
62+
`pnpm --filter @nebutra/errors exec tsc --noEmit`
63+
- Because this package currently has no package-local tests, verify the
64+
narrowest downstream consumer that exercises the changed error contract when
65+
behavior changes are non-trivial.

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# @nebutra/errors
2+
3+
## 0.1.1
4+
5+
### Patch Changes
6+
7+
- Publish registry package metadata under the MIT license.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Nebutra
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

NEBUTRA_SUBREPO.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Nebutra Subrepo Mirror
2+
3+
This repository is generated from `Nebutra/Nebutra-Sailor`.
4+
5+
| Field | Value |
6+
|---|---|
7+
| Package | `@nebutra/errors` |
8+
| Source directory | `packages/platform/errors` |
9+
| Mirror repo | `Nebutra/errors` |
10+
| Cohort | `first-wave` |
11+
12+
Do not treat this mirror as an independent source of truth. Release versions, package metadata, and dependency governance are maintained in the monorepo.

README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# @nebutra/errors
2+
3+
Public mirror for [@nebutra/errors](https://www.npmjs.com/package/%40nebutra%2Ferrors) from [Nebutra/Nebutra-Sailor](https://github.com/Nebutra/Nebutra-Sailor/tree/main/packages/platform/errors).
4+
5+
This repository is generated from the Nebutra Sailor monorepo. Package releases are cut from the monorepo and mirrored here for discovery, standalone cloning, and contribution intake.
6+
7+
- Canonical source: `packages/platform/errors` in `Nebutra/Nebutra-Sailor`
8+
- Package registry: npm and GitHub Packages
9+
- Contributions: open issues or PRs here; maintainers port accepted changes back into the monorepo source package
10+
11+
---
12+
Standardized error handling.
13+
14+
## Installation
15+
16+
```bash
17+
pnpm add @nebutra/errors
18+
```
19+
20+
## Features
21+
22+
- **Error Classes** — Typed error hierarchy
23+
- **HTTP Mapping** — Automatic status code mapping
24+
- **Error Codes** — Machine-readable error identifiers
25+
- **Sentry Integration** — Automatic error reporting
26+
27+
## Usage
28+
29+
### Throw Errors
30+
31+
```typescript
32+
import {
33+
NotFoundError,
34+
UnauthorizedError,
35+
ValidationError,
36+
RateLimitError,
37+
} from "@nebutra/errors";
38+
39+
throw new NotFoundError("User not found", { userId: "123" });
40+
throw new UnauthorizedError("Invalid token");
41+
throw new ValidationError("Email is required", { field: "email" });
42+
throw new RateLimitError("Too many requests", { retryAfter: 60 });
43+
```
44+
45+
### Error Classes
46+
47+
| Class | HTTP Status | Code |
48+
| ------------------- | ----------- | ------------------ |
49+
| `BadRequestError` | 400 | `BAD_REQUEST` |
50+
| `UnauthorizedError` | 401 | `UNAUTHORIZED` |
51+
| `ForbiddenError` | 403 | `FORBIDDEN` |
52+
| `NotFoundError` | 404 | `NOT_FOUND` |
53+
| `ConflictError` | 409 | `CONFLICT` |
54+
| `ValidationError` | 422 | `VALIDATION_ERROR` |
55+
| `RateLimitError` | 429 | `RATE_LIMITED` |
56+
| `InternalError` | 500 | `INTERNAL_ERROR` |
57+
58+
### Error Handler Middleware
59+
60+
```typescript
61+
import { errorHandler } from "@nebutra/errors";
62+
63+
// Express/Hono
64+
app.use(errorHandler());
65+
66+
// Response format:
67+
// {
68+
// "error": {
69+
// "code": "NOT_FOUND",
70+
// "message": "User not found",
71+
// "details": { "userId": "123" }
72+
// }
73+
// }
74+
```
75+
76+
### Type Guard
77+
78+
```typescript
79+
import { isAppError, AppError } from "@nebutra/errors";
80+
81+
try {
82+
await riskyOperation();
83+
} catch (error) {
84+
if (isAppError(error)) {
85+
// Known error with code and status
86+
return handleKnownError(error.code, error.statusCode);
87+
} else {
88+
// Unknown error
89+
throw new InternalError("Unexpected error");
90+
}
91+
}
92+
```
93+
94+
### Sentry Integration
95+
96+
```typescript
97+
import { captureError } from "@nebutra/errors";
98+
99+
try {
100+
await operation();
101+
} catch (error) {
102+
captureError(error, {
103+
user: { id: userId },
104+
tags: { feature: "checkout" },
105+
});
106+
}
107+
```
108+
109+
## Related
110+
111+
- [API Gateway](../../../backends/gateway/)
112+
- [Observability](../../../infra/ops/observability/)

package.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "@nebutra/errors",
3+
"version": "0.1.1",
4+
"description": "Unified error handling and API error responses",
5+
"private": false,
6+
"license": "MIT",
7+
"type": "module",
8+
"main": "./src/index.ts",
9+
"types": "./src/index.ts",
10+
"exports": {
11+
".": "./src/index.ts"
12+
},
13+
"scripts": {
14+
"test": "vitest run",
15+
"typecheck": "tsc --noEmit"
16+
},
17+
"devDependencies": {
18+
"@types/node": "^22.19.15",
19+
"typescript": "^5.9.3",
20+
"vitest": "^4.1.4"
21+
},
22+
"homepage": "https://github.com/Nebutra/errors#readme",
23+
"repository": {
24+
"type": "git",
25+
"url": "git+https://github.com/Nebutra/errors.git"
26+
},
27+
"bugs": {
28+
"url": "https://github.com/Nebutra/errors/issues"
29+
},
30+
"publishConfig": {
31+
"access": "public"
32+
},
33+
"nebutraMirror": {
34+
"sourceRepository": "Nebutra/Nebutra-Sailor",
35+
"sourceDirectory": "packages/platform/errors",
36+
"sourcePackage": "@nebutra/errors",
37+
"sourceSha": "c519685367963c04db5d05a5e2893faa682920ec",
38+
"canonicalSource": "monorepo"
39+
}
40+
}

src/index.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { describe, expect, it } from "vitest";
2+
import { CapabilityError, toApiError } from "./index";
3+
4+
describe("CapabilityError", () => {
5+
it("serializes a suggestion for API callers", () => {
6+
const error = new CapabilityError("knowledge-rag", "Missing local model", {
7+
suggestion: "Run knowledge-rag:doctor and install a local model.",
8+
metadata: { provider: "ollama" },
9+
});
10+
11+
expect(error.capability).toBe("knowledge-rag");
12+
expect(error.suggestion).toContain("knowledge-rag:doctor");
13+
expect(error.toJSON()).toMatchObject({
14+
code: "EXTERNAL_SERVICE_ERROR",
15+
suggestion: "Run knowledge-rag:doctor and install a local model.",
16+
metadata: { capability: "knowledge-rag", provider: "ollama" },
17+
});
18+
expect(toApiError(error).error.details).toMatchObject({
19+
suggestion: "Run knowledge-rag:doctor and install a local model.",
20+
});
21+
});
22+
});

0 commit comments

Comments
 (0)