Releases: jpbaking/error-extender
Release list
v2.0.1 — merge type-safety & CI cleanup
Patch release. No public API or runtime behavior changes — safe drop-in upgrade from 2.0.0.
Changes since v2.0.0
Internal
merge()refactor (d9ed6d2) — hoistedresult[key]/obj[key]into locals so theisPlainObjecttype guard narrows them, removing theas Record<string, unknown>casts. Behavior unchanged: deep-merge plain objects, last-write-wins otherwise.
CI / tooling
- Renamed workflow to
ci.yml; dropped the publish-on-release job and OIDC permissions (39aae1a). - Node version bumped to 22 in CI.
Quality
- Lint clean; 19/19 tests passing; 100% coverage (statements/branches/functions/lines).
Note: npm publishing is now manual (npm publish) — the release no longer triggers an automated publish.
v2.0.0 — TypeScript rewrite
Breaking Changes
- Factory-without-
newremoved. Errors must now be constructed withnew:// Before (v1.x) throw CustomError({ message: 'oops' }); // After (v2.0.0) throw new CustomError({ message: 'oops' });
- Entry point moved to
dist/. Package exports aredist/index.jsanddist/index.d.ts. Do not import fromsrc/.
What's New
Full TypeScript rewrite
The entire source has been rewritten in TypeScript (src/). Compiled output ships as CommonJS targeting ES2020 via tsc.
Typed public API
import extendError, {
ExtendOptions,
ErrorConstructorOptions,
ExtendedError,
ExtendedErrorConstructor,
} from 'error-extender';
interface HttpErrorData { status: number; body?: string }
const HttpError = extendError<HttpErrorData>('HttpError', {
defaultData: { status: 500 },
});
const err = new HttpError({ data: { status: 404, body: 'Not Found' } });
err.data?.status; // typed as number| Exported type | Description |
|---|---|
ExtendOptions<TData> |
Options passed to extendError() |
ErrorConstructorOptions<TData> |
Options passed to new CustomError() |
ExtendedError<TData> |
Instance interface with typed data and cause |
ExtendedErrorConstructor<TData> |
Constructor interface returned by extendError() |
Prototype-based inheritance (no class extends)
The factory uses Object.create() to wire the prototype chain directly, avoiding the constructor-chaining issues that affect class extends Error patterns in transpiled environments. instanceof checks work correctly across deep hierarchies.
Default inheritance and deep-merge
defaultMessageis inherited from the parent if the child does not set one.defaultDatadeep-merges with the parent'sdefaultDatawhen both are plain objects; otherwise the child's value replaces the parent's.
const BaseError = extendError('BaseError', { defaultMessage: 'Something went wrong' });
const ChildError = extendError('ChildError', { parent: BaseError });
new ChildError().message; // 'Something went wrong' — inheritedShort-form constructor aliases
All constructor options have single-letter aliases to reduce boilerplate at call sites:
| Short | Long |
|---|---|
m |
message |
d |
data |
c |
cause |
throw new HttpError({ m: 'Not Found', d: { status: 404 }, c: originalError });cause chain in stack traces
When a cause is provided, its stack is appended to the thrown error's stack as Caused by: ..., giving full chain visibility in logs without any extra tooling.
CI/CD: automated npm publish
A GitHub Actions workflow now publishes to npmjs automatically on every GitHub release (NPM_TOKEN secret required).
License change
Relicensed from MIT to 0BSD (Zero-Clause BSD). No attribution required, no conditions — maximally permissive.
Test results
Test Files 3 passed (3)
Tests 19 passed (19)
Duration 539ms
| Metric | Coverage |
|---|---|
| Statements | 100% (42/42) |
| Branches | 100% (47/47) |
| Functions | 100% (5/5) |
| Lines | 100% (42/42) |
Commits since v1.0.2
| Commit | Description |
|---|---|
27f19d6 |
feat: Migrate to TypeScript with Vitest |
8035b9f |
docs: Rewrite README for TypeScript migration |
2ec704a |
chore: Release 2.0.0 — version bump, AGENTS.md DOX hierarchy, updated README |
4eb7bc5 |
chore: Relicense from MIT to 0BSD |
9d1458f |
chore: Remove email address from package.json author field |
adfb1c4 |
chore: Upgrade all devDependencies to latest |
9683020 |
fix: Quote test glob so mocha resolves ** recursively |
739cfb4 |
chore: Add GitHub Actions publish workflow |
0a28999 |
chore: Rename publish workflow to npm-publish-on-github-release.yml |
v1.0.2 — npm listing fix
Patch release to fix the npm registry listing. No functional code changes.
Change list
- README: added "100% Code Coverage" callout section — notes that coverage is verifiable via
npm test - package.json: version bump
1.0.1→1.0.2to force a clean republish and restore the correct npm registry listing
v1.0.1 — README improvements
Documentation-only release. No code changes.
Change list
- README: added stacktrace demo at the top of the file — a full end-to-end example showing
Caused by:output when acauseis supplied - README: fixed copy-paste error — two code snippets used
extendError('ServiceError', ...)where the variable was namedAppError; both now correctly readextendError('AppError', ...) - package.json: version bump
1.0.0→1.0.1
v1.0.0 — Initial release
Initial release of error-extender — a utility for creating custom Error subclasses in Node.js with first-class support for error chaining, default context, and deep data merging.
Features
extendError(name, options?)
Creates a named custom error class. The returned constructor is a proper Error subclass — instanceof works up the full chain.
const extendError = require('error-extender');
const AppError = extendError('AppError');
const ServiceError = extendError('ServiceError', { parent: AppError });
const DbError = extendError('DbError', { parent: ServiceError });
new DbError() instanceof ServiceError // true
new DbError() instanceof AppError // true
new DbError() instanceof Error // trueoptions at class-definition time
| key | type | description |
|---|---|---|
parent |
Error constructor |
Error class to extend (default: Error) |
defaultMessage |
string |
Fallback message when none is provided at construction |
defaultData |
any |
Fallback data; plain-object values deep-merge down the inheritance chain |
Constructor options
Each extended error accepts an object literal with short aliases:
| key | alias | type |
|---|---|---|
message |
m |
string |
data |
d |
any |
cause |
c |
Error |
data merging
Instance data deep-merges with defaultData when both are plain objects:
const AppError = extendError('AppError', {
defaultData: { status: 503, body: { message: 'Unhandled error.' } }
});
new AppError({ d: { status: 401 } }).data
// → { status: 401, body: { message: 'Unhandled error.' } }cause chaining
Attaching a cause appends its full stack as Caused by:, mirroring Java exception chaining:
const rootCause = new Error('connection refused');
const err = new ServiceError({ message: 'Service unavailable', cause: rootCause });
console.log(err.stack);
// ServiceError: Service unavailable
// at ...
// Caused by: Error: connection refused
// at ...