Skip to content

Commit af474ae

Browse files
author
Nebutra Mirror Bot
committed
chore: sync from Nebutra-Sailor@07a39e3298e2a86074e44ed066be682adf468517
0 parents  commit af474ae

12 files changed

Lines changed: 807 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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+
timeout-minutes: 15
15+
steps:
16+
- uses: actions/checkout@v4
17+
- uses: pnpm/action-setup@v4
18+
with:
19+
version: 10.32.1
20+
- uses: actions/setup-node@v4
21+
with:
22+
node-version: 22
23+
# No pnpm-lock.yaml in standalone mirrors — caching requires a lockfile.
24+
- name: Install
25+
run: pnpm install --no-frozen-lockfile --ignore-scripts
26+
- name: Build
27+
run: |
28+
if jq -e '.scripts.build // empty' package.json >/dev/null; then
29+
pnpm run build
30+
else
31+
echo 'no build script'
32+
fi
33+
- name: Typecheck
34+
# Hard gate when workspace deps are vendored or package is self-contained.
35+
run: |
36+
if jq -e '.scripts.typecheck // empty' package.json >/dev/null; then
37+
pnpm run typecheck
38+
else
39+
echo 'no typecheck script'
40+
fi
41+
- name: Test
42+
# Soft-fail: monorepo-only harnesses (extensionless node:test, etc.).
43+
# Install + build remain hard gates.
44+
continue-on-error: true
45+
run: |
46+
if jq -e '.scripts.test // empty' package.json >/dev/null; then
47+
pnpm run test
48+
else
49+
echo 'no test script'
50+
fi

.gitignore

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

AGENTS.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
- Run the package-local suite before changing error types, codes, or
64+
middleware: `pnpm --filter @nebutra/errors test` (`vitest run`). It covers
65+
the package's index (`src/index.test.ts`). Also verify the narrowest
66+
downstream consumer that exercises the changed error contract when behavior
67+
changes are non-trivial.

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# @nebutra/errors
2+
3+
## 0.1.2
4+
5+
### Patch Changes
6+
7+
- Ship the MIT LICENSE file these packages have always declared but never included.
8+
9+
Every one of these declares `"license": "MIT"` in its manifest, and npm shows
10+
that on the registry page — but the tarball carried no licence text at all.
11+
MIT's own terms require the notice to accompany "all copies or substantial
12+
portions of the Software", so a consumer vendoring one of these packages had
13+
nothing to comply with.
14+
15+
No code changes. This is the licence text only, published so the tarballs
16+
match what the manifests have been claiming.
17+
18+
`tests/architecture/release-surface.test.ts` now asserts the LICENSE _file_
19+
exists and is MIT, not just the manifest _field_ — the field-only check is how
20+
this went unnoticed, and is also how `create-sailor` shipped the full AGPL-3.0
21+
text under an MIT declaration for its entire published history.
22+
23+
## 0.1.1
24+
25+
### Patch Changes
26+
27+
- 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: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"name": "@nebutra/errors",
3+
"version": "0.1.2",
4+
"description": "Unified error handling and API error responses",
5+
"private": false,
6+
"license": "MIT",
7+
"type": "module",
8+
"main": "./dist/index.js",
9+
"types": "./dist/index.d.ts",
10+
"exports": {
11+
".": {
12+
"types": "./dist/index.d.ts",
13+
"import": "./dist/index.js",
14+
"default": "./dist/index.js"
15+
}
16+
},
17+
"files": [
18+
"dist"
19+
],
20+
"scripts": {
21+
"build": "tsc -p tsconfig.json",
22+
"test": "vitest run",
23+
"typecheck": "tsc -p tsconfig.json --noEmit"
24+
},
25+
"devDependencies": {
26+
"@types/node": "^22.19.15",
27+
"typescript": "^5.9.3",
28+
"vitest": "^4.1.4"
29+
},
30+
"homepage": "https://github.com/Nebutra/errors#readme",
31+
"repository": {
32+
"type": "git",
33+
"url": "git+https://github.com/Nebutra/errors.git"
34+
},
35+
"bugs": {
36+
"url": "https://github.com/Nebutra/errors/issues"
37+
},
38+
"publishConfig": {
39+
"access": "public"
40+
},
41+
"nebutraMirror": {
42+
"sourceRepository": "Nebutra/Nebutra-Sailor",
43+
"sourceDirectory": "packages/platform/errors",
44+
"sourcePackage": "@nebutra/errors",
45+
"sourceSha": "07a39e3298e2a86074e44ed066be682adf468517",
46+
"canonicalSource": "monorepo"
47+
},
48+
"packageManager": "pnpm@10.32.1"
49+
}

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)