
Security News
/Company News
Securing the Financial Frontier: How Capital One Uses Socket for Open Source Security
Capital One is partnering with Socket to proactively secure its open source supply chain.
Extensible error class with automatic code derivation, message interpolation, error grouping, and JSON serialization.
Core error class library for TypeScript. Provides BaseError with automatic code derivation, message interpolation, and JSON serialization. Zero runtime dependencies.
Table of Contents
npm install @ebec/core
The constructor accepts a string or an ErrorOptions object:
import { BaseError } from '@ebec/core';
// String message
const error = new BaseError('something went wrong');
// Options object
const error = new BaseError({
message: 'something went wrong',
code: 'SOMETHING_WRONG',
});
// No arguments — defaults to message "An error occurred"
const error = new BaseError();
When no code is provided, it is derived from the class name by converting PascalCase to CONSTANT_CASE:
const error = new BaseError();
console.log(error.code);
// "BASE_ERROR"
class NotFoundError extends BaseError {}
const notFound = new NotFoundError();
console.log(notFound.code);
// "NOT_FOUND_ERROR"
An explicit code always takes priority:
const error = new BaseError({ code: 'CUSTOM_CODE' });
console.log(error.code);
// "CUSTOM_CODE"
Use messageData to fill {placeholder} tokens in the message. The data is used for interpolation only and is not stored on the error instance.
const error = new BaseError({
message: 'User {id} not found in {service}',
messageData: { id: 42, service: 'auth' },
});
console.log(error.message);
// "User 42 not found in auth"
Missing keys are left as-is:
const error = new BaseError({
message: 'Missing {field}',
messageData: { other: 'value' },
});
console.log(error.message);
// "Missing {field}"
Use the cause option to preserve the original error:
try {
await db.query('...');
} catch (err) {
throw new BaseError({
message: 'query failed',
code: 'DB_ERROR',
cause: err,
});
}
toJSON() returns a plain object with name, message, code, and optionally cause. If cause is a BaseError, it is serialized recursively.
const cause = new BaseError({ message: 'inner', code: 'INNER' });
const error = new BaseError({ message: 'outer', code: 'OUTER', cause });
console.log(JSON.stringify(error, null, 2));
// {
// "name": "BaseError",
// "message": "outer",
// "code": "OUTER",
// "cause": {
// "name": "BaseError",
// "message": "inner",
// "code": "INNER",
// "@instanceof": ["@ebec/core/BaseError"]
// },
// "@instanceof": ["@ebec/core/BaseError"]
// }
The @instanceof key carries the class-marker chain — one Symbol.for(...) registry key per class in the inheritance path — as a string list, since symbols don't survive JSON.stringify. Use matchesInstanceof to match a marker against both the in-process symbol chain and the rehydrated string chain:
import { BASE_ERROR_INSTANCE, matchesInstanceof } from '@ebec/core';
const rehydrated = JSON.parse(JSON.stringify(new BaseError('boom')));
matchesInstanceof(rehydrated, BASE_ERROR_INSTANCE); // true
import { isBaseError, isErrorWithCode } from '@ebec/core';
// Check if any value is a BaseError-shaped object
if (isBaseError(error)) {
console.log(error.code);
}
// Narrow by specific code
if (isErrorWithCode(error, 'NOT_FOUND')) {
// error.code is narrowed to 'NOT_FOUND'
}
// Match against multiple codes
if (isErrorWithCode(error, ['NOT_FOUND', 'GONE'])) {
// error.code is 'NOT_FOUND' | 'GONE'
}
Use the errors option to collect multiple errors into a single error:
const errors = [
new BaseError({ message: 'field "name" is required', code: 'VALIDATION' }),
new BaseError({ message: 'field "email" is invalid', code: 'VALIDATION' }),
];
throw new BaseError({
message: 'validation failed',
errors,
});
Plain Error instances work too — no wrapping needed:
const results = await Promise.allSettled([taskA(), taskB(), taskC()]);
const failures = results
.filter((r) => r.status === 'rejected')
.map((r) => r.reason);
if (failures.length > 0) {
throw new BaseError({ message: 'batch operation failed', errors: failures });
}
Use isBaseErrorGroup to check if an error carries grouped errors:
import { isBaseErrorGroup } from '@ebec/core';
if (isBaseErrorGroup(error)) {
for (const child of error.errors) {
console.log(child.message);
}
}
toJSON() includes errors when present, serializing each child via its toJSON() method if available:
const error = new BaseError({
message: 'batch failed',
errors: [
new BaseError({ message: 'step 1', code: 'STEP_1' }),
new Error('step 2'),
],
});
console.log(JSON.stringify(error, null, 2));
// {
// "name": "BaseError",
// "message": "batch failed",
// "code": "BASE_ERROR",
// "errors": [
// { "name": "BaseError", "message": "step 1", "code": "STEP_1" },
// { "message": "step 2" }
// ]
// }
Define a centralized catalog of error factories with interpolation support:
import { defineErrorCatalog } from '@ebec/core';
const errors = defineErrorCatalog({
USER_NOT_FOUND: { message: 'User {id} not found' },
INVALID_INPUT: { message: 'Invalid input: {reason}', code: 'VALIDATION_ERROR' },
});
// Create errors with interpolation data
throw errors.USER_NOT_FOUND({ id: 42 });
// ^ message: "User 42 not found", code: "USER_NOT_FOUND"
// Override via second argument
throw errors.INVALID_INPUT({ reason: 'email required' }, { code: 'MISSING_EMAIL' });
// ^ message: "Invalid input: email required", code: "MISSING_EMAIL"
When code is not specified in the catalog entry, the key name is used as the code.
class BaseError extends Error {
readonly code: string;
readonly errors?: ReadonlyArray<Error>;
cause?: unknown;
constructor(input?: string | ErrorOptions);
toJSON(): { name: string; message: string; code: string; cause?: unknown; errors?: unknown[]; '@instanceof': string[] };
}
| Property | Type | Description |
|---|---|---|
message | string | Error message. Defaults to "An error occurred". |
code | string | Error identifier. Derived from class name if not set. |
messageData | Record<string, unknown> | Data for {placeholder} interpolation. Not stored. |
cause | unknown | Underlying cause of the error. |
errors | Error[] | Collection of errors for batch/group scenarios. |
stack | string | Override the stack trace. |
| Function | Returns | Description |
|---|---|---|
isBaseError(input) | input is IBaseError | Checks for Error with string code |
isBaseErrorGroup(input) | input is IBaseErrorGroup | Checks for isBaseError + errors array |
isErrorWithCode(input, code) | input is IBaseError & { code: C } | Narrows code to specific value(s) |
isError(input) | input is Error | Duck-type check for Error-shaped objects |
isErrorOptions(input) | input is ErrorOptions | Validates options shape |
| Function | Description |
|---|---|
sanitizeErrorCode(input) | Converts PascalCase to CONSTANT_CASE |
extractErrorOptions(input) | Normalizes string | ErrorOptions to ErrorOptions |
defineErrorCatalog(definitions) | Creates typed error factory functions |
toSerializable(input) | Converts to JSON-safe form via toJSON() or { message } fallback |
markInstanceof(target, marker) | Appends a Symbol.for(...) class marker to the target's @instanceof chain |
hasInstanceof(input, marker) | Checks the chain for the marker symbol (strict, in-process form only) |
matchesInstanceof(input, marker) | Checks the chain for the marker symbol or its description string (JSON-rehydrated form) |
serializeInstanceofChain(input) | Serializes the chain to its string form, as emitted by toJSON() |
Made with 💚
Published under MIT License.
FAQs
Extensible error class with automatic code derivation, message interpolation, error grouping, and JSON serialization.
The npm package @ebec/core receives a total of 12,883 weekly downloads. As such, @ebec/core popularity was classified as popular.
We found that @ebec/core demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
/Company News
Capital One is partnering with Socket to proactively secure its open source supply chain.

Security News
Socket CTO Ahmad Nassri discusses how to keep AI agents from bypassing package blocks, limit credential access, and monitor their actions.

Security News
GPT-6 Astra tried to plant malicious code in simulated open source projects using fake GitHub accounts and deceptive PRs during an assigned CTF challenge.