From d11d9a15259b280e41a3a8635918b12ed3e40e16 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 2 Jun 2026 11:12:34 +0200 Subject: [PATCH 1/7] docs: add first draft of documentation site Create initial documentation structure with 13 MDX pages covering: - Getting started and installation - Error factory and instance concepts - Exception chaining with from() and causes() - Single and multiple inheritance - Type checking with is() - Message templates - API reference - Practical recipes Structure managed via meta.json with navigation sections. Co-Authored-By: Claude Opus 4.7 --- apps/web/content/docs/api-reference.mdx | 191 ++++++++++++++ apps/web/content/docs/causes-function.mdx | 135 ++++++++++ apps/web/content/docs/error-factory.mdx | 138 ++++++++++ apps/web/content/docs/error-instance.mdx | 150 +++++++++++ apps/web/content/docs/fields-schema.mdx | 177 +++++++++++++ apps/web/content/docs/from-method.mdx | 136 ++++++++++ apps/web/content/docs/index.mdx | 107 +++++++- apps/web/content/docs/installation.mdx | 87 ++++++ apps/web/content/docs/message-templates.mdx | 162 ++++++++++++ apps/web/content/docs/meta.json | 23 ++ .../web/content/docs/multiple-inheritance.mdx | 141 ++++++++++ apps/web/content/docs/recipes.mdx | 248 ++++++++++++++++++ apps/web/content/docs/single-inheritance.mdx | 130 +++++++++ apps/web/content/docs/test.mdx | 17 -- apps/web/content/docs/type-checking.mdx | 168 ++++++++++++ 15 files changed, 1986 insertions(+), 24 deletions(-) create mode 100644 apps/web/content/docs/api-reference.mdx create mode 100644 apps/web/content/docs/causes-function.mdx create mode 100644 apps/web/content/docs/error-factory.mdx create mode 100644 apps/web/content/docs/error-instance.mdx create mode 100644 apps/web/content/docs/fields-schema.mdx create mode 100644 apps/web/content/docs/from-method.mdx create mode 100644 apps/web/content/docs/installation.mdx create mode 100644 apps/web/content/docs/message-templates.mdx create mode 100644 apps/web/content/docs/meta.json create mode 100644 apps/web/content/docs/multiple-inheritance.mdx create mode 100644 apps/web/content/docs/recipes.mdx create mode 100644 apps/web/content/docs/single-inheritance.mdx delete mode 100644 apps/web/content/docs/test.mdx create mode 100644 apps/web/content/docs/type-checking.mdx diff --git a/apps/web/content/docs/api-reference.mdx b/apps/web/content/docs/api-reference.mdx new file mode 100644 index 0000000..2cd7041 --- /dev/null +++ b/apps/web/content/docs/api-reference.mdx @@ -0,0 +1,191 @@ +--- +title: API Reference +description: Complete API reference for all exports from @deessejs/errors. +--- + +This page documents all public exports from @deessejs/errors. Use this as a comprehensive reference for the library's API. + +## error() + +Creates an error factory function for defining typed, structured errors. + +```ts +const errorFactory = error(config) +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `config.name` | `string` | Error name identifier (required) | +| `config.message` | `string` | Message template with `{field}` placeholders | +| `config.fields` | `StandardSchemaV1` | Field schema for validation (Zod, Valibot, etc.) | +| `config.inherits` | `ErrorFactory \| ErrorFactory[]` | Parent error(s) to inherit from | + +### Returns + +An `ErrorFactory` function that creates error instances. The factory has these properties: + +| Property | Type | Description | +|----------|------|-------------| +| `name` | `string` | The error name | +| `inherits` | `ErrorFactory \| ErrorFactory[] \| undefined` | Parent error types | +| `schema` | `StandardSchemaV1 \| undefined` | Field validation schema | +| `rawMessage` | `string \| undefined` | Original message template | + +### Example + +```ts +import { error } from '@deessejs/errors'; + +const ValidationError = error<{ field: string }>({ + name: 'ValidationError', + message: 'Field "{field}" is invalid', + inherits: AppError, +}); + +const err = ValidationError({ field: 'email' }); +``` + +--- + +## raise() + +Throws an error instance. This is the primary mechanism for throwing errors in @deessejs/errors. + +```ts +raise(error: ErrorInstance): never +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `error` | `ErrorInstance` | The error to throw | + +### Returns + +`never` — This function always throws. + +### Example + +```ts +import { error, raise } from '@deessejs/errors'; + +const ValidationError = error({ name: 'ValidationError' }); + +raise(ValidationError({})); +``` + +--- + +## is() + +Type guard function to check if an error is an instance of a specific error type. + +```ts +const result = is(error, ErrorType) +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `error` | `unknown` | The error to check | +| `ErrorType` | `ErrorFactory \| ErrorClass` | The error type to check against | + +### Returns + +`boolean` — `true` if the error matches or inherits from the type. + +### Example + +```ts +import { error, is } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ + name: 'ValidationError', + inherits: AppError, +}); + +const err = ValidationError({}); + +is(err, ValidationError); // true +is(err, AppError); // true (through inheritance) +``` + +--- + +## causes() + +Returns all causes in an error chain. + +```ts +const chain = causes(error: unknown): Error[] +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `error` | `unknown` | The error to get causes from | + +### Returns + +`Error[]` — Array of errors in the cause chain, ordered newest to oldest. Returns an empty array for null, undefined, or errors without causes. + +### Example + +```ts +import { error, causes } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const appErr = AppError({}); +appErr.from(new Error('Original error')); + +const chain = causes(appErr); +console.log(chain.length); // 1 +``` + +--- + +## ErrorInstance + +The type of object returned by error factories. It extends the native `Error` type. + +### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `name` | `string` | Error name identifier | +| `message` | `string` | Human-readable error message | +| `stack` | `string` | Stack trace string | +| `fields` | `T` | User-defined fields | +| `notes` | `string[]` | Additional notes | +| `cause` | `Error \| null` | Direct cause of this error | +| `causes` | `Error[]` | Full cause chain | +| `context` | `Record \| null` | Injected context data | +| `inherits` | `ErrorFactory \| ErrorFactory[] \| undefined` | Parent error factories | + +### Methods + +| Method | Description | +|--------|-------------| +| `from(cause: Error)` | Chains a cause error to this error | + +--- + +## See Also + + + + Create custom error types. + + + Use is() for type guards. + + + Chain errors with from(). + + \ No newline at end of file diff --git a/apps/web/content/docs/causes-function.mdx b/apps/web/content/docs/causes-function.mdx new file mode 100644 index 0000000..49b42d4 --- /dev/null +++ b/apps/web/content/docs/causes-function.mdx @@ -0,0 +1,135 @@ +--- +title: The causes() Function +description: Use the causes() function to traverse error chains and access all causes in an error. +--- + +The `causes()` function provides a safe way to access the cause chain of any error. It works with both @deessejs/errors instances and native JavaScript errors, returning an empty array for errors without causes. + +## Basic Usage + +Call `causes()` with an error to get its cause chain as an array: + +```ts basic.ts +import { error, causes } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +const appErr = AppError({}); +appErr.from(ValidationError({ field: 'email' })); +appErr.from(new Error('Network timeout')); + +const chain = causes(appErr); + +console.log(chain.length); // 2 +console.log(chain[0].name); // "ValidationError" +console.log(chain[1].message); // "Network timeout" +``` + +The returned array contains only the errors that were explicitly added using `from()`, ordered from newest to oldest. + +## Why Use causes() Instead of Direct Access? + +The `causes()` function handles edge cases that direct property access doesn't: + +```ts safety.ts +import { causes } from '@deessejs/errors'; + +// Works with native errors (no causes property) +const nativeError = new Error('Something failed'); +const chain1 = causes(nativeError); +console.log(chain1.length); // 0 + +// Works with null/undefined (returns empty array) +const chain2 = causes(null); +console.log(chain2.length); // 0 + +const chain3 = causes(undefined); +console.log(chain3.length); // 0 +``` + +This robustness makes `causes()` ideal for writing generic error handling code that doesn't need to check the error type first. + +## Iterating Over the Chain + +You can iterate over the causes array to log or process each error in the chain: + +```ts iterate.ts +import { error, causes } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +const appErr = AppError({}); +appErr.from(ValidationError({ field: 'email' })); +appErr.from(new Error('Database connection failed')); + +// Log each cause +for (const cause of causes(appErr)) { + console.log(`[${cause.name}] ${cause.message}`); +} + +// Output: +// [ValidationError] ValidationError +// [Error] Database connection failed +``` + +This is useful for building detailed error logs or monitoring dashboards that track where errors originate. + +## Building Error Summaries + +A common pattern is to build a summary message from the error chain: + +```ts summary.ts +import { error, causes } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +const appErr = AppError({}); +appErr.from(ValidationError({ field: 'email' })); +appErr.from(new Error('DB connection failed')); + +const summary = causes(appErr) + .map((cause) => `${cause.name}: ${cause.message}`) + .join(' → '); + +console.log(summary); +// "ValidationError: ValidationError → Error: DB connection failed" +``` + +## Combining with is() + +Use `causes()` with `is()` to search for specific error types in the chain: + +```ts combined.ts +import { error, causes, is } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const NetworkError = error({ name: 'NetworkError' }); +const ValidationError = error({ name: 'ValidationError' }); + +const appErr = AppError({}); +appErr.from(NetworkError({ endpoint: '/api/users' })); +appErr.from(ValidationError({ field: 'email' })); + +// Check if any cause is a NetworkError +const hasNetworkFailure = causes(appErr).some( + (cause) => is(cause, NetworkError) +); + +console.log(hasNetworkFailure); // true +``` + +This pattern is useful when you need to know not just that an error happened, but what kind of errors were involved in its chain. + +## See Also + + + + Learn how to build error chains with from(). + + + Use is() to check error types. + + \ No newline at end of file diff --git a/apps/web/content/docs/error-factory.mdx b/apps/web/content/docs/error-factory.mdx new file mode 100644 index 0000000..444cbbc --- /dev/null +++ b/apps/web/content/docs/error-factory.mdx @@ -0,0 +1,138 @@ +--- +title: Error Factory +description: Create custom error types with the error() function in @deessejs/errors. +--- + +The `error()` function is the primary way to define error types in @deessejs/errors. It returns a factory function that creates error instances with your specified configuration. + +## Basic Usage + +At minimum, an error factory requires a name. This name identifies the error type and appears in the `name` property of instances. + +```ts basic.ts +import { error } from '@deessejs/errors'; + +const NotFoundError = error({ + name: 'NotFoundError', +}); + +const err = NotFoundError({}); +console.log(err.name); // "NotFoundError" +console.log(err.message); // "NotFoundError" +``` + +When you provide only a name, the message defaults to the same value. This is useful for simple errors where the name itself is descriptive enough. + +## Custom Messages + +You can provide a custom message that gives more context about what went wrong: + +```ts custom-message.ts +const ValidationError = error({ + name: 'ValidationError', + message: 'Validation failed', +}); + +const err = ValidationError({}); +console.log(err.message); // "Validation failed" +``` + +## Creating Instances with Fields + +The real power of error factories comes from attaching structured data. You can define the type of fields your error accepts using a generic parameter: + +```ts with-fields.ts +const UserError = error<{ userId: string; reason: string }>({ + name: 'UserError', +}); + +const err = UserError({ userId: 'usr_123', reason: 'not found' }); +console.log(err.fields.userId); // "usr_123" +console.log(err.fields.reason); // "not found" +``` + +The fields object is completely flexible. You can include any data that helps describe the error: IDs, timestamps, values that caused the error, or any other context. + +## Using Fields with Schema Validation + +For more robust error definitions, you can provide a Standard Schema (compatible with Zod, Valibot, or ArkType) to validate fields at creation time: + +```ts with-schema.ts +import { z } from 'zod'; +import { error } from '@deessejs/errors'; + +const ValidationError = error({ + name: 'ValidationError', + fields: z.object({ + field: z.string(), + reason: z.string(), + }), +}); + +const err = ValidationError({ field: 'email', reason: 'invalid format' }); +``` + +When fields don't match the schema, an error is thrown during error creation. This helps catch configuration mistakes early. + +## Factory Properties + +The function returned by `error()` has several useful properties attached to it: + +```ts properties.ts +const AppError = error({ name: 'AppError' }); + +console.log(AppError.name); // "AppError" +console.log(AppError.schema); // undefined (no schema defined) +console.log(AppError.inherits); // undefined (no parent) +``` + +These properties are useful for introspection and for the `is()` function to check inheritance relationships. + +## Reusing Error Factories + +Error factories are designed to be created once and reused throughout your application. Define them at module level so they're available everywhere: + +```ts definitions.ts +// errors/index.ts +import { error } from '@deessejs/errors'; + +export const NotFoundError = error({ + name: 'NotFoundError', + message: 'Resource not found', +}); + +export const ValidationError = error({ + name: 'ValidationError', + message: 'Validation failed', +}); + +export const NetworkError = error({ + name: 'NetworkError', + message: 'Network request failed', +}); +``` + +Then import and use them wherever needed: + +```ts usage.ts +import { raise } from '@deessejs/errors'; +import { NotFoundError } from './errors'; + +if (!resource) { + raise(NotFoundError({})); +} +``` + +## See Also + + + + Understand the properties and methods available on error instances. + + + Use the is() function to check error types. + + + Learn more about field definitions and schema validation. + + \ No newline at end of file diff --git a/apps/web/content/docs/error-instance.mdx b/apps/web/content/docs/error-instance.mdx new file mode 100644 index 0000000..3032504 --- /dev/null +++ b/apps/web/content/docs/error-instance.mdx @@ -0,0 +1,150 @@ +--- +title: Error Instance +description: Understand the properties and methods available on error instances created by @deessejs/errors. +--- + +When you create an error using an error factory, you get an error instance. This instance extends the native JavaScript `Error` object with additional properties and methods specific to @deessejs/errors. + +## Standard Properties + +Error instances include all standard `Error` properties: + +```ts properties.ts +import { error } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError', message: 'Something went wrong' }); +const err = AppError({}); + +console.log(err.name); // "AppError" +console.log(err.message); // "Something went wrong" +console.log(err.stack); // Stack trace string +console.log(err instanceof Error); // true +``` + +These properties behave exactly like native errors, so they work with all existing error handling code and tools. + +## The fields Property + +The `fields` property contains the data you passed when creating the error. This is where you store structured context about what went wrong. + +```ts fields.ts +const ValidationError = error<{ field: string; value: unknown }>({ + name: 'ValidationError', + message: 'Invalid value', +}); + +const err = ValidationError({ field: 'email', value: 'not-an-email' }); + +console.log(err.fields.field); // "email" +console.log(err.fields.value); // "not-an-email" +``` + +The fields object is typed based on what you defined in the error factory, providing full TypeScript support for accessing these values. + +## The from() Method + +The `from()` method chains another error as the cause of the current error. This is how you preserve the full context of what led to an error. + +```ts from-method.ts +import { error } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +const validationErr = ValidationError({ field: 'email' }); +const appErr = AppError({}); + +// Chain the validation error as a cause +appErr.from(validationErr); + +console.log(appErr.cause === validationErr); // true +console.log(appErr.causes.length); // 1 +``` + +The `from()` method returns the error instance, allowing you to chain multiple calls or combine it with other operations. + +## The cause Property + +The `cause` property holds the direct cause of the error — the error passed to the most recent `from()` call. + +```ts cause.ts +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +const appErr = AppError({}); +appErr.from(ValidationError({ field: 'name' })); + +console.log(appErr.cause instanceof ValidationError); // true +``` + +When no cause has been set, `cause` is `null`. + +## The causes Array + +The `causes` array contains the entire chain of errors, ordered from most recent to oldest. This gives you the full history of what happened. + +```ts causes-array.ts +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +const appErr = AppError({}); +appErr.from(ValidationError({ field: 'email' })); +appErr.from(new Error('Database connection failed')); + +console.log(appErr.causes.length); // 2 +console.log(appErr.causes[0].name); // "ValidationError" (most recent) +console.log(appErr.causes[1].message); // "Database connection failed" (oldest) +``` + +When you chain multiple errors, the new cause is added to the front of the array, keeping chronological order. + +## The notes Property + +The `notes` property allows you to attach additional context to an error after it's created. This is useful for adding debugging information in catch blocks. + +```ts notes.ts +const AppError = error({ name: 'AppError' }); +const err = AppError({}); + +// Add context notes +err.notes.push('Caught in user controller'); +err.notes.push('User ID was usr_123'); + +console.log(err.notes); +// ["Caught in user controller", "User ID was usr_123"] +``` + +Notes are stored as an array of strings and can be modified at any time. + +## The context Property + +The `context` property holds additional data that was injected into the error. This is useful for framework integrations or middleware that need to attach extra information. + +```ts context.ts +const AppError = error({ name: 'AppError' }); +const err = AppError({}); + +// Attach context data +err.context = { + requestId: 'req_abc123', + timestamp: new Date().toISOString(), +}; + +console.log(err.context.requestId); // "req_abc123" +``` + +When no context has been set, `context` is `null`. + +## See Also + + + + Create error types with the error() function. + + + Learn more about chaining errors with from(). + + + Check error types using the is() function. + + \ No newline at end of file diff --git a/apps/web/content/docs/fields-schema.mdx b/apps/web/content/docs/fields-schema.mdx new file mode 100644 index 0000000..d64e1f8 --- /dev/null +++ b/apps/web/content/docs/fields-schema.mdx @@ -0,0 +1,177 @@ +--- +title: Fields and Schema +description: Define structured fields and validation schemas for error types in @deessejs/errors. +--- + +Errors become much more useful when they carry structured data about what went wrong. @deessejs/errors lets you define fields on your error factories and optionally validate them using Standard Schema compatible libraries. + +## Defining Fields + +When you create an error factory, you can specify a generic type that defines the shape of the fields object: + +```ts basic.ts +import { error } from '@deessejs/errors'; + +const ValidationError = error<{ + field: string; + reason: string; + value?: unknown; +}>({ + name: 'ValidationError', + message: 'Validation failed', +}); +``` + +Now when you create an error instance, TypeScript ensures you provide the required fields: + +```ts usage.ts +// All required fields provided +const err = ValidationError({ + field: 'email', + reason: 'invalid format', +}); + +// Optional field omitted (valid) +const err2 = ValidationError({ + field: 'email', + reason: 'invalid format', + value: 'not-an-email', +}); +``` + +The fields object is always accessible on the error instance, giving you a consistent place to look for error context. + +## Accessing Field Values + +Once an error is caught, you can access its fields to provide meaningful feedback or logging: + +```ts access.ts +import { error } from '@deessejs/errors'; +import { raise } from '@deessejs/errors'; + +const ValidationError = error<{ field: string; reason: string }>({ + name: 'ValidationError', + message: 'Validation failed', +}); + +try { + raise(ValidationError({ field: 'email', reason: 'not a valid email address' })); +} catch (err) { + // TypeScript knows the shape of fields + const fields = (err as { fields: { field: string; reason: string } }).fields; + console.log(`Error in field "${fields.field}": ${fields.reason}`); + // Output: Error in field "email": not a valid email address +} +``` + +For full type inference in catch blocks, use the `is()` function with type narrowing. + +## Schema Validation + +For production applications, you may want to validate field values when errors are created. @deessejs/errors supports Standard Schema, which means you can use any compatible validation library like Zod, Valibot, or ArkType. + +### Using Zod + +```ts zod.ts +import { z } from 'zod'; +import { error } from '@deessejs/errors'; + +const ValidationError = error({ + name: 'ValidationError', + fields: z.object({ + field: z.string().min(1), + reason: z.string().min(1), + }), +}); + +// Valid error creation +const err = ValidationError({ field: 'email', reason: 'invalid' }); + +// Invalid creation - throws ZodError +try { + ValidationError({ field: '', reason: 'invalid' }); +} catch (e) { + console.log(e instanceof Error); // true +} +``` + +### Using Valibot + +```ts valibot.ts +import { valibot } from 'fumadocs-core/source'; +import { error } from '@deessejs/errors'; + +const ValidationError = error({ + name: 'ValidationError', + fields: valibot({ + field: 'string', + reason: 'string', + }), +}); +``` + +### Using ArkType + +```ts arktype.ts +import { error } from '@deessejs/errors'; +import { t } from 'arktype'; + +const ValidationError = error({ + name: 'ValidationError', + fields: t.type({ + field: 'string', + reason: 'string', + }), +}); +``` + +## Why Use Schema Validation? + +Schema validation in error factories provides several benefits: + +**Early error detection** — Configuration mistakes in your error definitions are caught immediately rather than causing subtle bugs later. + +**Self-documenting code** — The schema serves as documentation for what data each error type expects. + +**Consistent data** — All errors of a given type have the same structure, making logging and monitoring easier. + +## Common Field Patterns + +Here are some common patterns for error fields: + +```ts patterns.ts +// Database errors +const DatabaseError = error<{ + query: string; + table?: string; + code?: string; +}>({ name: 'DatabaseError' }); + +// API errors +const ApiError = error<{ + endpoint: string; + statusCode: number; + response?: unknown; +}>({ name: 'ApiError' }); + +// Validation errors +const ValidationError = error<{ + field: string; + reason: string; + value?: unknown; + constraints?: Record; +}>({ name: 'ValidationError' }); +``` + +Design your fields to capture what's useful for debugging and logging, not just what's required to identify the error. + +## See Also + + + + Create error types with the error() function. + + + See practical examples of error definitions for common scenarios. + + \ No newline at end of file diff --git a/apps/web/content/docs/from-method.mdx b/apps/web/content/docs/from-method.mdx new file mode 100644 index 0000000..52a2f82 --- /dev/null +++ b/apps/web/content/docs/from-method.mdx @@ -0,0 +1,136 @@ +--- +title: Exception Chaining +description: Chain errors together using the from() method to preserve the full context of what went wrong. +--- + +When an error occurs as a result of another error, it's important to preserve that relationship. @deessejs/errors provides the `from()` method for exactly this purpose, creating chains that maintain the full history of failures. + +## Basic Chaining + +The `from()` method attaches a cause error to the current error. Call it on an error instance to establish the relationship: + +```ts basic.ts +import { error } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +// Create the original error +const validationErr = ValidationError({ field: 'email' }); + +// Create the wrapping error +const appErr = AppError({}); + +// Chain the validation error as the cause +appErr.from(validationErr); + +console.log(appErr.cause === validationErr); // true +``` + +The `from()` method returns the error instance, so you can chain method calls or use it inline. + +## Multiple Causes + +A single error can have multiple causes in its chain. Each call to `from()` adds a new cause to the front of the chain: + +```ts multiple.ts +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +const appErr = AppError({}); + +// Add causes in order +appErr.from(ValidationError({ field: 'email' })); +appErr.from(new Error('Database connection failed')); + +// Most recent cause is first +console.log(appErr.causes.length); // 2 +console.log(appErr.causes[0].name); // "ValidationError" +console.log(appErr.causes[1].message); // "Database connection failed" +``` + +The order is always newest first, which makes sense when reading error chains — the most recent cause is what led directly to the current error. + +## Chaining with raise() + +You can combine `from()` with `raise()` for concise error handling: + +```ts with-raise.ts +import { error, raise } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +try { + // Something throws a validation error + throw ValidationError({ field: 'email' }); +} catch (err) { + // Wrap it in an application error + raise(AppError({}).from(err)); +} +``` + +Since `from()` returns the error instance, you can write this more compactly: + +```ts compact.ts +import { error, raise } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +try { + throw ValidationError({ field: 'email' }); +} catch (err) { + raise(AppError({}).from(err)); +} +``` + +## Accessing the Full Chain + +After building an error chain, you can access all causes through the `causes` array or use the `causes()` helper function for more robust access: + +```ts access.ts +import { error, causes } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ name: 'ValidationError' }); + +const appErr = AppError({}); +appErr.from(ValidationError({ field: 'email' })); +appErr.from(new Error('Connection timeout')); + +// Using the causes array directly +console.log(appErr.causes.length); // 2 + +// Using the causes() helper (handles non-@deessejs/errors errors) +const chain = causes(appErr); +console.log(chain.length); // 2 +``` + +The `causes()` function is particularly useful when working with errors from different sources, as it gracefully handles cases where the `causes` array might not exist. + +## Why Chain Errors? + +Exception chaining serves several important purposes: + +**Debugging** — When something goes wrong in production, the full chain tells the complete story of what happened, not just the final error. + +**Logging** — You can log or monitor the entire chain to understand patterns in failures across your application. + +**User experience** — Show users a meaningful error message while keeping technical details for diagnostics. + +**Service boundaries** — When errors cross async boundaries or service calls, chaining preserves context that would otherwise be lost. + +## See Also + + + + Learn about all properties available on error instances. + + + Traverse error chains programmatically. + + + Check error types in catch blocks. + + \ No newline at end of file diff --git a/apps/web/content/docs/index.mdx b/apps/web/content/docs/index.mdx index 1ede18e..f0137f8 100644 --- a/apps/web/content/docs/index.mdx +++ b/apps/web/content/docs/index.mdx @@ -1,13 +1,106 @@ --- -title: Hello World -description: Your first document +title: Getting Started +description: Get started with @deessejs/errors, a TypeScript error handling library with exception chaining and hierarchical inheritance. --- -Welcome to the docs! You can start writing documents in `/content/docs`. +@deessejs/errors is a TypeScript library that reimagines error handling in JavaScript. Inspired by Python's exception system, it provides exception chaining, hierarchical inheritance, and rich error semantics through a function-based API. -## What is Next? +This library is particularly useful when you need to: + +- Preserve the full context of errors across async boundaries and service calls +- Organize errors in meaningful hierarchies that reflect your domain +- Attach structured data and metadata to errors for better debugging + +## Quick Start + +The quickest way to understand how @deessejs/errors works is to see it in action. Here's a basic example that demonstrates creating typed errors and chaining them together. + +```ts index.ts +import { error, raise } from '@deessejs/errors'; + +// Define your error types +const ValidationError = error({ + name: 'ValidationError', + message: 'Validation failed', +}); + +// Use the factory to create error instances +const validationErr = ValidationError({}); +console.log(validationErr.message); // "Validation failed" +``` + +For catching and handling errors, you can use the standard try/catch syntax or the library's `raise()` function: + +```ts handling.ts +try { + raise(ValidationError({})); +} catch (err) { + console.log(err.name); // "ValidationError" + console.log(err instanceof Error); // true +} +``` + +## Key Features + +### Exception Chaining + +When an error occurs as a result of another error, you can preserve that relationship using the `.from()` method. This creates a chain that maintains the full history of what went wrong. + +```ts chaining.ts +const validationErr = ValidationError({}); +const appErr = error({ name: 'AppError' })(); + +// Chain the validation error as the cause +appErr.from(validationErr); + +console.log(appErr.cause === validationErr); // true +console.log(appErr.causes.length); // 1 +``` + +### Hierarchical Inheritance + +Errors can inherit from other errors, creating hierarchies that are useful for organization and type checking. A child error can check against its parent types. + +```ts inheritance.ts +import { error, is } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ + name: 'ValidationError', + inherits: AppError, +}); + +const err = ValidationError({}); + +// Check the error type +console.log(is(err, ValidationError)); // true +console.log(is(err, AppError)); // true (through inheritance) +``` + +### Message Templates + +You can define errors with message templates that include field placeholders. These are replaced at runtime with the data you provide. + +```ts templates.ts +const UserError = error<{ userId: string; reason: string }>({ + name: 'UserError', + message: 'User "{userId}" failed: {reason}', +}); + +const err = UserError({ userId: 'usr_123', reason: 'not found' }); +console.log(err.message); // "User "usr_123" failed: not found" +``` + +## See Also - - - + + Learn how to install and set up the library in your project. + + + Create custom error types with the error() function. + + + Chain errors together to preserve context. + + \ No newline at end of file diff --git a/apps/web/content/docs/installation.mdx b/apps/web/content/docs/installation.mdx new file mode 100644 index 0000000..caa28b1 --- /dev/null +++ b/apps/web/content/docs/installation.mdx @@ -0,0 +1,87 @@ +--- +title: Installation +description: Install @deessejs/errors in your TypeScript or JavaScript project. +--- + +Adding @deessejs/errors to your project is straightforward. The library is available on npm and supports all major package managers. + +## Install the Package + +Choose your preferred package manager and run the installation command: + +```bash +npm install @deessejs/errors +``` + +```bash +pnpm add @deessejs/errors +``` + +```bash +yarn add @deessejs/errors +``` + +```bash +bun add @deessejs/errors +``` + +## Requirements + +@deessejs/errors requires Node.js 18 or later. The library is written in TypeScript and ships with full type definitions, so no additional `@types` packages are needed. + +## Import the Library + +Once installed, you can import the core functions from the package: + +```ts main.ts +import { error, raise, is, causes } from '@deessejs/errors'; +``` + +You can also import specific functions if you prefer tree-shaking: + +```ts partial.ts +import { error } from '@deessejs/errors'; +import { is } from '@deessejs/errors'; +``` + +## TypeScript Configuration + +The library works out of the box with TypeScript. No special compiler options are required, but the following settings in your `tsconfig.json` will ensure the best experience: + +```json tsconfig.json +{ + "compilerOptions": { + "strict": true, + "moduleResolution": "bundler" + } +} +``` + +The `moduleResolution: "bundler"` option is recommended if you're using a modern bundler like Vite, webpack 5, or Turbopack. + +## Verify Installation + +To verify that the library is installed correctly, create a simple test file: + +```ts verify.ts +import { error } from '@deessejs/errors'; + +const TestError = error({ name: 'TestError' }); +const err = TestError({}); + +console.log(err.name); // "TestError" +console.log(err instanceof Error); // true +``` + +Run this file with Node.js or your bundler to confirm everything works. + +## See Also + + + + Return to the introduction and quick start guide. + + + Learn how to create custom error types. + + \ No newline at end of file diff --git a/apps/web/content/docs/message-templates.mdx b/apps/web/content/docs/message-templates.mdx new file mode 100644 index 0000000..9b819da --- /dev/null +++ b/apps/web/content/docs/message-templates.mdx @@ -0,0 +1,162 @@ +--- +title: Message Templates +description: Use message templates with field placeholders and modifiers in @deessejs/errors. +--- + +Message templates let you create dynamic error messages that include runtime values. This makes errors more informative and easier to debug, as the message contains specific context about what went wrong. + +## Basic Placeholders + +Wrap field names in curly braces to create placeholders in your message: + +```ts basic.ts +import { error } from '@deessejs/errors'; + +const ValidationError = error<{ field: string }>({ + name: 'ValidationError', + message: 'Field "{field}" is invalid', +}); + +const err = ValidationError({ field: 'email' }); +console.log(err.message); // "Field "email" is invalid" +``` + +The placeholder is replaced with the actual value from the fields object at runtime. + +## Multiple Placeholders + +You can include multiple placeholders in a single message: + +```ts multiple.ts +import { error } from '@deessejs/errors'; + +const FormatError = error<{ expected: string; actual: string }>({ + name: 'FormatError', + message: 'Expected {expected}, got {actual}', +}); + +const err = FormatError({ expected: 'number', actual: 'string' }); +console.log(err.message); // "Expected number, got string" +``` + +Each placeholder is replaced with its corresponding field value. + +## Modifiers + +Modifiers transform the placeholder value before insertion. They follow the field name with a colon. + +### Uppercase Modifier + +The `:upper` modifier converts the value to uppercase: + +```ts upper.ts +import { error } from '@deessejs/errors'; + +const UserCreatedError = error<{ userId: string }>({ + name: 'UserCreatedError', + message: 'Created user: {userId:upper}', +}); + +const err = UserCreatedError({ userId: 'usr_abc123' }); +console.log(err.message); // "Created user: USR_ABC123" +``` + +This is useful for displaying IDs or codes that should be consistently formatted. + +### Lowercase Modifier + +The `:lower` modifier converts the value to lowercase: + +```ts lower.ts +import { error } from '@deessejs/errors'; + +const PathError = error<{ path: string }>({ + name: 'PathError', + message: 'Invalid path: {path:lower}', +}); + +const err = PathError({ path: '/USERS/DATA' }); +console.log(err.message); // "Invalid path: /users/data" +``` + +### JSON Modifier + +The `:json` modifier serializes complex values to JSON: + +```ts json.ts +import { error } from '@deessejs/errors'; + +const DataError = error<{ data: { id: number; name: string } }>({ + name: 'DataError', + message: 'Invalid data: {data:json}', +}); + +const err = DataError({ data: { id: 1, name: 'test' } }); +console.log(err.message); // "Invalid data: {"id":1,"name":"test"}" +``` + +The JSON modifier is particularly useful for debugging when you need to see the full value. + +## Combining Modifiers + +Modifiers can be combined with other fields: + +```ts combined.ts +import { error } from '@deessejs/errors'; + +const ApiError = error<{ + method: string; + endpoint: string; + statusCode: number; +}>({ + name: 'ApiError', + message: '{method:upper} {endpoint} failed with status {statusCode}', +}); + +const err = ApiError({ + method: 'get', + endpoint: '/api/users', + statusCode: 404, +}); +console.log(err.message); // "GET /api/users failed with status 404" +``` + +## Missing Field Values + +If a placeholder references a field that wasn't provided, the placeholder is left unchanged in the message: + +```ts missing.ts +import { error } from '@deessejs/errors'; + +const ValidationError = error<{ field: string }>({ + name: 'ValidationError', + message: 'Field "{field}" is required', +}); + +const err = ValidationError({}); // field not provided +console.log(err.message); // "Field "{field}" is required" +``` + +This behavior is intentional — it helps you identify when expected data is missing. + +## Best Practices + +**Be specific** — Include enough context in the message to understand the error without looking at the code. + +**Keep messages readable** — While placeholders add information, don't overload the message with too many values. + +**Use modifiers appropriately** — Uppercase for codes and IDs, lowercase for paths, JSON for complex objects. + +## See Also + + + + Create error types with the error() function. + + + Define structured fields for errors. + + + Practical examples of error definitions. + + \ No newline at end of file diff --git a/apps/web/content/docs/meta.json b/apps/web/content/docs/meta.json new file mode 100644 index 0000000..e3d8de6 --- /dev/null +++ b/apps/web/content/docs/meta.json @@ -0,0 +1,23 @@ +{ + "title": "@deessejs/errors", + "pages": [ + "index", + "installation", + "---Core Concepts---", + "error-factory", + "error-instance", + "fields-schema", + "---Chaining---", + "from-method", + "causes-function", + "---Inheritance---", + "single-inheritance", + "multiple-inheritance", + "type-checking", + "---Advanced---", + "message-templates", + "---Reference---", + "api-reference", + "recipes" + ] +} \ No newline at end of file diff --git a/apps/web/content/docs/multiple-inheritance.mdx b/apps/web/content/docs/multiple-inheritance.mdx new file mode 100644 index 0000000..fcad330 --- /dev/null +++ b/apps/web/content/docs/multiple-inheritance.mdx @@ -0,0 +1,141 @@ +--- +title: Multiple Inheritance +description: Create errors that inherit from multiple parent error types in @deessejs/errors. +--- + +While single inheritance creates clear parent-child relationships, some errors naturally belong to multiple categories. @deessejs/errors supports multiple inheritance, allowing an error to inherit from several parent types simultaneously. + +## Basic Multiple Inheritance + +To inherit from multiple parents, pass an array of error factories to the `inherits` property: + +```ts basic.ts +import { error, is } from '@deessejs/errors'; + +const NetworkError = error({ name: 'NetworkError' }); +const StorageError = error({ name: 'StorageError' }); + +// Inherits from both NetworkError and StorageError +const NetworkStorageError = error({ + name: 'NetworkStorageError', + inherits: [NetworkError, StorageError], +}); +``` + +Now this error belongs to both categories in the hierarchy. + +## Type Checking with Multiple Parents + +When an error inherits from multiple parents, `is()` returns true for any of its parents: + +```ts checking.ts +const NetworkError = error({ name: 'NetworkError' }); +const StorageError = error({ name: 'StorageError' }); + +const NetworkStorageError = error({ + name: 'NetworkStorageError', + inherits: [NetworkError, StorageError], +}); + +const err = NetworkStorageError({}); + +console.log(is(err, NetworkStorageError)); // true +console.log(is(err, NetworkError)); // true +console.log(is(err, StorageError)); // true +``` + +This flexibility lets you categorize errors from multiple angles. + +## Practical Example + +Consider a caching layer that sits between the network and storage. It might fail in ways that are both network-related and storage-related: + +```ts example.ts +import { error, raise, is } from '@deessejs/errors'; + +const NetworkError = error({ name: 'NetworkError' }); +const StorageError = error({ name: 'StorageError' }); + +const CacheError = error({ + name: 'CacheError', + message: 'Cache operation failed', + inherits: [NetworkError, StorageError], +}); + +// Handler that deals with network issues +function handleNetworkIssue(err: unknown) { + if (is(err, NetworkError)) { + console.log('Network problem detected:', err.message); + } +} + +// Handler that deals with storage issues +function handleStorageIssue(err: unknown) { + if (is(err, StorageError)) { + console.log('Storage problem detected:', err.message); + } +} + +const cacheErr = CacheError({}); + +handleNetworkIssue(cacheErr); // Logs (through NetworkError inheritance) +handleStorageIssue(cacheErr); // Logs (through StorageError inheritance) +``` + +This pattern is useful for middleware and infrastructure code that needs to respond to errors based on their characteristics rather than their specific type. + +## Combining with Specific Error Data + +Multiple inheritance works seamlessly with typed fields: + +```ts with-fields.ts +import { error, is } from '@deessejs/errors'; + +const NetworkError = error<{ endpoint: string }>({ + name: 'NetworkError', + message: 'Network request to {endpoint} failed', +}); + +const StorageError = error<{ path: string }>({ + name: 'StorageError', + message: 'Storage operation at {path} failed', +}); + +const CacheError = error<{ key: string; operation: string }>({ + name: 'CacheError', + inherits: [NetworkError, StorageError], + message: 'Cache {operation} for key {key} failed', +}); + +// The error carries its own specific data +const err = CacheError({ key: 'user:123', operation: 'read' }); +console.log(err.fields.key); // "user:123" +console.log(err.message); // "Cache read for key user:123 failed" + +// But also inherits type checking for parents +console.log(is(err, NetworkError)); // true +console.log(is(err, StorageError)); // true +``` + +## When to Use Multiple Inheritance + +Multiple inheritance is appropriate when: + +**The error has multiple dimensions** — A timeout could be a network issue and a service issue simultaneously. + +**You're building middleware** — Libraries between the application and infrastructure often need to respond to multiple error categories. + +**You want flexible handling** — Code can catch errors by any of their characteristics, not just their exact type. + +Avoid using multiple inheritance just because you can. Prefer single inheritance for most errors, and only use multiple inheritance when the error genuinely belongs to multiple categories. + +## See Also + + + + Learn about basic parent-child error hierarchies. + + + Check error types including inherited ones. + + \ No newline at end of file diff --git a/apps/web/content/docs/recipes.mdx b/apps/web/content/docs/recipes.mdx new file mode 100644 index 0000000..fac0377 --- /dev/null +++ b/apps/web/content/docs/recipes.mdx @@ -0,0 +1,248 @@ +--- +title: Recipes +description: Practical examples and patterns for using @deessejs/errors in real applications. +--- + +This page collects common patterns and practical examples for using @deessejs/errors in your applications. These recipes demonstrate how to apply the library's features to solve real-world error handling problems. + +## Validation Errors + +Validation errors are one of the most common error types. They typically include the field that failed validation and a reason for the failure. + +```ts validation.ts +import { error, raise } from '@deessejs/errors'; +import { z } from 'zod'; + +// Define validation schema +const UserSchema = z.object({ + email: z.string().email(), + age: z.number().min(0).max(150), +}); + +// Create the error factory +const ValidationError = error<{ field: string; reason: string; value?: unknown }>({ + name: 'ValidationError', + message: 'Validation failed for field "{field}"', +}); + +// Validation function +function validateUser(data: unknown) { + const result = UserSchema.safeParse(data); + + if (!result.success) { + const issue = result.error.issues[0]; + raise( + ValidationError({ + field: issue.path.join('.'), + reason: issue.message, + value: (result.error as { value?: unknown }).value, + }) + ); + } + + return result.data; +} +``` + +When validation fails, you get an error with specific information about which field failed and why. + +## Network Errors + +Network errors benefit from including the endpoint and any relevant request data. + +```ts network.ts +import { error, raise } from '@deessejs/errors'; + +const NetworkError = error<{ + endpoint: string; + method?: string; + statusCode?: number; +}>({ + name: 'NetworkError', + message: 'Request to {endpoint} failed', +}); + +async function fetchWithError(url: string) { + const response = await fetch(url); + + if (!response.ok) { + raise( + NetworkError({ + endpoint: url, + statusCode: response.status, + }) + ); + } + + return response.json(); +} +``` + +You can extend this pattern to include request headers, body, or timing information. + +## Database Errors + +Database errors typically need the query that failed and any relevant identifiers. + +```ts database.ts +import { error, raise } from '@deessejs/errors'; + +const DatabaseError = error<{ + operation: string; + table?: string; + query?: string; +}>({ + name: 'DatabaseError', + message: 'Database {operation} failed', +}); + +// Wrap database operations +async function executeQuery(query: string, table: string) { + try { + return await db.query(query); + } catch (err) { + raise( + DatabaseError({ + operation: 'query', + table, + query, + }).from(err as Error) + ); + } +} +``` + +Chaining the original error preserves the full context of what went wrong. + +## Error Handling Middleware + +Create a reusable error handler that processes errors based on their type. + +```ts handler.ts +import { error, raise, is, causes } from '@deessejs/errors'; +import { ValidationError, NetworkError, DatabaseError } from './errors'; + +export function handleApiError(err: unknown) { + // Log the full error chain + console.error('Error chain:', causes(err).map((e) => e.message)); + + if (is(err, ValidationError)) { + return { + status: 400, + message: `Invalid input: ${err.fields.field}`, + }; + } + + if (is(err, NetworkError)) { + return { + status: 503, + message: 'External service unavailable', + }; + } + + if (is(err, DatabaseError)) { + return { + status: 500, + message: 'Database operation failed', + }; + } + + // Generic fallback + return { + status: 500, + message: 'Internal server error', + }; +} +``` + +This pattern centralizes error handling logic and ensures consistent responses. + +## Service Layer Error Wrapping + +When building services that call other services, wrap errors to add context while preserving the cause chain. + +```ts service.ts +import { error, raise } from '@deessejs/errors'; +import { NetworkError, UserNotFoundError } from './errors'; + +const AppError = error({ name: 'AppError' }); + +class UserService { + async getUser(userId: string) { + try { + const user = await this.fetchUser(userId); + if (!user) { + raise(UserNotFoundError({ userId })); + } + return user; + } catch (err) { + // Wrap any error from lower layers + raise(AppError({}).from(err as Error)); + } + } + + private async fetchUser(userId: string) { + // Implementation + return null; + } +} +``` + +The caller gets both the context of the current operation and the underlying cause. + +## Hierarchical Error Categories + +Create a hierarchy that lets you handle errors at different levels of granularity. + +```ts hierarchy.ts +import { error, is } from '@deessejs/errors'; + +// Top level +const AppError = error({ name: 'AppError' }); + +// Domain level +const UserError = error({ name: 'UserError', inherits: AppError }); +const ProductError = error({ name: 'ProductError', inherits: AppError }); + +// Specific errors +const UserNotFoundError = error<{ userId: string }>({ + name: 'UserNotFoundError', + inherits: UserError, +}); + +const UserPermissionError = error<{ userId: string; action: string }>({ + name: 'UserPermissionError', + inherits: UserError, +}); + +function handleError(err: unknown) { + if (is(err, AppError)) { + // Catches everything + } + if (is(err, UserError)) { + // Catches UserNotFoundError, UserPermissionError + } + if (is(err, UserNotFoundError)) { + // Catches only user not found errors + } +} +``` + +This lets you write both broad handlers and specific ones depending on the situation. + +## See Also + + + + Create custom error types. + + + Build error hierarchies. + + + Handle errors by type. + + + Preserve error context. + + \ No newline at end of file diff --git a/apps/web/content/docs/single-inheritance.mdx b/apps/web/content/docs/single-inheritance.mdx new file mode 100644 index 0000000..5903599 --- /dev/null +++ b/apps/web/content/docs/single-inheritance.mdx @@ -0,0 +1,130 @@ +--- +title: Single Inheritance +description: Create error hierarchies with single inheritance in @deessejs/errors. +--- + +Error hierarchies help organize errors by domain and concern. @deessejs/errors supports single inheritance, where one error type can inherit from another, creating a parent-child relationship. + +## Basic Inheritance + +To create an error that inherits from another, use the `inherits` property when defining the error factory: + +```ts basic.ts +import { error, is } from '@deessejs/errors'; + +// Base error for the application +const AppError = error({ name: 'AppError' }); + +// Specific error that inherits from AppError +const ValidationError = error({ + name: 'ValidationError', + inherits: AppError, +}); +``` + +Now `ValidationError` is a child of `AppError` in the hierarchy. You can verify this relationship using the `is()` function. + +## Type Checking with Inheritance + +When an error inherits from another, `is()` returns true for both the error itself and all its ancestors: + +```ts checking.ts +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ + name: 'ValidationError', + inherits: AppError, +}); + +const err = ValidationError({ field: 'email' }); + +console.log(is(err, ValidationError)); // true +console.log(is(err, AppError)); // true (through inheritance) +``` + +This is powerful for error handling — you can write a catch block for `AppError` and it will catch all errors that inherit from it, including `ValidationError`, `DatabaseError`, and any other descendants. + +## Practical Example + +Here's how inheritance helps in a real application: + +```ts example.ts +import { error, raise, is } from '@deessejs/errors'; + +// Base errors +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ + name: 'ValidationError', + inherits: AppError, +}); +const NetworkError = error({ + name: 'NetworkError', + inherits: AppError, +}); +const DatabaseError = error({ + name: 'DatabaseError', + inherits: AppError, +}); + +// Error handler that catches all application errors +function handleError(err: unknown) { + if (is(err, AppError)) { + // Catches ValidationError, NetworkError, DatabaseError, etc. + console.log(`App error: ${err.name}`); + } +} + +handleError(ValidationError({ field: 'email' })); // Logs +handleError(NetworkError({ url: '/api' })); // Logs +``` + +This pattern lets you handle errors at different levels of granularity depending on your needs. + +## Inheriting with Message Templates + +You can combine inheritance with message templates for powerful error definitions: + +```ts with-message.ts +import { error, is } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); + +const ValidationError = error<{ field: string }>({ + name: 'ValidationError', + inherits: AppError, + message: 'Field "{field}" is invalid', +}); + +const err = ValidationError({ field: 'email' }); +console.log(err.message); // "Field "email" is invalid" +console.log(is(err, AppError)); // true +``` + +## Accessing Parent Information + +The `inherits` property on the error factory lets you access parent types programmatically: + +```ts parents.ts +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ + name: 'ValidationError', + inherits: AppError, +}); + +console.log(ValidationError.inherits === AppError); // true +``` + +This is useful for building introspection tools or generating documentation automatically. + +## See Also + + + + Learn how errors can inherit from multiple parents. + + + Check error types with the is() function. + + + See the complete picture of how errors work together. + + \ No newline at end of file diff --git a/apps/web/content/docs/test.mdx b/apps/web/content/docs/test.mdx deleted file mode 100644 index f475f4a..0000000 --- a/apps/web/content/docs/test.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Components -description: Components ---- - -## Code Block - -```js -console.log('Hello World'); -``` - -## Cards - - - - - diff --git a/apps/web/content/docs/type-checking.mdx b/apps/web/content/docs/type-checking.mdx new file mode 100644 index 0000000..50e73d5 --- /dev/null +++ b/apps/web/content/docs/type-checking.mdx @@ -0,0 +1,168 @@ +--- +title: Type Checking +description: Use the is() function to check error types and inheritance relationships in @deessejs/errors. +--- + +The `is()` function is your primary tool for checking what kind of error you're dealing with. It works with @deessejs/errors instances, native JavaScript errors, and respects the full inheritance hierarchy. + +## Basic Type Checking + +Pass an error and an error type to `is()` to check if the error is an instance of that type: + +```ts basic.ts +import { error, is } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ + name: 'ValidationError', + inherits: AppError, +}); + +const err = ValidationError({ field: 'email' }); + +console.log(is(err, ValidationError)); // true +console.log(is(err, AppError)); // true (through inheritance) +``` + +The function returns `true` if the error matches the type directly or inherits from it. + +## Using is() in Catch Blocks + +The most common use case is inside catch blocks where you need to handle different error types: + +```ts catch-block.ts +import { error, raise, is } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ + name: 'ValidationError', + inherits: AppError, +}); +const NetworkError = error({ + name: 'NetworkError', + inherits: AppError, +}); + +function handleError(err: unknown) { + if (is(err, ValidationError)) { + console.log(`Validation failed: ${err.fields?.field}`); + } else if (is(err, NetworkError)) { + console.log(`Network error: ${err.fields?.endpoint}`); + } else if (is(err, AppError)) { + console.log(`General app error: ${err.message}`); + } +} +``` + +TypeScript narrows the type inside each branch when using `is()`, giving you access to the correct fields. + +## Working with Native Errors + +The `is()` function also works with native JavaScript errors: + +```ts native.ts +import { error, is } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); + +try { + JSON.parse('invalid json'); +} catch (err) { + if (is(err, SyntaxError)) { + console.log('JSON parsing failed'); + } else if (is(err, AppError)) { + console.log('Application error'); + } +} +``` + +This lets you mix @deessejs/errors with standard JavaScript error handling seamlessly. + +## Type Narrowing + +When `is()` returns true, TypeScript knows the error is of that specific type. This enables type-safe access to fields: + +```ts type-safety.ts +import { error, is } from '@deessejs/errors'; + +const ValidationError = error<{ field: string; reason: string }>({ + name: 'ValidationError', +}); + +function processError(err: unknown) { + if (is(err, ValidationError)) { + // TypeScript knows err is ValidationError + // So we can safely access fields + console.log(`Field: ${err.fields.field}`); + console.log(`Reason: ${err.fields.reason}`); + } +} +``` + +Without the `is()` check, TypeScript would report an error because `err` is `unknown`. + +## Multiple Type Checks + +You can check for multiple types in sequence: + +```ts multiple.ts +import { error, is } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ + name: 'ValidationError', + inherits: AppError, +}); +const NetworkError = error({ + name: 'NetworkError', + inherits: AppError, +}); + +function categorize(err: unknown): string { + if (is(err, NetworkError)) return 'network'; + if (is(err, ValidationError)) return 'validation'; + if (is(err, AppError)) return 'app'; + return 'unknown'; +} +``` + +Order your checks from most specific to least specific to ensure the correct handler is triggered. + +## Why Not Use instanceof? + +Native JavaScript `instanceof` doesn't work with @deessejs/errors errors because they're created with a factory function rather than a class. The `is()` function bridges this gap and adds support for inheritance checking. + +```ts comparison.ts +import { error, is } from '@deessejs/errors'; + +const AppError = error({ name: 'AppError' }); +const ValidationError = error({ + name: 'ValidationError', + inherits: AppError, +}); + +const err = ValidationError({}); + +// This works +console.log(is(err, ValidationError)); // true +console.log(is(err, AppError)); // true + +// This doesn't work for @deessejs/errors +console.log(err instanceof ValidationError); // false +``` + +Always use `is()` instead of `instanceof` when working with @deessejs/errors errors. + +## See Also + + + + Learn how error hierarchies work. + + + Errors that inherit from multiple parents. + + + Properties available on error instances. + + \ No newline at end of file From 51a9e001b77eeca375263550e11b6935fda4237b Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 2 Jun 2026 11:26:06 +0200 Subject: [PATCH 2/7] docs: documentation structure (ready for review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/claude-code) From dc697e8b2832fc959baf3bd93c450025f605acf1 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 2 Jun 2026 11:29:04 +0200 Subject: [PATCH 3/7] vercel: add config for monorepo build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specify build command, output directory, and install command to work with Vercel using repo root as root directory. 🤖 Generated with [Claude Code](https://claude.ai/claude-code) --- vercel.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 vercel.json diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..c11e633 --- /dev/null +++ b/vercel.json @@ -0,0 +1,5 @@ +{ + "buildCommand": "pnpm --filter web build", + "outputDirectory": "apps/web/.next", + "installCommand": "pnpm install" +} \ No newline at end of file From 65d125bfc834aa90e8aa109cb23586731ab701b5 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 2 Jun 2026 11:32:49 +0200 Subject: [PATCH 4/7] docs: update app name and GitHub repo config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/claude-code) --- apps/web/src/lib/shared.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/web/src/lib/shared.ts b/apps/web/src/lib/shared.ts index 812926e..f4ae214 100644 --- a/apps/web/src/lib/shared.ts +++ b/apps/web/src/lib/shared.ts @@ -1,11 +1,10 @@ -export const appName = 'My App'; +export const appName = '@deessejs/errors'; export const docsRoute = '/docs'; export const docsImageRoute = '/og/docs'; export const docsContentRoute = '/llms.mdx/docs'; -// fill this with your actual GitHub info, for example: export const gitConfig = { - user: 'fuma-nama', - repo: 'fumadocs', + user: 'nesalia-inc', + repo: 'errors', branch: 'main', }; From c2cceb226e9b3bc6b0b76ce418f8d3fdf9c9e766 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 2 Jun 2026 11:36:29 +0200 Subject: [PATCH 5/7] docs: apply black theme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/claude-code) --- apps/web/src/app/global.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/global.css b/apps/web/src/app/global.css index f86f3c9..a127bc3 100644 --- a/apps/web/src/app/global.css +++ b/apps/web/src/app/global.css @@ -1,5 +1,5 @@ @import 'tailwindcss'; -@import 'fumadocs-ui/css/neutral.css'; +@import 'fumadocs-ui/css/black.css'; @import 'fumadocs-ui/css/preset.css'; html { From 7abaef58331094f89d74ee29065b603075fb90f1 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 2 Jun 2026 11:43:27 +0200 Subject: [PATCH 6/7] docs: add title to all code blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/claude-code) --- apps/web/content/docs/api-reference.mdx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/web/content/docs/api-reference.mdx b/apps/web/content/docs/api-reference.mdx index 2cd7041..52b5e4e 100644 --- a/apps/web/content/docs/api-reference.mdx +++ b/apps/web/content/docs/api-reference.mdx @@ -9,7 +9,7 @@ This page documents all public exports from @deessejs/errors. Use this as a comp Creates an error factory function for defining typed, structured errors. -```ts +```ts title="${f%.mdx}.ts" const errorFactory = error(config) ``` @@ -35,7 +35,7 @@ An `ErrorFactory` function that creates error instances. The factory has these p ### Example -```ts +```ts title="${f%.mdx}.ts" import { error } from '@deessejs/errors'; const ValidationError = error<{ field: string }>({ @@ -53,7 +53,7 @@ const err = ValidationError({ field: 'email' }); Throws an error instance. This is the primary mechanism for throwing errors in @deessejs/errors. -```ts +```ts title="${f%.mdx}.ts" raise(error: ErrorInstance): never ``` @@ -69,7 +69,7 @@ raise(error: ErrorInstance): never ### Example -```ts +```ts title="${f%.mdx}.ts" import { error, raise } from '@deessejs/errors'; const ValidationError = error({ name: 'ValidationError' }); @@ -83,7 +83,7 @@ raise(ValidationError({})); Type guard function to check if an error is an instance of a specific error type. -```ts +```ts title="${f%.mdx}.ts" const result = is(error, ErrorType) ``` @@ -100,7 +100,7 @@ const result = is(error, ErrorType) ### Example -```ts +```ts title="${f%.mdx}.ts" import { error, is } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -121,7 +121,7 @@ is(err, AppError); // true (through inheritance) Returns all causes in an error chain. -```ts +```ts title="${f%.mdx}.ts" const chain = causes(error: unknown): Error[] ``` @@ -137,7 +137,7 @@ const chain = causes(error: unknown): Error[] ### Example -```ts +```ts title="${f%.mdx}.ts" import { error, causes } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); From 058485a3148a70650c6977e777c6dd56601a3f61 Mon Sep 17 00:00:00 2001 From: martyy-code Date: Tue, 2 Jun 2026 11:49:03 +0200 Subject: [PATCH 7/7] docs: fix code block title syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use title="filename" format for code block headers 🤖 Generated with [Claude Code](https://claude.ai/claude-code) --- apps/web/content/docs/api-reference.mdx | 16 ++++++++-------- apps/web/content/docs/causes-function.mdx | 10 +++++----- apps/web/content/docs/error-factory.mdx | 14 +++++++------- apps/web/content/docs/error-instance.mdx | 14 +++++++------- apps/web/content/docs/fields-schema.mdx | 14 +++++++------- apps/web/content/docs/from-method.mdx | 10 +++++----- apps/web/content/docs/index.mdx | 10 +++++----- apps/web/content/docs/installation.mdx | 8 ++++---- apps/web/content/docs/message-templates.mdx | 14 +++++++------- apps/web/content/docs/multiple-inheritance.mdx | 8 ++++---- apps/web/content/docs/recipes.mdx | 12 ++++++------ apps/web/content/docs/single-inheritance.mdx | 10 +++++----- apps/web/content/docs/type-checking.mdx | 12 ++++++------ 13 files changed, 76 insertions(+), 76 deletions(-) diff --git a/apps/web/content/docs/api-reference.mdx b/apps/web/content/docs/api-reference.mdx index 52b5e4e..3622e33 100644 --- a/apps/web/content/docs/api-reference.mdx +++ b/apps/web/content/docs/api-reference.mdx @@ -9,7 +9,7 @@ This page documents all public exports from @deessejs/errors. Use this as a comp Creates an error factory function for defining typed, structured errors. -```ts title="${f%.mdx}.ts" +```ts title="title="${f%.mdx}.ts"" const errorFactory = error(config) ``` @@ -35,7 +35,7 @@ An `ErrorFactory` function that creates error instances. The factory has these p ### Example -```ts title="${f%.mdx}.ts" +```ts title="title="${f%.mdx}.ts"" import { error } from '@deessejs/errors'; const ValidationError = error<{ field: string }>({ @@ -53,7 +53,7 @@ const err = ValidationError({ field: 'email' }); Throws an error instance. This is the primary mechanism for throwing errors in @deessejs/errors. -```ts title="${f%.mdx}.ts" +```ts title="title="${f%.mdx}.ts"" raise(error: ErrorInstance): never ``` @@ -69,7 +69,7 @@ raise(error: ErrorInstance): never ### Example -```ts title="${f%.mdx}.ts" +```ts title="title="${f%.mdx}.ts"" import { error, raise } from '@deessejs/errors'; const ValidationError = error({ name: 'ValidationError' }); @@ -83,7 +83,7 @@ raise(ValidationError({})); Type guard function to check if an error is an instance of a specific error type. -```ts title="${f%.mdx}.ts" +```ts title="title="${f%.mdx}.ts"" const result = is(error, ErrorType) ``` @@ -100,7 +100,7 @@ const result = is(error, ErrorType) ### Example -```ts title="${f%.mdx}.ts" +```ts title="title="${f%.mdx}.ts"" import { error, is } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -121,7 +121,7 @@ is(err, AppError); // true (through inheritance) Returns all causes in an error chain. -```ts title="${f%.mdx}.ts" +```ts title="title="${f%.mdx}.ts"" const chain = causes(error: unknown): Error[] ``` @@ -137,7 +137,7 @@ const chain = causes(error: unknown): Error[] ### Example -```ts title="${f%.mdx}.ts" +```ts title="title="${f%.mdx}.ts"" import { error, causes } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); diff --git a/apps/web/content/docs/causes-function.mdx b/apps/web/content/docs/causes-function.mdx index 49b42d4..19f479c 100644 --- a/apps/web/content/docs/causes-function.mdx +++ b/apps/web/content/docs/causes-function.mdx @@ -9,7 +9,7 @@ The `causes()` function provides a safe way to access the cause chain of any err Call `causes()` with an error to get its cause chain as an array: -```ts basic.ts +```ts title="basic.ts" import { error, causes } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -32,7 +32,7 @@ The returned array contains only the errors that were explicitly added using `fr The `causes()` function handles edge cases that direct property access doesn't: -```ts safety.ts +```ts title="safety.ts" import { causes } from '@deessejs/errors'; // Works with native errors (no causes property) @@ -54,7 +54,7 @@ This robustness makes `causes()` ideal for writing generic error handling code t You can iterate over the causes array to log or process each error in the chain: -```ts iterate.ts +```ts title="iterate.ts" import { error, causes } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -80,7 +80,7 @@ This is useful for building detailed error logs or monitoring dashboards that tr A common pattern is to build a summary message from the error chain: -```ts summary.ts +```ts title="summary.ts" import { error, causes } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -102,7 +102,7 @@ console.log(summary); Use `causes()` with `is()` to search for specific error types in the chain: -```ts combined.ts +```ts title="combined.ts" import { error, causes, is } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); diff --git a/apps/web/content/docs/error-factory.mdx b/apps/web/content/docs/error-factory.mdx index 444cbbc..b05aa1a 100644 --- a/apps/web/content/docs/error-factory.mdx +++ b/apps/web/content/docs/error-factory.mdx @@ -9,7 +9,7 @@ The `error()` function is the primary way to define error types in @deessejs/err At minimum, an error factory requires a name. This name identifies the error type and appears in the `name` property of instances. -```ts basic.ts +```ts title="basic.ts" import { error } from '@deessejs/errors'; const NotFoundError = error({ @@ -27,7 +27,7 @@ When you provide only a name, the message defaults to the same value. This is us You can provide a custom message that gives more context about what went wrong: -```ts custom-message.ts +```ts title="custom-message.ts" const ValidationError = error({ name: 'ValidationError', message: 'Validation failed', @@ -41,7 +41,7 @@ console.log(err.message); // "Validation failed" The real power of error factories comes from attaching structured data. You can define the type of fields your error accepts using a generic parameter: -```ts with-fields.ts +```ts title="with-fields.ts" const UserError = error<{ userId: string; reason: string }>({ name: 'UserError', }); @@ -57,7 +57,7 @@ The fields object is completely flexible. You can include any data that helps de For more robust error definitions, you can provide a Standard Schema (compatible with Zod, Valibot, or ArkType) to validate fields at creation time: -```ts with-schema.ts +```ts title="with-schema.ts" import { z } from 'zod'; import { error } from '@deessejs/errors'; @@ -78,7 +78,7 @@ When fields don't match the schema, an error is thrown during error creation. Th The function returned by `error()` has several useful properties attached to it: -```ts properties.ts +```ts title="properties.ts" const AppError = error({ name: 'AppError' }); console.log(AppError.name); // "AppError" @@ -92,7 +92,7 @@ These properties are useful for introspection and for the `is()` function to che Error factories are designed to be created once and reused throughout your application. Define them at module level so they're available everywhere: -```ts definitions.ts +```ts title="definitions.ts" // errors/index.ts import { error } from '@deessejs/errors'; @@ -114,7 +114,7 @@ export const NetworkError = error({ Then import and use them wherever needed: -```ts usage.ts +```ts title="usage.ts" import { raise } from '@deessejs/errors'; import { NotFoundError } from './errors'; diff --git a/apps/web/content/docs/error-instance.mdx b/apps/web/content/docs/error-instance.mdx index 3032504..a201866 100644 --- a/apps/web/content/docs/error-instance.mdx +++ b/apps/web/content/docs/error-instance.mdx @@ -9,7 +9,7 @@ When you create an error using an error factory, you get an error instance. This Error instances include all standard `Error` properties: -```ts properties.ts +```ts title="properties.ts" import { error } from '@deessejs/errors'; const AppError = error({ name: 'AppError', message: 'Something went wrong' }); @@ -27,7 +27,7 @@ These properties behave exactly like native errors, so they work with all existi The `fields` property contains the data you passed when creating the error. This is where you store structured context about what went wrong. -```ts fields.ts +```ts title="fields.ts" const ValidationError = error<{ field: string; value: unknown }>({ name: 'ValidationError', message: 'Invalid value', @@ -45,7 +45,7 @@ The fields object is typed based on what you defined in the error factory, provi The `from()` method chains another error as the cause of the current error. This is how you preserve the full context of what led to an error. -```ts from-method.ts +```ts title="from-method.ts" import { error } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -67,7 +67,7 @@ The `from()` method returns the error instance, allowing you to chain multiple c The `cause` property holds the direct cause of the error — the error passed to the most recent `from()` call. -```ts cause.ts +```ts title="cause.ts" const AppError = error({ name: 'AppError' }); const ValidationError = error({ name: 'ValidationError' }); @@ -83,7 +83,7 @@ When no cause has been set, `cause` is `null`. The `causes` array contains the entire chain of errors, ordered from most recent to oldest. This gives you the full history of what happened. -```ts causes-array.ts +```ts title="causes-array.ts" const AppError = error({ name: 'AppError' }); const ValidationError = error({ name: 'ValidationError' }); @@ -102,7 +102,7 @@ When you chain multiple errors, the new cause is added to the front of the array The `notes` property allows you to attach additional context to an error after it's created. This is useful for adding debugging information in catch blocks. -```ts notes.ts +```ts title="notes.ts" const AppError = error({ name: 'AppError' }); const err = AppError({}); @@ -120,7 +120,7 @@ Notes are stored as an array of strings and can be modified at any time. The `context` property holds additional data that was injected into the error. This is useful for framework integrations or middleware that need to attach extra information. -```ts context.ts +```ts title="context.ts" const AppError = error({ name: 'AppError' }); const err = AppError({}); diff --git a/apps/web/content/docs/fields-schema.mdx b/apps/web/content/docs/fields-schema.mdx index d64e1f8..d29fc0b 100644 --- a/apps/web/content/docs/fields-schema.mdx +++ b/apps/web/content/docs/fields-schema.mdx @@ -9,7 +9,7 @@ Errors become much more useful when they carry structured data about what went w When you create an error factory, you can specify a generic type that defines the shape of the fields object: -```ts basic.ts +```ts title="basic.ts" import { error } from '@deessejs/errors'; const ValidationError = error<{ @@ -24,7 +24,7 @@ const ValidationError = error<{ Now when you create an error instance, TypeScript ensures you provide the required fields: -```ts usage.ts +```ts title="usage.ts" // All required fields provided const err = ValidationError({ field: 'email', @@ -45,7 +45,7 @@ The fields object is always accessible on the error instance, giving you a consi Once an error is caught, you can access its fields to provide meaningful feedback or logging: -```ts access.ts +```ts title="access.ts" import { error } from '@deessejs/errors'; import { raise } from '@deessejs/errors'; @@ -72,7 +72,7 @@ For production applications, you may want to validate field values when errors a ### Using Zod -```ts zod.ts +```ts title="zod.ts" import { z } from 'zod'; import { error } from '@deessejs/errors'; @@ -97,7 +97,7 @@ try { ### Using Valibot -```ts valibot.ts +```ts title="valibot.ts" import { valibot } from 'fumadocs-core/source'; import { error } from '@deessejs/errors'; @@ -112,7 +112,7 @@ const ValidationError = error({ ### Using ArkType -```ts arktype.ts +```ts title="arktype.ts" import { error } from '@deessejs/errors'; import { t } from 'arktype'; @@ -139,7 +139,7 @@ Schema validation in error factories provides several benefits: Here are some common patterns for error fields: -```ts patterns.ts +```ts title="patterns.ts" // Database errors const DatabaseError = error<{ query: string; diff --git a/apps/web/content/docs/from-method.mdx b/apps/web/content/docs/from-method.mdx index 52a2f82..e2128ac 100644 --- a/apps/web/content/docs/from-method.mdx +++ b/apps/web/content/docs/from-method.mdx @@ -9,7 +9,7 @@ When an error occurs as a result of another error, it's important to preserve th The `from()` method attaches a cause error to the current error. Call it on an error instance to establish the relationship: -```ts basic.ts +```ts title="basic.ts" import { error } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -33,7 +33,7 @@ The `from()` method returns the error instance, so you can chain method calls or A single error can have multiple causes in its chain. Each call to `from()` adds a new cause to the front of the chain: -```ts multiple.ts +```ts title="multiple.ts" const AppError = error({ name: 'AppError' }); const ValidationError = error({ name: 'ValidationError' }); @@ -55,7 +55,7 @@ The order is always newest first, which makes sense when reading error chains You can combine `from()` with `raise()` for concise error handling: -```ts with-raise.ts +```ts title="with-raise.ts" import { error, raise } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -72,7 +72,7 @@ try { Since `from()` returns the error instance, you can write this more compactly: -```ts compact.ts +```ts title="compact.ts" import { error, raise } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -89,7 +89,7 @@ try { After building an error chain, you can access all causes through the `causes` array or use the `causes()` helper function for more robust access: -```ts access.ts +```ts title="access.ts" import { error, causes } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); diff --git a/apps/web/content/docs/index.mdx b/apps/web/content/docs/index.mdx index f0137f8..33ff3f4 100644 --- a/apps/web/content/docs/index.mdx +++ b/apps/web/content/docs/index.mdx @@ -15,7 +15,7 @@ This library is particularly useful when you need to: The quickest way to understand how @deessejs/errors works is to see it in action. Here's a basic example that demonstrates creating typed errors and chaining them together. -```ts index.ts +```ts title="index.ts" import { error, raise } from '@deessejs/errors'; // Define your error types @@ -31,7 +31,7 @@ console.log(validationErr.message); // "Validation failed" For catching and handling errors, you can use the standard try/catch syntax or the library's `raise()` function: -```ts handling.ts +```ts title="handling.ts" try { raise(ValidationError({})); } catch (err) { @@ -46,7 +46,7 @@ try { When an error occurs as a result of another error, you can preserve that relationship using the `.from()` method. This creates a chain that maintains the full history of what went wrong. -```ts chaining.ts +```ts title="chaining.ts" const validationErr = ValidationError({}); const appErr = error({ name: 'AppError' })(); @@ -61,7 +61,7 @@ console.log(appErr.causes.length); // 1 Errors can inherit from other errors, creating hierarchies that are useful for organization and type checking. A child error can check against its parent types. -```ts inheritance.ts +```ts title="inheritance.ts" import { error, is } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -81,7 +81,7 @@ console.log(is(err, AppError)); // true (through inheritance) You can define errors with message templates that include field placeholders. These are replaced at runtime with the data you provide. -```ts templates.ts +```ts title="templates.ts" const UserError = error<{ userId: string; reason: string }>({ name: 'UserError', message: 'User "{userId}" failed: {reason}', diff --git a/apps/web/content/docs/installation.mdx b/apps/web/content/docs/installation.mdx index caa28b1..ecad277 100644 --- a/apps/web/content/docs/installation.mdx +++ b/apps/web/content/docs/installation.mdx @@ -33,13 +33,13 @@ bun add @deessejs/errors Once installed, you can import the core functions from the package: -```ts main.ts +```ts title="main.ts" import { error, raise, is, causes } from '@deessejs/errors'; ``` You can also import specific functions if you prefer tree-shaking: -```ts partial.ts +```ts title="partial.ts" import { error } from '@deessejs/errors'; import { is } from '@deessejs/errors'; ``` @@ -48,7 +48,7 @@ import { is } from '@deessejs/errors'; The library works out of the box with TypeScript. No special compiler options are required, but the following settings in your `tsconfig.json` will ensure the best experience: -```json tsconfig.json +```json title="tsconfig.json" { "compilerOptions": { "strict": true, @@ -63,7 +63,7 @@ The `moduleResolution: "bundler"` option is recommended if you're using a modern To verify that the library is installed correctly, create a simple test file: -```ts verify.ts +```ts title="verify.ts" import { error } from '@deessejs/errors'; const TestError = error({ name: 'TestError' }); diff --git a/apps/web/content/docs/message-templates.mdx b/apps/web/content/docs/message-templates.mdx index 9b819da..d889002 100644 --- a/apps/web/content/docs/message-templates.mdx +++ b/apps/web/content/docs/message-templates.mdx @@ -9,7 +9,7 @@ Message templates let you create dynamic error messages that include runtime val Wrap field names in curly braces to create placeholders in your message: -```ts basic.ts +```ts title="basic.ts" import { error } from '@deessejs/errors'; const ValidationError = error<{ field: string }>({ @@ -27,7 +27,7 @@ The placeholder is replaced with the actual value from the fields object at runt You can include multiple placeholders in a single message: -```ts multiple.ts +```ts title="multiple.ts" import { error } from '@deessejs/errors'; const FormatError = error<{ expected: string; actual: string }>({ @@ -49,7 +49,7 @@ Modifiers transform the placeholder value before insertion. They follow the fiel The `:upper` modifier converts the value to uppercase: -```ts upper.ts +```ts title="upper.ts" import { error } from '@deessejs/errors'; const UserCreatedError = error<{ userId: string }>({ @@ -67,7 +67,7 @@ This is useful for displaying IDs or codes that should be consistently formatted The `:lower` modifier converts the value to lowercase: -```ts lower.ts +```ts title="lower.ts" import { error } from '@deessejs/errors'; const PathError = error<{ path: string }>({ @@ -83,7 +83,7 @@ console.log(err.message); // "Invalid path: /users/data" The `:json` modifier serializes complex values to JSON: -```ts json.ts +```ts title="json.ts" import { error } from '@deessejs/errors'; const DataError = error<{ data: { id: number; name: string } }>({ @@ -101,7 +101,7 @@ The JSON modifier is particularly useful for debugging when you need to see the Modifiers can be combined with other fields: -```ts combined.ts +```ts title="combined.ts" import { error } from '@deessejs/errors'; const ApiError = error<{ @@ -125,7 +125,7 @@ console.log(err.message); // "GET /api/users failed with status 404" If a placeholder references a field that wasn't provided, the placeholder is left unchanged in the message: -```ts missing.ts +```ts title="missing.ts" import { error } from '@deessejs/errors'; const ValidationError = error<{ field: string }>({ diff --git a/apps/web/content/docs/multiple-inheritance.mdx b/apps/web/content/docs/multiple-inheritance.mdx index fcad330..54ae026 100644 --- a/apps/web/content/docs/multiple-inheritance.mdx +++ b/apps/web/content/docs/multiple-inheritance.mdx @@ -9,7 +9,7 @@ While single inheritance creates clear parent-child relationships, some errors n To inherit from multiple parents, pass an array of error factories to the `inherits` property: -```ts basic.ts +```ts title="basic.ts" import { error, is } from '@deessejs/errors'; const NetworkError = error({ name: 'NetworkError' }); @@ -28,7 +28,7 @@ Now this error belongs to both categories in the hierarchy. When an error inherits from multiple parents, `is()` returns true for any of its parents: -```ts checking.ts +```ts title="checking.ts" const NetworkError = error({ name: 'NetworkError' }); const StorageError = error({ name: 'StorageError' }); @@ -50,7 +50,7 @@ This flexibility lets you categorize errors from multiple angles. Consider a caching layer that sits between the network and storage. It might fail in ways that are both network-related and storage-related: -```ts example.ts +```ts title="example.ts" import { error, raise, is } from '@deessejs/errors'; const NetworkError = error({ name: 'NetworkError' }); @@ -88,7 +88,7 @@ This pattern is useful for middleware and infrastructure code that needs to resp Multiple inheritance works seamlessly with typed fields: -```ts with-fields.ts +```ts title="with-fields.ts" import { error, is } from '@deessejs/errors'; const NetworkError = error<{ endpoint: string }>({ diff --git a/apps/web/content/docs/recipes.mdx b/apps/web/content/docs/recipes.mdx index fac0377..3535500 100644 --- a/apps/web/content/docs/recipes.mdx +++ b/apps/web/content/docs/recipes.mdx @@ -9,7 +9,7 @@ This page collects common patterns and practical examples for using @deessejs/er Validation errors are one of the most common error types. They typically include the field that failed validation and a reason for the failure. -```ts validation.ts +```ts title="validation.ts" import { error, raise } from '@deessejs/errors'; import { z } from 'zod'; @@ -50,7 +50,7 @@ When validation fails, you get an error with specific information about which fi Network errors benefit from including the endpoint and any relevant request data. -```ts network.ts +```ts title="network.ts" import { error, raise } from '@deessejs/errors'; const NetworkError = error<{ @@ -84,7 +84,7 @@ You can extend this pattern to include request headers, body, or timing informat Database errors typically need the query that failed and any relevant identifiers. -```ts database.ts +```ts title="database.ts" import { error, raise } from '@deessejs/errors'; const DatabaseError = error<{ @@ -118,7 +118,7 @@ Chaining the original error preserves the full context of what went wrong. Create a reusable error handler that processes errors based on their type. -```ts handler.ts +```ts title="handler.ts" import { error, raise, is, causes } from '@deessejs/errors'; import { ValidationError, NetworkError, DatabaseError } from './errors'; @@ -161,7 +161,7 @@ This pattern centralizes error handling logic and ensures consistent responses. When building services that call other services, wrap errors to add context while preserving the cause chain. -```ts service.ts +```ts title="service.ts" import { error, raise } from '@deessejs/errors'; import { NetworkError, UserNotFoundError } from './errors'; @@ -194,7 +194,7 @@ The caller gets both the context of the current operation and the underlying cau Create a hierarchy that lets you handle errors at different levels of granularity. -```ts hierarchy.ts +```ts title="hierarchy.ts" import { error, is } from '@deessejs/errors'; // Top level diff --git a/apps/web/content/docs/single-inheritance.mdx b/apps/web/content/docs/single-inheritance.mdx index 5903599..2f765ad 100644 --- a/apps/web/content/docs/single-inheritance.mdx +++ b/apps/web/content/docs/single-inheritance.mdx @@ -9,7 +9,7 @@ Error hierarchies help organize errors by domain and concern. @deessejs/errors s To create an error that inherits from another, use the `inherits` property when defining the error factory: -```ts basic.ts +```ts title="basic.ts" import { error, is } from '@deessejs/errors'; // Base error for the application @@ -28,7 +28,7 @@ Now `ValidationError` is a child of `AppError` in the hierarchy. You can verify When an error inherits from another, `is()` returns true for both the error itself and all its ancestors: -```ts checking.ts +```ts title="checking.ts" const AppError = error({ name: 'AppError' }); const ValidationError = error({ name: 'ValidationError', @@ -47,7 +47,7 @@ This is powerful for error handling — you can write a catch block for `AppErro Here's how inheritance helps in a real application: -```ts example.ts +```ts title="example.ts" import { error, raise, is } from '@deessejs/errors'; // Base errors @@ -83,7 +83,7 @@ This pattern lets you handle errors at different levels of granularity depending You can combine inheritance with message templates for powerful error definitions: -```ts with-message.ts +```ts title="with-message.ts" import { error, is } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -103,7 +103,7 @@ console.log(is(err, AppError)); // true The `inherits` property on the error factory lets you access parent types programmatically: -```ts parents.ts +```ts title="parents.ts" const AppError = error({ name: 'AppError' }); const ValidationError = error({ name: 'ValidationError', diff --git a/apps/web/content/docs/type-checking.mdx b/apps/web/content/docs/type-checking.mdx index 50e73d5..580258f 100644 --- a/apps/web/content/docs/type-checking.mdx +++ b/apps/web/content/docs/type-checking.mdx @@ -9,7 +9,7 @@ The `is()` function is your primary tool for checking what kind of error you're Pass an error and an error type to `is()` to check if the error is an instance of that type: -```ts basic.ts +```ts title="basic.ts" import { error, is } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -30,7 +30,7 @@ The function returns `true` if the error matches the type directly or inherits f The most common use case is inside catch blocks where you need to handle different error types: -```ts catch-block.ts +```ts title="catch-block.ts" import { error, raise, is } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -60,7 +60,7 @@ TypeScript narrows the type inside each branch when using `is()`, giving you acc The `is()` function also works with native JavaScript errors: -```ts native.ts +```ts title="native.ts" import { error, is } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -82,7 +82,7 @@ This lets you mix @deessejs/errors with standard JavaScript error handling seaml When `is()` returns true, TypeScript knows the error is of that specific type. This enables type-safe access to fields: -```ts type-safety.ts +```ts title="type-safety.ts" import { error, is } from '@deessejs/errors'; const ValidationError = error<{ field: string; reason: string }>({ @@ -105,7 +105,7 @@ Without the `is()` check, TypeScript would report an error because `err` is `unk You can check for multiple types in sequence: -```ts multiple.ts +```ts title="multiple.ts" import { error, is } from '@deessejs/errors'; const AppError = error({ name: 'AppError' }); @@ -132,7 +132,7 @@ Order your checks from most specific to least specific to ensure the correct han Native JavaScript `instanceof` doesn't work with @deessejs/errors errors because they're created with a factory function rather than a class. The `is()` function bridges this gap and adds support for inheritance checking. -```ts comparison.ts +```ts title="comparison.ts" import { error, is } from '@deessejs/errors'; const AppError = error({ name: 'AppError' });