From 7d37e4608da5b1c97da6f9cb1d4f1226e88dc0a6 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 4 Aug 2026 13:37:55 +0200 Subject: [PATCH 1/8] docs: restyle README to match shared template Adopt the @deessejs/fp README layout for consistency across the deessejs ecosystem. --- README.md | 289 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 150 insertions(+), 139 deletions(-) diff --git a/README.md b/README.md index 0de1045..3d2fe96 100644 --- a/README.md +++ b/README.md @@ -1,184 +1,195 @@ -# @deessejs/errors - -[![npm](https://img.shields.io/npm/v/@deessejs/errors)](https://www.npmjs.com/package/@deessejs/errors) -[![TypeScript](https://img.shields.io/badge/typescript-%E2%9A%99%EF%B8%8F-blue)](https://www.typescriptlang.org/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) - -A TypeScript error handling library with exception chaining, hierarchical inheritance, and rich error semantics — inspired by Python's error system. - -## Features - -- **Exception Chaining** — Preserve the full context of errors with cause chains via `.from()` -- **Hierarchical Inheritance** — Organize errors in meaningful hierarchies with single or multiple inheritance -- **Rich Error Semantics** — Attach structured data, templates, and notes to errors -- **TypeScript First** — Full type safety with comprehensive type definitions - -## Installation +

+

@deessejs/errors

+

+ +

+ 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. +

+ +

+ + License + + + CI + + + Stars + + + npm + +

+ +

+ + Documentation + +

