@jenova-marie/ts-rust-result - v2.2.18
    Preparing search index...

    @jenova-marie/ts-rust-result - v2.2.18

    ts-rust-result βœ¨πŸ¦€πŸ’–

    A lightweight, zero-dependency TypeScript library that brings Rust's Result type to your JavaScript/TypeScript projects. Handle errors gracefully with type safety and functional programming patterns. 🌸✨

    npm install @jenova-marie/ts-rust-result
    # or
    yarn add @jenova-marie/ts-rust-result
    # or
    pnpm add @jenova-marie/ts-rust-result

    Supports both ESM and CommonJS! Works in all Node.js environments:

    • βœ… Modern ESM: import { ok, err } from '@jenova-marie/ts-rust-result'
    • βœ… Legacy CJS: const { ok, err } = require('@jenova-marie/ts-rust-result')
    • βœ… TypeScript execution: Works with tsx, ts-node, and other TS runners

    Version 2.2 adds dual module support and domain-specific helper utilities:

    • πŸ“¦ Dual Module Support - Works with both ESM (import) and CommonJS (require)
    • 🎯 createDomainResult<E>() - Eliminate ALL type assertions in your modules
    • πŸ“š Pattern Guide - Comprehensive documentation for common patterns
    • πŸ”§ Clean API - No more err<ConfigError>() or as Result<T, E> casts needed
    • ♻️ Reusable - Create once, use everywhere in your domain
    import { ok, err, type Result } from '@jenova-marie/ts-rust-result'
    import { fileNotFound, type ConfigError } from './errors.js'

    function loadConfig(path: string): Result<Config, ConfigError> {
    if (!exists(path)) {
    return err<ConfigError>(fileNotFound(path)) // ❌ Still need generic
    }
    return ok(config) as Result<Config, ConfigError> // ❌ Type assertion
    }
    // config-result.ts - create once
    import { createDomainResult } from '@jenova-marie/ts-rust-result/helpers'
    import { type ConfigError } from './errors.js'

    export const { ok, err } = createDomainResult<ConfigError>()
    export type ConfigResult<T> = Result<T, ConfigError>
    // config-loader.ts - use everywhere
    import { ok, err, type ConfigResult } from './config-result.js'
    import { fileNotFound } from './errors.js'

    function loadConfig(path: string): ConfigResult<Config> {
    if (!exists(path)) {
    return err(fileNotFound(path)) // βœ… Completely clean!
    }
    return ok(config) // βœ… No casts needed!
    }

    // Recursive functions work perfectly
    function processTree(node: Node): ConfigResult<ProcessedNode> {
    for (const child of node.children) {
    const result = processTree(child)
    if (!result.ok) return result // βœ… Type flows through!
    }
    return ok(processed)
    }

    Learn More: See content/PATTERNS.md for comprehensive examples!

    Version 2.1 adds full generic type support - the #1 requested feature! No more as any casts:

    • ✨ Generic Result Type - Result<T, E = Error> with full type inference
    • 🎯 Zero Type Casts - err(fileNotFound(path)) just works - fully typed!
    • πŸ” IntelliSense Support - Access error properties with full autocomplete
    • 🌊 Union Error Types - Result<T, FileError | ValidationError> supported
    • πŸ”„ 100% Backward Compatible - Existing code continues to work unchanged
    function loadConfig(path: string): Result<Config> {
    if (!exists(path)) {
    return err(fileNotFound(path) as any) // ❌ Type cast required
    }
    return ok(config)
    }
    import { type FileNotFoundError } from '@jenova-marie/ts-rust-result/errors'

    function loadConfig(path: string): Result<Config, FileNotFoundError> {
    if (!exists(path)) {
    return err(fileNotFound(path)) // βœ… Fully typed!
    }
    return ok(config)
    }

    // Consumer code gets full type safety
    const result = loadConfig('config.json')
    if (!result.ok) {
    console.log(result.error.path) // βœ… TypeScript knows .path exists!
    }

    Migration: Additive change with default parameters - existing code works unchanged!

    Version 2.0 transforms ts-rust-result into an opinionated error handling framework with:

    • πŸ—οΈ Domain Errors - 8 standard error categories (FileSystem, Parse, Validation, Network, Database, Auth, Config, Unexpected)
    • πŸ”¨ Builder Pattern - Fluent API for creating errors: error('FileNotFound').withContext({...}).build()
    • ⚑ Factory Shortcuts - 30+ convenience functions like fileNotFound(), invalidJSON(), schemaValidation()
    • πŸ“Š Observability Built-in - First-class Sentry, OpenTelemetry, and Prometheus integration
    • πŸ” Smart Stack Traces - Auto-capture in dev/test, skip in production (NODE_ENV aware)
    • ⛓️ Error Chaining - Preserve full error context with .withCause()
    • βœ… Zod Integration - fromZodSafeParse() for seamless schema validation
    • πŸ“¦ Subpath Exports - Clean imports: @jenova-marie/ts-rust-result/errors, /observability

    Migration from 1.x: The core Result API is unchanged! All 1.x code continues to work. The new error infrastructure is opt-in through separate imports.

    Want the unopinionated 1.x version? It remains available at @jenova-marie/ts-rust-result@1.3.6.

    // Core Result (unchanged from 1.x)
    import { ok, err, type Result } from '@jenova-marie/ts-rust-result'

    // New: Opinionated error handling
    import { fileNotFound, error, type FileNotFoundError } from '@jenova-marie/ts-rust-result/errors'
    import { toLogContext } from '@jenova-marie/ts-rust-result/observability'

    function loadConfig(path: string): Result<Config, FileNotFoundError> {
    if (!exists(path)) {
    return err(fileNotFound(path))
    }
    return ok(parse(readFile(path)))
    }

    const result = loadConfig('/app/config.json')
    if (!result.ok) {
    // Structured logging with one line
    logger.error(toLogContext(result.error), 'Failed to load config')

    // Send to Sentry
    Sentry.captureException(toSentryError(result.error))

    // Record metric
    errorCounter.inc(toMetricLabels(result.error))
    }

    Learn More:

    Error handling in JavaScript and TypeScript is fundamentally broken. Here's what we're dealing with: πŸ’”

    1. Inconsistent Error Handling πŸ’” - Some functions throw exceptions, others return null/undefined, and others return error objects. There's no standard way to handle failures.

    2. Type Safety Issues 😱 - TypeScript can't guarantee that you've handled all error cases. A function might return User | null, but TypeScript won't force you to check for null.

    3. Error Propagation Hell πŸ”₯ - You end up with deeply nested try-catch blocks or error checking at every level of your call stack.

    4. Lost Context 😒 - When errors bubble up through multiple layers, you lose the original context and stack trace information.

    5. Unpredictable Control Flow πŸŒͺ️ - Exceptions can be thrown from anywhere, making it hard to reason about your code's execution path.

    RustResult provides a consistent, type-safe, and ergonomic way to handle errors by treating them as values rather than exceptions. This approach:

    • Eliminates the guesswork 🎯 - No more wondering "what if this fails?"
    • Forces explicit error handling πŸ›‘οΈ - TypeScript's type system has your back!
    • Preserves error context πŸ’Ž - Keep all the important details throughout your call chain
    • Makes control flow predictable πŸŽͺ - Easy to follow and reason about
    • Enables functional programming patterns 🌈 - Transform and compose with style!

    Instead of error-prone traditional patterns with inconsistent error handling, you get a clean, type-safe approach where TypeScript forces you to handle both success and error cases explicitly.

    • πŸ¦€ Rust-style Result types - Ok<T> and Err<E> with full TypeScript support
    • πŸ›‘οΈ Type-safe error handling - No more throwing exceptions everywhere
    • πŸ”§ Functional utilities - map, mapErr, unwrap, and more
    • ⚑ Async support - tryResult for wrapping async operations
    • πŸ§ͺ Assertion helpers - assert, assertOr, assertNotNil with Result returns
    • πŸ“¦ Zero dependencies - Lightweight and tree-shakeable
    • 🎯 TypeScript-first - Full type safety and IntelliSense support
    • πŸ”„ Dual module support - Works with both ESM and CommonJS out of the box
    • Better Developer Experience 🌟 - IntelliSense and TypeScript will guide you to handle all error cases
    • Reduced Cognitive Load 🧠 - No more wondering "what if this fails?" - the type system tells you
    • Cleaner Code πŸŽ€ - Eliminate deeply nested try-catch blocks and error checking
    • Functional Programming 🌈 - Chain operations with map, mapErr, and other functional utilities
    • Consistent Error Handling 🀝 - Everyone on your team handles errors the same way
    • Better Code Reviews πŸ‘€ - Error handling is explicit and visible in the type signatures
    • Easier Testing πŸ§ͺ - Results are just values - easy to test success and failure cases
    • Reduced Bugs πŸ› - TypeScript prevents you from forgetting to handle error cases
    • Better User Experience πŸ’« - Graceful error handling without crashes
    • Improved Debugging πŸ” - Rich error context preserved throughout the call chain
    • Performance ⚑ - No exception throwing overhead in the happy path
    • Maintainability πŸ—οΈ - Clear separation between success and error logic
    import { ok, err, type Result } from '@jenova-marie/ts-rust-result'

    // Functions that can fail return Result<T, E>
    function parseNumber(input: string): Result<number> {
    const num = Number(input)
    if (isNaN(num)) {
    return err(new Error(`Invalid number: ${input}`))
    }
    return ok(num)
    }

    // Handle results with type-safe checks
    const result = parseNumber('42')
    if (result.ok) {
    console.log(`Parsed: ${result.value}`) // TypeScript knows .value exists
    } else {
    console.error(result.error.message) // TypeScript knows .error exists
    }
    // v2.1.0+ with generic error types
    type Ok<T> = { ok: true; value: T };
    type Err<E = Error> = { ok: false; error: E };
    type Result<T, E = Error> = Ok<T> | Err<E>;

    Creates a successful result.

    Creates an error result. v2.1.0+: Fully typed - no casts needed!

    Type guard to check if a result is successful.

    Type guard to check if a result is an error.

    Unwraps a result, throwing the error if it's an error.

    Maps a successful result value using the provided function.

    Maps an error result using the provided function.

    Wraps an async function in a try-catch block and returns a Result.

    Rust-style assertion that returns a Result instead of throwing.

    Rust-style assertion with a typed error parameter.

    Asserts that a value is not null or undefined, returning the value if valid.

    RustResult follows a specific pattern to maintain clean separation between error handling and business logic:

    Functions that can fail should implement appropriate error handling and return Result<T> directly, using ok() for success and err() for failures.

    When calling functions that might throw (like third-party APIs, database calls, or existing code), wrap the call with tryResult().

    If you find yourself wrapping your own functions in tryResult(), you're doing it wrong.

    • Your functions: Return Result<T> directly ✨
    • Third-party calls: Use tryResult() to wrap 🌊
    • Never: Wrap your own functions in tryResult() 🚫
    import { ok, err, tryResult, type Result } from '@jenova-marie/ts-rust-result'
    import { networkError, type NetworkError } from '@jenova-marie/ts-rust-result/errors'

    interface User {
    id: string
    name: string
    }

    async function fetchUser(id: string): Promise<Result<User, NetworkError>> {
    // Wrap third-party calls that might throw
    const response = await tryResult(() => fetch(`/api/users/${id}`))
    if (!response.ok) {
    return err(networkError(`Failed to fetch user ${id}`, response.error))
    }

    const data = await tryResult(() => response.value.json())
    if (!data.ok) {
    return err(networkError('Failed to parse response', data.error))
    }

    return ok(data.value as User)
    }
    import { ok, err, type Result } from '@jenova-marie/ts-rust-result'
    import { schemaValidation, type SchemaValidationError } from '@jenova-marie/ts-rust-result/errors'

    interface UserInput {
    email: string
    age: number
    }

    function validateUser(input: UserInput): Result<UserInput, SchemaValidationError> {
    const issues: string[] = []

    if (!input.email.includes('@')) {
    issues.push('Invalid email format')
    }
    if (input.age < 0 || input.age > 150) {
    issues.push('Age must be between 0 and 150')
    }

    if (issues.length > 0) {
    return err(schemaValidation('UserInput', issues))
    }

    return ok(input)
    }
    import { mapErr } from '@jenova-marie/ts-rust-result'

    // Transform errors across module boundaries
    function loadApplicationConfig(): Result<Config, AppError> {
    const result = readConfigFile('/etc/app/config.json')

    // Transform FileNotFoundError β†’ AppError with context
    const transformed = mapErr(result, (fsErr) =>
    appError(`Config missing at ${fsErr.path}: ${fsErr.message}`)
    )

    if (!transformed.ok) return transformed
    return parseConfig(transformed.value)
    }

    Migrate from traditional try-catch patterns to Result-based error handling for better type safety and consistency.

    Convert Promise-based error handling to Result patterns for more predictable control flow.

    • Zero Runtime Overhead πŸš€ - Results are just plain objects with no hidden costs
    • Tree-shakeable 🌳 - Only include the functions you actually use
    • No Dependencies πŸ“¦ - No external libraries to load or parse
    • TypeScript-only 🎯 - No runtime type checking overhead
    • Modern Browsers 🌐 - ES2020+ features (Chrome 80+, Firefox 75+, Safari 13.1+)
    • Node.js 🟒 - 18.0.0+ (both ESM and CommonJS)
    • TypeScript πŸ”΅ - 4.5+
    • Module Systems πŸ“¦ - ESM (import) and CommonJS (require)

    We love contributions! Here's how you can help:

    1. Fork the repository 🍴
    2. Clone your fork: git clone https://github.com/yourusername/ts-rust-result.git
    3. Install dependencies: pnpm install
    4. Create a feature branch: git checkout -b feature/amazing-feature
    • Build the project: pnpm build
    • Run tests: pnpm test
    • Run tests in watch mode: pnpm test:watch
    • Lint code: pnpm lint
    1. Write your code following the existing style
    2. Add tests for new functionality
    3. Update documentation if needed
    4. Ensure all tests pass: pnpm test
    5. Commit your changes: git commit -m "feat: add amazing feature"
    1. Push to your fork: git push origin feature/amazing-feature
    2. Create a Pull Request with a clear description of your changes
    3. Wait for review and address any feedback
    • Bug fixes πŸ› - Help us squash those bugs!
    • New features ✨ - Ideas for additional utility functions
    • Documentation improvements πŸ“š - Better examples, clearer explanations
    • Performance optimizations ⚑ - Make it faster!
    • TypeScript improvements πŸ”΅ - Better type definitions and inference

    This project is committed to providing a welcoming and inclusive environment for all contributors. We expect all participants to:

    • Be respectful and considerate of others πŸ€—
    • Use welcoming and inclusive language πŸ’¬
    • Be collaborative and open to constructive feedback 🀝
    • Focus on what is best for the community 🌟
    • Show empathy towards other community members πŸ’•
    • GitHub Issues πŸ› - For bug reports and feature requests
    • GitHub Discussions πŸ’¬ - For questions and general discussion
    • Stack Overflow πŸ” - Tag questions with ts-rust-result

    Q: Why not just use try-catch everywhere? πŸ€” A: Try-catch doesn't provide type safety and can make control flow unpredictable. Results make error handling explicit and type-safe.

    Q: Isn't this just more verbose? πŸ“ A: Initially yes, but it prevents bugs and makes your code more maintainable in the long run.

    Q: Can I mix Results with traditional error handling? πŸ”„ A: Yes! Use tryResult to wrap existing async functions and gradually migrate your codebase.

    • Pattern Guide - Common patterns, best practices, and real-world examples

      • Domain-specific Result wrappers
      • Union error types
      • Recursive functions
      • Error composition
      • Type inference tips
    • Error Design Philosophy - Architectural decisions and design patterns

      • Why plain objects over Error classes
      • Discriminated unions
      • Builder pattern vs factory functions
      • Stack trace strategy
      • Error chaining
    • Sentry Integration - Error monitoring with Sentry

      • Setup and configuration
      • Converting DomainErrors to Sentry errors
      • Best practices for error reporting
      • Filtering and context enrichment
    • OpenTelemetry Integration - Distributed tracing with OpenTelemetry

      • Span attributes from errors
      • Trace context propagation
      • Metrics and observability
      • Integration with Jaeger/Tempo
    • Zod Integration - Schema validation with Zod

      • Converting Zod parse results to Results
      • Type-safe validation
      • Error mapping
      • Best practices

    GPL-3.0 License - see the LICENSE file for details.

    • Rust Community πŸ¦€ - For the inspiration and the Result type pattern
    • TypeScript Team πŸ”΅ - For the amazing type system that makes this possible
    • All Contributors πŸ‘―β€β™€οΈ - For making this library better

    Made with πŸ’– by Pippa βœ¨πŸ¦€

    "Error handling should be elegant, not an afterthought." 🌸✌️