npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@brickworks/result

v2.0.2

Published

A simple, powerful Result type for JavaScript with API serialization support

Readme

@brickworks/result

A simple, powerful Result type for JavaScript with built-in API serialization support.

Features

  • 🚀 Zero Dependencies - Lightweight and fast
  • 🧹 Simple - No symbols, no hidden properties, just plain objects
  • 🎯 Three States - Success/Failure/Error (not just Ok/Err)
  • 🔗 Chainable - Fluent API with .onSuccess() / .onFailure() / .onError()
  • 🌐 API-Ready - Built-in serialization with Result.fetch() and Result.parse()
  • Async Support - fromPromise(), all(), and more
  • ✍️ Flexible Naming - Use success() or createSuccess(), your choice
  • 📝 TypeScript - Full type definitions included

Installation

npm install @brickworks/result

Quick Start

import { Result } from '@brickworks/result';

// Create results
const ok = Result.success({ id: 123, name: 'Alice' });
const fail = Result.failure('User not found');

// Chain handlers
ok
  .onSuccess(r => console.log('User:', r.value))
  .onFailure(r => console.error('Failed:', r.reason));

// Transform
const greeting = Result.map(ok, user => `Hello ${user.name}!`);

// Pattern match
Result.match(ok, {
  success: r => console.log(r.value),
  failure: r => console.error(r.reason),
  error: e => console.error(e.message)
});

API Usage (The Killer Feature)

Server Side

import express from 'express';
import { Result } from '@brickworks/result';

const app = express();
Result.setThrowErrors(false);

app.get('/api/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id);
  
  if (user) {
    res.json(Result.success(user));
  } else {
    res.status(404).json(Result.failure('User not found'));
  }
});

Client Side

Option 1: Using Result.fetch() (simplest)

import { Result } from '@brickworks/result';

// One line - fetch and parse automatically
const result = await Result.fetch('/api/users/123');

result
  .onSuccess(r => displayUser(r.value))
  .onFailure(r => showError(r.reason))
  .onError(e => logError(e.message));

Option 2: Using fetchResult() (named import)

import { fetchResult } from '@brickworks/result';

const result = await fetchResult('/api/users/123');

Option 3: Manual fetch with Result.parse()

const response = await fetch('/api/users/123');
const result = Result.parse(await response.text());

Why it works: Results serialize to JSON with a _result: true marker, and Result.fetch()/Result.parse() automatically restore all methods.

Core Concepts

Three States

| State | Use Case | Example | |-------|----------|---------| | Success | Operation succeeded | User found, data saved | | Failure | Expected business failure | Validation failed, not found | | Error | Unexpected exception | DB crash, network timeout |

import { Result } from '@brickworks/result';

// Success - when things work
const ok = Result.success({ userId: 123 });

// Failure - expected problems (validation, not found, etc.)
const fail = Result.failure('Invalid email', { field: 'email' }, 'VALIDATION_ERROR');

// Error - unexpected problems (system errors, crashes)
Result.setThrowErrors(false); // Don't throw, return error object
const err = Result.error('Database connection failed', { host: 'localhost' });

API Reference

Creating Results

// All three methods support: (value/reason/message, data?, code?)

Result.success(data)
Result.failure(reason)
Result.error(message)  // Set Result.setThrowErrors(false) first

// Also available with explicit names:
Result.createSuccess(data)
Result.createFailure(reason)
Result.createError(message)

Fetching & Parsing (API Helpers)

// Fetch and auto-parse Result from API
const result = await Result.fetch(url, options);

// Parse JSON string to Result
const result = Result.parse(jsonString);

// Traditional approach (still works)
const result = JSON.parse(jsonString, Result.revive);

Guards

Result.isResult(value)    // → true if any Result type
Result.isSuccess(result)  // → true if success
Result.isFailure(result)  // → true if failure
Result.isError(result)    // → true if error

// Aliases
Result.successful(result)
Result.failed(result)
Result.errored(result)

Extractors

Result.getValue(result)   // → value (throws if not success)
Result.getReason(result)  // → reason (throws if not failure)
Result.getData(result)    // → data object (always safe)

Chainable Methods

result
  .onSuccess(r => console.log('Value:', r.value))
  .onFailure(r => console.error('Reason:', r.reason))
  .onError(e => console.error('Error:', e.message));

// Returns the same result for chaining

Transformations

// map - transform success value
const doubled = Result.map(result, val => val * 2);

// mapFailure - transform failure reason
const mapped = Result.mapFailure(result, reason => reason.toUpperCase());

// flatMap - chain operations that return Results
const chained = Result.flatMap(result, val => getUserById(val));

// match - pattern matching (returns value)
const output = Result.match(result, {
  success: r => r.value,
  failure: r => 'default',
  error: e => null
});

// unwrapOr - extract with fallback
const value = Result.unwrapOr(result, 'default');

// unwrapOrElse - extract with computed fallback
const value = Result.unwrapOrElse(result, r => computeDefault(r));

Async Operations