+ +> **Sibling projects:** [@deessejs/fp](https://github.com/deessejs/fp) provides `Result` and `Try` types that integrate natively with [@deessejs/errors](https://github.com/deessejs/errors) error factories. Install them together to get a complete error-handling story without glue code. + +--- + +## What is included + +| 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](https://github.com/deessejs/fp) integration** | `Result`/`Try` accept `ErrorInstance` directly. ||Type-safe error pipelines end-to-end, no string-error footguns. | + +## Why this library + +- **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`/`main` duplication. +- **Minimal runtime.** The only runtime dependency is `@standard-schema/spec`. +- **TypeScript first-class.** Strict types, no `any` leakages, full inference. JSDoc on every public symbol. +- **Real testing.** Vitest with type-level and runtime tests, including cause-chain traversal. + +## Quick start + +### Prerequisites + +- Node.js 22.x for consumers (the package emits ESM) +- pnpm 10+ for development (`corepack enable` if not installed) +- TypeScript 5.x for consumers (`dist/*.d.ts` is published) + +### Install ```bash npm install @deessejs/errors -# or -pnpm add @deessejs/errors -# or -yarn add @deessejs/errors ``` -## Quick Start +[@deessejs/fp](https://github.com/deessejs/fp) is optional - install it if you want to compose `Result`/`Try` types around `ErrorInstance`. -### Creating Errors +### Usage -```typescript -import { error } from '@deessejs/errors'; +```{type=typescript} -// Simple error -const ValidationError = error({ name: 'ValidationError' }); -const err = ValidationError(); +The example below uses several typed errors chained together. -// Error with message template -const ValidationError = error({ - name: 'ValidationError', - message: 'Field "{field}" is invalid: {reason}', -}); +### Engine compatibility -const err = ValidationError({ field: 'email', reason: 'invalid format' }); -// err.message === 'Field "email" is invalid: invalid format' -``` +| Runtime | Minimum version | +|---| +| Node.js | 22.0.0 | +| pnpm | 10 (for development) | +| TypeScript | 5.x | -### Exception Chaining +ESM-only. Consumers using a CJS resolver need to use dynamic `import()` or migrate to ESM. -```typescript -import { error } from '@deessejs/errors'; +## Available commands -const ValidationError = error({ name: 'ValidationError' }); -const ProcessingError = error({ name: 'ProcessingError' }); +### Package: `@deessejs/errors` -const validationErr = ValidationError({ field: 'email' }); -const processingErr = ProcessingError(); +| 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 | -// Chain errors with .from() -processingErr.from(validationErr); +### Root (monorepo) -console.log(processingErr.message); // "ProcessingError" -console.log(processingErr.cause); // validationErr -console.log(processingErr.causes); // [validationErr] -``` +| 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 | -### Hierarchical Inheritance - -```typescript -import { error } from '@deessejs/errors'; - -// Single inheritance -const AppError = error({ name: 'AppError' }); -const ValidationError = error({ - name: 'ValidationError', - inherits: AppError, -}); - -// Multiple inheritance -const NetworkError = error({ name: 'NetworkError' }); -const StorageError = error({ name: 'StorageError' }); -const CombinedError = error({ - name: 'CombinedError', - inherits: [NetworkError, StorageError], -}); -``` +### App: `web` (documentation site) -### Type Checking +| 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 | -```typescript -import { error, is } from '@deessejs/errors'; +## Compatibility -const AppError = error({ name: 'AppError' }); -const ValidationError = error({ - name: 'ValidationError', - inherits: AppError, -}); +### Runtime dependency -const err = ValidationError(); +| 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. | -is(err, ValidationError); // true -is(err, AppError); // true (inherits from AppError) -``` +### Peer dependencies -### Traverse Cause Chain +| Package | Required | Notes | +|---| +| [@deessejs/fp](https://github.com/deessejs/fp) | Optional, peer `>=1.0.0` | Recommended if you want `Result`/`Try` types around `ErrorInstance`. Not required for using `@deessejs/errors` alone. | -```typescript -import { error, causes } from '@deessejs/errors'; +### Engines -const Err1 = error({ name: 'Err1' }); -const Err2 = error({ name: 'Err2' }); -const Err3 = error({ name: 'Err3' }); +| Field | Value | +|---| +| `engines.node` | `>=22.14.0` | +| `packageManager` | `pnpm@10.34.5` | -const err1 = Err1(); -const err2 = Err2().from(err1); -const err3 = Err3().from(err2); +## Project structure -// Iterate through the cause chain -for (const cause of causes(err3)) { - console.log(cause.name); -} -// Output: Err2, Err1 ``` -## Why @deessejs/errors? - -Built-in JavaScript errors are limited. `@deessejs/errors` brings Python-style error handling to TypeScript. +. ++-- packages/ ++-- apps/ ++-- docs/ ++-- pnpm-workspace.yaml ++-- turbo.json# Turborepo pipelines ++-- .changeset/# Changesets for versioning ++-- README.md -| Feature | Built-in `Error` | @deessejs/errors | -| ------------------------ | ---------------- | ---------------- | -| Exception chaining | ❌ | ✅ | -| Hierarchical inheritance | ❌ | ✅ | -| Message templates | ❌ | ✅ | -| Type-safe fields | ❌ | ✅ | -| Standard Schema support | ❌ | ✅ | - -```typescript -// Traditional approach — limited context -throw new Error('Validation failed'); // ❌ Generic, no structure - -// @deessejs/errors — rich, maintainable errors -const err = ValidationError({ field: 'email', reason: 'invalid format' }); -err.from(originalError); // ✅ Chain exceptions, preserve context ``` -## FAQ - -### How do I create a custom error type? +## Publishing -```typescript -import { error } from '@deessejs/errors'; - -const ValidationError = error({ name: 'ValidationError' }); -const err = ValidationError({ field: 'email' }); -``` +Releases are fully automated via Changesets + npm Trusted Publishing (OIDC). No long-lived `NPM_TOKEN` is required. -### How do I chain exceptions? +| What | How | +|---| +| Bump version | Add a `.changeset/.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-`, 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` ` ` | -```typescript -import { error } from '@deessejs/errors'; +For the full release runbook, see [`docs/internal/engineering/process/releasing-a-new-version.md`](docs/internal/engineering/process/releasing-a-new-version.md). -const validationErr = ValidationError({ field: 'email' }); -const processingErr = ProcessingError().from(validationErr); +## Architecture notes -console.log(processingErr.cause); // validationErr -``` +- **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. No `any` leakages. +- **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](https://github.com/deessejs/fp).** `Result` constructors accept `ErrorInstance` so you never have to coerce a typed error to a string. -### How do I check if an error is of a specific type? +## Contributing -```typescript -import { error, is } from '@deessejs/errors'; +Open an issue to discuss larger changes. For typos, broken links, and small fixes, PRs are welcome. -const AppError = error({ name: 'AppError' }); -const ValidationError = error({ name: 'ValidationError', inherits: AppError }); +Before submitting a PR: -is(err, AppError); // true if err is ValidationError or any descendant -``` +1. Run `pnpm --filter @deessejs/errors test:run` and `pnpm --filter @deessejs/errors lint`. +2. Add a `.changeset/.md` if the change is user-facing (patch / minor / major). +3. Update `docs/internal/product/README.md` if the API surface changes. -## Documentation +## License -For full documentation, visit [errors.deessejs.com](https://errors.deessejs.com) +[MIT](./LICENSE). See the LICENSE file for details. -## License +## Support -MIT +- Issues: [github.com/deessejs/errors/issues](https://github.com/deessejs/errors/issues) +- Discussions: [github.com/deessejs/errors/discussions](https://github.com/deessejs/errors/discussions) +- Email: [support@deessejs.com](mailto:support@deessejs.com) +- Documentation: [errors.deessejs.com](https://errors.deessejs.com) +``` From e2b4e14086b06f1f478451a022113e695e7c7add Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 4 Aug 2026 13:43:33 +0200 Subject: [PATCH 2/8] docs: add Acknowledgements section crediting @deessejs/package-template Mention the deessejs/package-template and deessejs/fp README in a new Acknowledgements section, placed above the License section. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 3d2fe96..f1c229e 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,12 @@ Before submitting a PR: 2. Add a `.changeset/.md` if the change is user-facing (patch / minor / major). 3. Update `docs/internal/product/README.md` if the API surface changes. + +## Acknowledgements + +The README layout and monorepo tooling for this project are based on the [deessejs/package-template](https://github.com/deessejs/package-template). The shipped README borrows its structure from the [deessejs/fp README](https://github.com/deessejs/fp), adapted for the @deessejs/errors API surface. + + ## License [MIT](./LICENSE). See the LICENSE file for details. From 31f2e12ac8f268c8e42bd50986040d9f129fb3d2 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 4 Aug 2026 14:09:00 +0200 Subject: [PATCH 3/8] docs: fix README -- corrected table, restored code block and tree, fixed deprecate row --- README.md | 177 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index f1c229e..5594d3d 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,20 @@ -

-

@deessejs/errors

+

+

@deessejs/errors

-

- 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. +

+ 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.

-

- - License - - - CI - - - Stars - - - npm - +

+ License + CI + Stars + npm

-

- - Documentation - +

+ Documentation

> **Sibling projects:** [@deessejs/fp](https://github.com/deessejs/fp) provides `Result` and `Try` types that integrate natively with [@deessejs/errors](https://github.com/deessejs/errors) error factories. Install them together to get a complete error-handling story without glue code. @@ -33,16 +23,17 @@ ## What is included -| 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](https://github.com/deessejs/fp) integration** | `Result`/`Try` accept `ErrorInstance` directly. ||Type-safe error pipelines end-to-end, no string-error footguns. | +| 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](https://github.com/deessejs/fp) integration | `Result`/`Try` accept `ErrorInstance` directly. | Type-safe error pipelines end-to-end, no string-error footguns. | ## Why this library @@ -70,17 +61,38 @@ npm install @deessejs/errors ### Usage -```{type=typescript} +```typescript +import { error, raise, is, causes } from '@deessejs/errors'; -The example below uses several typed errors chained together. +// 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] +``` ### Engine compatibility -| Runtime | Minimum version | -|---| -| Node.js | 22.0.0 | -| pnpm | 10 (for development) | -| TypeScript | 5.x | +| 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. @@ -88,78 +100,86 @@ ESM-only. Consumers using a CJS resolver need to use dynamic `import()` or migra ### Package: `@deessejs/errors` -| 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 --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 | ### Root (monorepo) -| Command | What it does | -|---| -| `pnpm build` | Build via Turborepo | -| `pnpm test` | Run all tests | -| `pnpm lint` | Lint every workspace | +| 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 | +| `pnpm format` | Format with Prettier | ### App: `web` (documentation site) -| Command | What it does | -|---| -| `pnpm --filter web dev` | Start the docs site in dev mode | +| 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 | ## Compatibility ### Runtime dependency -| Package | Required | Notes | -|---| +| 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. | ### Peer dependencies -| Package | Required | Notes | -|---| +| Package | Required | Notes | +| ---------------------------------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------- | | [@deessejs/fp](https://github.com/deessejs/fp) | Optional, peer `>=1.0.0` | Recommended if you want `Result`/`Try` types around `ErrorInstance`. Not required for using `@deessejs/errors` alone. | ### Engines -| Field | Value | -|---| -| `engines.node` | `>=22.14.0` | +| Field | Value | +| ---------------- | -------------- | +| `engines.node` | `>=22.14.0` | | `packageManager` | `pnpm@10.34.5` | ## Project structure ``` - . -+-- packages/ -+-- apps/ -+-- docs/ -+-- pnpm-workspace.yaml -+-- turbo.json# Turborepo pipelines -+-- .changeset/# Changesets for versioning -+-- README.md - +├── 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 ``` ## Publishing Releases are fully automated via Changesets + npm Trusted Publishing (OIDC). No long-lived `NPM_TOKEN` is required. -| What | How | -|---| -| Bump version | Add a `.changeset/.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-`, 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` ` ` | +| What | How | +| ------------------- | -------------------------------------------------------------------------------------------------------------------------- | +| Bump version | Add a `.changeset/.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-`, 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@ ''` | For the full release runbook, see [`docs/internal/engineering/process/releasing-a-new-version.md`](docs/internal/engineering/process/releasing-a-new-version.md). @@ -182,12 +202,10 @@ Before submitting a PR: 2. Add a `.changeset/.md` if the change is user-facing (patch / minor / major). 3. Update `docs/internal/product/README.md` if the API surface changes. - ## Acknowledgements The README layout and monorepo tooling for this project are based on the [deessejs/package-template](https://github.com/deessejs/package-template). The shipped README borrows its structure from the [deessejs/fp README](https://github.com/deessejs/fp), adapted for the @deessejs/errors API surface. - ## License [MIT](./LICENSE). See the LICENSE file for details. @@ -198,4 +216,3 @@ The README layout and monorepo tooling for this project are based on the [deesse - Discussions: [github.com/deessejs/errors/discussions](https://github.com/deessejs/errors/discussions) - Email: [support@deessejs.com](mailto:support@deessejs.com) - Documentation: [errors.deessejs.com](https://errors.deessejs.com) -``` From 4f854a96513a557556af853c6fff1097a11e0ced Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 4 Aug 2026 14:33:59 +0200 Subject: [PATCH 4/8] docs: align packages/errors/README.md with new shared template --- packages/errors/README.md | 203 ++++++++++++-------------------------- 1 file changed, 63 insertions(+), 140 deletions(-) diff --git a/packages/errors/README.md b/packages/errors/README.md index 0de1045..13b825e 100644 --- a/packages/errors/README.md +++ b/packages/errors/README.md @@ -2,183 +2,106 @@ [![npm](https://img.shields.io/npm/v/@deessejs/errors)](https://www.npmjs.com/package/@deessejs/errors) [![TypeScript](https://img.shields.io/badge/typescript-%E2%9A%99%EF%B8%8F-blue)](https://www.typescriptlang.org/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![CI](https://img.shields.io/github/actions/workflow/status/deessejs/errors/ci.yml?label=CI)](https://github.com/deessejs/errors/actions/workflows/ci.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) -A TypeScript error handling library with exception chaining, hierarchical inheritance, and rich error semantics — inspired by Python's error system. +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`](https://github.com/deessejs/fp)'s Result and Try. -## Features +> **Sibling projects:** [`@deessejs/fp`](https://github.com/deessejs/fp) provides `Result` and `Try` types that integrate natively with [`@deessejs/errors`](https://github.com/deessejs/errors) error factories. Install them together to get a complete error-handling story without glue code. -- **Exception Chaining** — Preserve the full context of errors with cause chains via `.from()` -- **Hierarchical Inheritance** — Organize errors in meaningful hierarchies with single or multiple inheritance -- **Rich Error Semantics** — Attach structured data, templates, and notes to errors -- **TypeScript First** — Full type safety with comprehensive type definitions +--- -## Installation +## What is included + +| 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`](https://github.com/deessejs/fp) integration | `Result`/`Try` accept `ErrorInstance` directly. | Type-safe error pipelines end-to-end, no string-error footguns. | + +## Install ```bash npm install @deessejs/errors -# or -pnpm add @deessejs/errors -# or -yarn add @deessejs/errors ``` -## Quick Start +[`@deessejs/fp`](https://github.com/deessejs/fp) is optional - install it if you want to compose `Result`/`Try` types around `ErrorInstance`. -### Creating Errors +## Usage ```typescript -import { error } from '@deessejs/errors'; +import { error, raise, is, causes } from '@deessejs/errors'; -// Simple error -const ValidationError = error({ name: 'ValidationError' }); -const err = ValidationError(); - -// Error with message template +// 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' -``` - -### Exception Chaining - -```typescript -import { error } from '@deessejs/errors'; - -const ValidationError = error({ name: 'ValidationError' }); -const ProcessingError = error({ name: 'ProcessingError' }); - -const validationErr = ValidationError({ field: 'email' }); -const processingErr = ProcessingError(); - -// Chain errors with .from() -processingErr.from(validationErr); - -console.log(processingErr.message); // "ProcessingError" -console.log(processingErr.cause); // validationErr -console.log(processingErr.causes); // [validationErr] -``` - -### Hierarchical Inheritance -```typescript -import { error } from '@deessejs/errors'; - -// Single inheritance -const AppError = error({ name: 'AppError' }); -const ValidationError = error({ - name: 'ValidationError', - inherits: AppError, -}); - -// Multiple inheritance -const NetworkError = error({ name: 'NetworkError' }); -const StorageError = error({ name: 'StorageError' }); -const CombinedError = error({ - name: 'CombinedError', - inherits: [NetworkError, StorageError], -}); -``` - -### Type Checking - -```typescript -import { error, is } from '@deessejs/errors'; - -const AppError = error({ name: 'AppError' }); -const ValidationError = error({ - name: 'ValidationError', - inherits: AppError, -}); +// Chain a cause +const cause = error({ name: 'NetworkError' })(); +err.from(cause); -const err = ValidationError(); +// Throw it +raise(err); +// Later, type-check and walk the chain is(err, ValidationError); // true -is(err, AppError); // true (inherits from AppError) -``` - -### Traverse Cause Chain - -```typescript -import { error, causes } from '@deessejs/errors'; - -const Err1 = error({ name: 'Err1' }); -const Err2 = error({ name: 'Err2' }); -const Err3 = error({ name: 'Err3' }); - -const err1 = Err1(); -const err2 = Err2().from(err1); -const err3 = Err3().from(err2); - -// Iterate through the cause chain -for (const cause of causes(err3)) { - console.log(cause.name); -} -// Output: Err2, Err1 -``` - -## Why @deessejs/errors? - -Built-in JavaScript errors are limited. `@deessejs/errors` brings Python-style error handling to TypeScript. - -| Feature | Built-in `Error` | @deessejs/errors | -| ------------------------ | ---------------- | ---------------- | -| Exception chaining | ❌ | ✅ | -| Hierarchical inheritance | ❌ | ✅ | -| Message templates | ❌ | ✅ | -| Type-safe fields | ❌ | ✅ | -| Standard Schema support | ❌ | ✅ | - -```typescript -// Traditional approach — limited context -throw new Error('Validation failed'); // ❌ Generic, no structure - -// @deessejs/errors — rich, maintainable errors -const err = ValidationError({ field: 'email', reason: 'invalid format' }); -err.from(originalError); // ✅ Chain exceptions, preserve context +causes(err); // [cause] ``` -## FAQ +## Why this library -### How do I create a custom error type? +- **Simple by default.** No class hierarchies, no decorators under reflection. +- **ESM-only.** Modern packaging, no CJS shim, no `module`/`main` duplication. +- **Minimal runtime.** The only runtime dependency is `@standard-schema/spec`. +- **TypeScript first-class.** Strict types, no `any` leakages, full inference. JSDoc on every public symbol. +- **Real testing.** Vitest with type-level and runtime tests, including cause-chain traversal. -```typescript -import { error } from '@deessejs/errors'; +## Engine compatibility -const ValidationError = error({ name: 'ValidationError' }); -const err = ValidationError({ field: 'email' }); -``` +| Runtime | Required | +| ---------- | -------------------- | +| Node.js | `>=22.14.0` | +| pnpm | `10` for development | +| TypeScript | `5.x` | -### How do I chain exceptions? +ESM-only. Consumers using a CJS resolver need to use dynamic `import()` or migrate to ESM. -```typescript -import { error } from '@deessejs/errors'; +## Available commands -const validationErr = ValidationError({ field: 'email' }); -const processingErr = ProcessingError().from(validationErr); +| Command | What it does | +| ----------------- | -------------------------------------------- | +| `pnpm build` | Build `dist/` (`tsc -p tsconfig.build.json`) | +| `pnpm test` | Run vitest in watch mode | +| `pnpm test:run` | Run vitest once | +| `pnpm type-check` | `tsc --noEmit` | +| `pnpm lint` | Run ESLint | -console.log(processingErr.cause); // validationErr -``` - -### How do I check if an error is of a specific type? +## Contributing -```typescript -import { error, is } from '@deessejs/errors'; +Open an issue to discuss larger changes. For typos, broken links, and small fixes, PRs are welcome. -const AppError = error({ name: 'AppError' }); -const ValidationError = error({ name: 'ValidationError', inherits: AppError }); +Before submitting a PR: -is(err, AppError); // true if err is ValidationError or any descendant -``` +1. Run `pnpm test:run` and `pnpm lint`. +2. Add a `.changeset/.md` if the change is user-facing (patch / minor / major). +3. Update `docs/internal/product/README.md` if the API surface changes. -## Documentation +## License -For full documentation, visit [errors.deessejs.com](https://errors.deessejs.com) +[MIT](./LICENSE). See the LICENSE file for details. -## License +## Acknowledgements -MIT +The README layout is based on the [deessejs/package-template](https://github.com/deessejs/package-template) and borrows its structure from the [deessejs/fp README](https://github.com/deessejs/fp), adapted for the `@deessejs/errors` API surface. From b8ab4c8b9de16a2376e16f7fdacae872658db221 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 4 Aug 2026 14:37:52 +0200 Subject: [PATCH 5/8] chore: enrich package.json with npm SEO fields Added: bugs.url, files, sideEffects:false, engines, funding, expanded keywords, expanded description. --- packages/errors/package.json | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/errors/package.json b/packages/errors/package.json index b7dd70e..08df779 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -1,12 +1,15 @@ { "name": "@deessejs/errors", "version": "1.3.2", - "description": "TypeScript error handling library inspired by Python — function-based API, exception chaining, hierarchical inheritance", + "description": "Lightweight, type-safe error handling for TypeScript — Python-style exception chaining, hierarchical inheritance, and rich error semantics. Function-based API, ESM-only, designed for first-class interoperability with @deessejs/fp.", "homepage": "https://errors.deessejs.com", "repository": { "type": "git", "url": "https://github.com/deessejs/errors.git" }, + "bugs": { + "url": "https://github.com/deessejs/errors/issues" + }, "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -17,6 +20,12 @@ "types": "./dist/index.d.ts" } }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "sideEffects": false, "scripts": { "test": "vitest", "test:run": "vitest run", @@ -28,13 +37,15 @@ "typescript", "errors", "exception", - "error-handling", "exception-chaining", + "error-handling", "hierarchical-errors", "python-style-errors", "custom-error-types", - "nodejs", - "npm-package" + "standard-schema", + "esm", + "monorepo", + "nodejs" ], "author": "Nesalia Inc. ", "license": "MIT", @@ -42,6 +53,13 @@ "access": "public", "provenance": true }, + "engines": { + "node": ">=22.14.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/deessejs" + }, "devDependencies": { "@eslint/js": "^9.0.0", "@types/node": "^25.9.1", From 8e1a9484acbb44a54898a856f0c76fc29ade4f3a Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 4 Aug 2026 14:45:44 +0200 Subject: [PATCH 6/8] chore: add changeset for docs/seo release (1.3.3) --- .changeset/docs-seo-enrichment.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .changeset/docs-seo-enrichment.md diff --git a/.changeset/docs-seo-enrichment.md b/.changeset/docs-seo-enrichment.md new file mode 100644 index 0000000..c503e29 --- /dev/null +++ b/.changeset/docs-seo-enrichment.md @@ -0,0 +1,15 @@ +--- +'@deessejs/errors': patch +--- + +Refresh the README and package.json metadata for the npm listing. + +README: +- Adopt the shared ecosystem layout (badges block, What is included table, Quick start, Compatibility, Project structure, Publishing, Architecture notes, Contributing, Acknowledgements). +- Credit `deessejs/package-template` and `deessejs/fp` in the new Acknowledgements section. +- Surface the `addNote()` (PEP 678), `raise()`, `causes()`, and the `Result`/`Try` interop story in both the root and the package README. + +Package metadata: +- Replace the placeholder description with a more searchable summary (Python-style, ESM, `@deessejs/fp` interop). +- Add `bugs.url`, `files`, `sideEffects: false`, `engines.node`, and `funding` (GitHub Sponsors). +- Refresh `keywords`: drop `npm-package`, add `standard-schema`, `esm`, `monorepo`. From 43c4928bc4894e8fd85eee26b837911ccf7b6528 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 4 Aug 2026 16:01:38 +0200 Subject: [PATCH 7/8] chore: drop changeset for docs/seo release --- .changeset/docs-seo-enrichment.md | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 .changeset/docs-seo-enrichment.md diff --git a/.changeset/docs-seo-enrichment.md b/.changeset/docs-seo-enrichment.md deleted file mode 100644 index c503e29..0000000 --- a/.changeset/docs-seo-enrichment.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@deessejs/errors': patch ---- - -Refresh the README and package.json metadata for the npm listing. - -README: -- Adopt the shared ecosystem layout (badges block, What is included table, Quick start, Compatibility, Project structure, Publishing, Architecture notes, Contributing, Acknowledgements). -- Credit `deessejs/package-template` and `deessejs/fp` in the new Acknowledgements section. -- Surface the `addNote()` (PEP 678), `raise()`, `causes()`, and the `Result`/`Try` interop story in both the root and the package README. - -Package metadata: -- Replace the placeholder description with a more searchable summary (Python-style, ESM, `@deessejs/fp` interop). -- Add `bugs.url`, `files`, `sideEffects: false`, `engines.node`, and `funding` (GitHub Sponsors). -- Refresh `keywords`: drop `npm-package`, add `standard-schema`, `esm`, `monorepo`. From 1acfb53ed01f95f11c6debdf50eba7c038a27cb0 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Wed, 5 Aug 2026 10:29:56 +0200 Subject: [PATCH 8/8] chore: add changeset for docs/seo release (1.3.3) --- .changeset/docs-seo-enrichment.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .changeset/docs-seo-enrichment.md diff --git a/.changeset/docs-seo-enrichment.md b/.changeset/docs-seo-enrichment.md new file mode 100644 index 0000000..c503e29 --- /dev/null +++ b/.changeset/docs-seo-enrichment.md @@ -0,0 +1,15 @@ +--- +'@deessejs/errors': patch +--- + +Refresh the README and package.json metadata for the npm listing. + +README: +- Adopt the shared ecosystem layout (badges block, What is included table, Quick start, Compatibility, Project structure, Publishing, Architecture notes, Contributing, Acknowledgements). +- Credit `deessejs/package-template` and `deessejs/fp` in the new Acknowledgements section. +- Surface the `addNote()` (PEP 678), `raise()`, `causes()`, and the `Result`/`Try` interop story in both the root and the package README. + +Package metadata: +- Replace the placeholder description with a more searchable summary (Python-style, ESM, `@deessejs/fp` interop). +- Add `bugs.url`, `files`, `sideEffects: false`, `engines.node`, and `funding` (GitHub Sponsors). +- Refresh `keywords`: drop `npm-package`, add `standard-schema`, `esm`, `monorepo`.