Lightweight, type-safe error handling for TypeScript — Python-style. Function-based API, exception chaining, hierarchical inheritance, and rich error semantics. ESM-only, designed for first-class interoperability with @deessejs/fp's Result and Try.
Sibling projects: @deessejs/fp provides
ResultandTrytypes that integrate natively with @deessejs/errors error factories. Install them together to get a complete error-handling story without glue code.
| Layer | What you get | Why it matters |
|---|---|---|
error() |
Define error factories with name, message templates, fields, and inheritance. | Python-style error definitions without classes. |
.from() chaining |
Cause chain via .from() + causes() traversal. |
Link errors together while preserving the full chain context. |
| Single or multiple inheritance | inherits: accepts a factory or an array. |
Organize error hierarchies that match your domain. |
is() type checking |
Runtime and type-safe classifier with inheritance support. | Discriminate errors without brittle instanceof. |
.addNote() |
Attach runtime context to error instances. | Python 3.11-style notes (PEP 678) for trail-of-breadcrumbs debugging. |
| Message templates | {field} placeholders with :upper, :lower, :json modifiers. |
Readable messages composed from structured fields at construction time. |
| Standard Schema fields | Accepts Zod / Valibot / ArkType schemas. | Validated structured data on every error instance, no hand-rolled guards. |
raise() |
Idiomatic throw helper. | Type-narrowed (never) raise(err) for control-flow readability. |
| @deessejs/fp integration | Result/Try accept ErrorInstance directly. |
Type-safe error pipelines end-to-end, no string-error footguns. |
- Simple by default. No class hierarchies to manage, no decorators under reflection. Just factory functions and chained calls.
- ESM-only. Modern packaging, no CJS shim, no
module/mainduplication. - Minimal runtime. The only runtime dependency is
@standard-schema/spec. - TypeScript first-class. Strict types, no
anyleakages, full inference. JSDoc on every public symbol. - Real testing. Vitest with type-level and runtime tests, including cause-chain traversal.
- Node.js 22.x for consumers (the package emits ESM)
- pnpm 10+ for development (
corepack enableif not installed) - TypeScript 5.x for consumers (
dist/*.d.tsis published)
npm install @deessejs/errors@deessejs/fp is optional - install it if you want to compose Result/Try types around ErrorInstance.
import { error, raise, is, causes } from '@deessejs/errors';
// Define an error factory with a templated message
const ValidationError = error({
name: 'ValidationError',
message: 'Field "{field}" is invalid: {reason}',
});
// Construct a typed error
const err = ValidationError({ field: 'email', reason: 'invalid format' });
// err.message === 'Field "email" is invalid: invalid format'
// Chain a cause
const cause = error({ name: 'NetworkError' })();
err.from(cause);
// Throw it
raise(err);
// Later, type-check and walk the chain
is(err, ValidationError); // true
causes(err); // [cause]| Runtime | Minimum version |
|---|---|
| Node.js | 22.0.0 |
| pnpm | 10 (for development) |
| TypeScript | 5.x |
ESM-only. Consumers using a CJS resolver need to use dynamic import() or migrate to ESM.
| Command | What it does |
|---|---|
pnpm --filter @deessejs/errors build |
Build dist/ (tsc -p tsconfig.build.json) |
pnpm --filter @deessejs/errors test |
Run vitest in watch mode |
pnpm --filter @deessejs/errors test:run |
Run vitest once |
pnpm --filter @deessejs/errors type-check |
tsc --noEmit |
pnpm --filter @deessejs/errors lint |
Run ESLint |
| Command | What it does |
|---|---|
pnpm build |
Build via Turborepo |
pnpm test |
Run all tests |
pnpm lint |
Lint every workspace |
pnpm type-check |
Type-check every workspace |
pnpm format |
Format with Prettier |
| Command | What it does |
|---|---|
pnpm --filter web dev |
Start the docs site in dev mode |
pnpm --filter web build |
Build the docs site for production |
| Package | Required | Notes |
|---|---|---|
@standard-schema/spec |
Yes, >=1.0.0 |
The interface used by fields. Schema implementations (Zod, Valibot, ArkType) are passed by the caller. |
| Package | Required | Notes |
|---|---|---|
| @deessejs/fp | Optional, peer >=1.0.0 |
Recommended if you want Result/Try types around ErrorInstance. Not required for using @deessejs/errors alone. |
| Field | Value |
|---|---|
engines.node |
>=22.14.0 |
packageManager |
pnpm@10.34.5 |
.
├── packages/
│ └── errors/ # The library — @deessejs/errors on npm
│ ├── src/ # Source code (ESM)
│ ├── tests/ # Vitest suites
│ ├── dist/ # Build output (gitignored)
│ └── tsconfig.build.json
├── apps/
│ └── web/ # Documentation site (Next.js + Fumadocs)
├── docs/
│ ├── internal/ # Engineering plans, runbooks
│ │ ├── product/
│ │ └── versions/
│ └── engineering/
├── pnpm-workspace.yaml
├── turbo.json # Turborepo pipelines
├── .changeset/ # Changesets for versioning
└── README.md
Releases are fully automated via Changesets + npm Trusted Publishing (OIDC). No long-lived NPM_TOKEN is required.
| What | How |
|---|---|
| Bump version | Add a .changeset/<topic>.md file with semver and description on a PR to staging |
| Open the release PR | Cherry-pick selected commits from staging into release/vX.Y.Z and PR to main |
| Publish | Merge to main - release.yml detects changesets and publishes via Trusted Publishing to npm with provenance attestation |
| Hotfix | Branch from main as release/hotfix-<slug>, open PR directly to main with [hotfix] label. Same workflow fires. |
| Rollback | Use pnpm changeset version then revert the merge. npm deprecations: pnpm npm deprecate @deessejs/errors@<rev> '<msg>' |
For the full release runbook, see docs/internal/engineering/process/releasing-a-new-version.md.
- ESM-only. The package exports ES modules. Consumers using legacy CJS resolvers must use dynamic
import(). - Strict types.
error()returns a typed factory;is()narrows. Noanyleakages. - Composition over inheritance. All error primitives compose via instance methods (
.from(),.addNote(),inherits). No class hierarchy on the consumer side. - Zero decorators. Pure factory functions. The library is straightforward to read in DevTools and
node --prof. - Smoke-tested before publish. The release workflow imports the built artifact and verifies key exports are present. A broken build fails the publish step before reaching npm.
- Symmetric interop with @deessejs/fp.
Resultconstructors acceptErrorInstanceso you never have to coerce a typed error to a string.
Open an issue to discuss larger changes. For typos, broken links, and small fixes, PRs are welcome.
Before submitting a PR:
- Run
pnpm --filter @deessejs/errors test:runandpnpm --filter @deessejs/errors lint. - Add a
.changeset/<topic>.mdif the change is user-facing (patch / minor / major). - Update
docs/internal/product/README.mdif the API surface changes.
The README layout and monorepo tooling for this project are based on the deessejs/package-template. The shipped README borrows its structure from the deessejs/fp README, adapted for the @deessejs/errors API surface.
MIT. See the LICENSE file for details.
- Issues: github.com/deessejs/errors/issues
- Discussions: github.com/deessejs/errors/discussions
- Email: support@deessejs.com
- Documentation: errors.deessejs.com