// Convert Promise to Result
const result = await Result.fromPromise(
  fetch('/api/users'),
  {
    failureCode: 'FETCH_FAILED',
    mapError: e => ({ reason: e.message, data: {} })
  }
);

// Combine multiple Results (short-circuits on first failure)
const results = await Result.all([
  Result.fetch('/api/users/1'),
  Result.fetch('/api/users/2'),
  Result.fetch('/api/users/3')
]);

Wire Format

// Serialize (happens automatically with res.json())
const json = JSON.stringify(result);
// → {"kind":"success","ok":true,"value":...,"_result":true}

// Deserialize
const result = Result.parse(json);
// or: JSON.parse(json, Result.revive)

// Manual conversion
const wire = Result.toWire(result);
const result = Result.fromWire(wire);

Practical Examples

API Helper

import { Result } from '@brickworks/result';

export const api = {
  getUser: (id) => Result.fetch(`/api/users/${id}`),
  
  createUser: (data) => Result.fetch('/api/users', {
    method: 'POST',
    body: JSON.stringify(data)
  }),
  
  updateUser: (id, data) => Result.fetch(`/api/users/${id}`, {
    method: 'PUT',
    body: JSON.stringify(data)
  })
};

// Usage
const userResult = await api.getUser(123);
userResult.onSuccess(r => displayUser(r.value));

Form Validation

function validateEmail(email) {
  return email.includes('@')
    ? Result.success(email)
    : Result.failure('Invalid email', { field: 'email' });
}

function validatePassword(password) {
  return password.length >= 8
    ? Result.success(password)
    : Result.failure('Password too short', { field: 'password' });
}

function validateForm(data) {
  const emailResult = validateEmail(data.email);
  const passwordResult = validatePassword(data.password);
  
  return Result.flatMap(emailResult, email =>
    Result.map(passwordResult, password =>
      ({ email, password })
    )
  );
}

React Hook

import { useState, useEffect } from 'react';
import { Result } from '@brickworks/result';

function useApi(url) {
  const [result, setResult] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    Result.fetch(url).then(r => {
      setResult(r);
      setLoading(false);
    });
  }, [url]);
  
  return { result, loading };
}

// Use it
function UserProfile({ userId }) {
  const { result, loading } = useApi(`/api/users/${userId}`);
  
  if (loading) return <div>Loading...</div>;
  
  return Result.match(result, {
    success: r => <div>Welcome {r.value.name}!</div>,
    failure: r => <div>Error: {r.reason}</div>,
    error: e => <div>System error</div>
  });
}

Error Handling with Codes

const result = await api.login(email, password);

result
  .onSuccess(r => {
    localStorage.setItem('token', r.value.token);
    navigate('/dashboard');
  })
  .onFailure(r => {
    switch (r.code) {
      case 'VALIDATION_ERROR':
        highlightField(r.data.field);
        break;
      case 'AUTH_FAILED':
        showError('Invalid credentials');
        break;
      case 'USER_NOT_FOUND':
        showError('User not found');
        break;
    }
  })
  .onError(e => {
    logToSentry(e);
    showGenericError();
  });

Configuration

// Control error throwing (default: true)
Result.setThrowErrors(false);  // Don't throw, return error objects

// Logging
Result.enableLog();
Result.setLogLevel('debug');  // 'error' | 'warn' | 'info' | 'debug'
Result.setMaxLog(1000);
Result.printLog();
Result.clearLog();

TypeScript

Full type definitions included:

import { Result, SuccessResult, FailureResult, ErrorResult } from '@brickworks/result';

async function getUser(id: number): Promise<Result<User>> {
  return Result.fetch<User>(`/api/users/${id}`);
}

const result = await getUser(123);

if (Result.isSuccess(result)) {
  result.value.name;  // TypeScript knows the type!
}

Naming Options

The library supports both terse and explicit naming:

// Terse (common in FP)
import { success, failure, error } from '@brickworks/result';

// Explicit (self-documenting)
import { createSuccess, createFailure, createError } from '@brickworks/result';

// Both styles work the same!

Why This Library?

vs. Traditional Error Handling

// ❌ Traditional (throw/catch)
try {
  const response = await fetch('/api/users/1');
  const data = await response.json();
  console.log(data);
} catch (err) {
  console.error(err);  // Lost context!
}

// ✅ Result (explicit handling)
const result = await Result.fetch('/api/users/1');
result
  .onSuccess(r => console.log(r.value))
  .onFailure(r => showError(r.reason, r.code))  // Rich context
  .onError(e => logToSentry(e));                // Type-safe

vs. Other Result Libraries

| Feature | @brickworks/result | Others | |---------|---------------|--------| | Three states | Success/Failure/Error | Usually just Ok/Err | | API serialization | Built-in | Manual | | Fetch wrapper | Result.fetch() | ❌ | | Parse helper | Result.parse() | ❌ | | Chainable methods | .onSuccess() etc. | Varies | | Rich metadata | codes, timestamps, data | Limited | | Dependencies | Zero | Varies |

Tests

95 tests passing. Run with:

npm test

License

ISC

Contributing

Contributions welcome! Please feel free to submit a Pull Request.