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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@visulima/ono

v2.0.2

Published

Ono is an error-parsing library that pretty prints JavaScript errors on a web page or the terminal.

Downloads

1,021

Readme

typescript-image mit licence npm downloads Chat PRs Welcome



| Light Mode | Dark Mode | | -------------------------------- | ------------------------------ | | light | dark |

Install

pnpm add @visulima/ono
npm i @visulima/ono
yarn add @visulima/ono

Features

  • Pretty, theme‑aware error page
    • Sticky header (shows error name/message while scrolling)
    • One‑click copy for error title (icon feedback)
  • Stack trace viewer
    • Shiki‑powered syntax highlighting (singleton highlighter)
    • Tabs for frames; grouping for internal/node_modules/application frames
    • Tooltips and labels to guide usage
    • Optional "Open in editor" button per frame
  • Error causes viewer (nested causes, each with its own viewer)
  • Solutions panel
    • Default open; smooth expand/collapse without layout jump
    • Animated height/opacity; icon toggles open/close
    • Built-in rule-based Markdown hints for common issues (ESM/CJS interop, export mismatch, port in use, missing files/case, TS path mapping, DNS/connection, React hydration mismatch, undefined property access)
    • Custom solution finders support
  • Raw stack trace panel
  • Theme toggle (auto/dark/light) with persistence
  • Copy to Clipboard - One-click copying for all data sections
  • Responsive Design - Sticky sidebar navigation with smooth scrolling
  • Consistent tooltips (one global script; components only output HTML)

New in latest version:

  • Tabbed Interface - Switch between Stack and Context views
  • Request Context Panel - Detailed HTTP request debugging information
    • cURL command with proper formatting and copy functionality
    • Headers, cookies, body, and session data
    • App routing, client info, Git status, and version details
    • Smart data sanitization and masking for sensitive information
    • Flexible Context API - Add any custom context data via createRequestContextPage()
  • Modern Ono Class API - Simple, consistent interface for both HTML and ANSI rendering
  • Solution Finders - Extensible system for custom error solutions

Accessibility and keyboard UX

  • ARIA-correct tabs and panels for stack frames; improved labeling
  • Focus trap within the overlay; restores focus on close
  • Keyboard shortcuts help dialog (press Shift+/ or “?” button)
  • Buttons/controls are keyboard-activatable (Enter/Space)

Editor integration

  • Editor selector is always visible; selection persists (localStorage)
  • Uses server endpoint when configured; otherwise opens via editor URL scheme (defaults to VS Code)

Using the Ono class (recommended)

The new Ono class provides a simple, consistent API for both HTML and ANSI error rendering:

import { Ono } from "@visulima/ono";

const ono = new Ono();

// HTML error page
const html = await ono.toHTML(error, {
    cspNonce: "your-nonce",
    theme: "dark",
    solutionFinders: [
        /* custom finders */
    ],
});

// ANSI terminal output
const { errorAnsi, solutionBox } = await ono.toANSI(error, {
    solutionFinders: [
        /* custom finders */
    ],
});

Node.js HTTP Server Example

import { createServer } from "node:http";
import { Ono } from "@visulima/ono";
import createRequestContextPage from "@visulima/ono/page/context";
import { createNodeHttpHandler } from "@visulima/ono/server/open-in-editor";

const ono = new Ono();
const openInEditorHandler = createNodeHttpHandler();

const server = createServer(async (request, response) => {
    const url = new URL(request.url || "/", `http://localhost:3000`);

    // Open-in-editor endpoint
    if (url.pathname === "/__open-in-editor") {
        return openInEditorHandler(request, response);
    }

    try {
        // Your app logic here
        throw new Error("Something went wrong!");
    } catch (error) {
        // Create context page with request information
        const contextPage = await createRequestContextPage(request, {
            context: {
                request: {
                    method: request.method,
                    url: request.url,
                    headers: request.headers,
                },
                user: {
                    client: {
                        ip: request.socket?.remoteAddress,
                        userAgent: request.headers["user-agent"],
                    },
                },
            },
        });

        // Generate HTML error page
        const html = await ono.toHTML(error, {
            content: [contextPage],
            openInEditorUrl: "__open-in-editor",
            cspNonce: "nonce-" + Date.now(),
            theme: "auto",
        });

        response.writeHead(500, {
            "Content-Type": "text/html",
            "Content-Length": Buffer.byteLength(html, "utf8"),
        });
        response.end(html);
    }
});

server.listen(3000);

Hono Framework Example

import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { Ono } from "@visulima/ono";
import createRequestContextPage from "@visulima/ono/page/context";

const app = new Hono();
const ono = new Ono();

app.get("/", (c) => c.text("OK"));

app.get("/error", () => {
    throw new Error("Boom from Hono");
});

app.onError(async (err, c) => {
    const contextPage = await createRequestContextPage(c.req.raw, {
        context: {
            request: {
                method: c.req.method,
                url: c.req.url,
                headers: Object.fromEntries(c.req.raw.headers.entries()),
            },
        },
    });

    const html = await ono.toHTML(err, {
        content: [contextPage],
        cspNonce: "hono-nonce-" + Date.now(),
        theme: "dark",
    });

    return c.html(html, 500);
});

serve({ fetch: app.fetch, port: 3000 });

API

Ono Class

The main API for rendering errors in both HTML and ANSI formats.

Constructor

const ono = new Ono();

Methods

toHTML(error, options?) => Promise<string>

Renders an error as an HTML page.

  • error: unknown - The error to render
  • options: TemplateOptions (optional)
    • content?: ContentPage[] - Additional pages to display as tabs
    • cspNonce?: string - CSP nonce for inline scripts/styles
    • editor?: Editors - Default editor for "Open in editor" functionality
    • openInEditorUrl?: string - Server endpoint for opening files in editor
    • solutionFinders?: SolutionFinder[] - Custom solution finders
    • theme?: 'dark' | 'light' | 'auto' - Theme preference

Returns the complete HTML string for the error page.

toANSI(error, options?) => Promise<{ errorAnsi: string; solutionBox?: string }>

Renders an error as ANSI terminal output.

  • error: unknown - The error to render
  • options: CliOptions (optional)
    • solutionFinders?: SolutionFinder[] - Custom solution finders
    • All other options from @visulima/error renderError options

Returns an object with errorAnsi (the formatted error) and optional solutionBox (suggested solutions).

toJSON(error, options?) => Promise<OnoJson>

Renders an error as a structured, JSON-serializable payload — the error name/message, the parsed stack frames for the error and every entry in its cause chain, the detected runtime, and a suggested solution (using the same solution-finder protocol as toHTML/toANSI). Useful for SPA/REST error responses when the client sends Accept: application/json.

  • error: unknown - The error to render
  • options: ToJsonOptions (optional)
    • solutionFinders?: SolutionFinder[] - Custom solution finders
import { renderJson } from "@visulima/ono";

app.use((error, request, response, next) => {
    if (request.accepts("json")) {
        renderJson(error).then((payload) => response.status(500).json(payload));

        return;
    }

    next(error);
});

Standalone renderers

The same three renderers are also exported as standalone, tree-shakeable functions — prefer these over instantiating Ono:

import { renderAnsi, renderHtml, renderJson } from "@visulima/ono";

const html = await renderHtml(error);
const { errorAnsi } = await renderAnsi(error);
const json = await renderJson(error);

createRequestContextPage(request, options) => Promise<ContentPage | undefined>

Creates a context page with detailed request debugging information.

  • request: Request - The HTTP request object
  • options: ContextContentOptions
    • context?: Record<string, unknown> - Additional context data
    • headerAllowlist?: string[] - Headers to include (default: all)
    • headerDenylist?: string[] - Headers to exclude
    • maskValue?: string - Mask for sensitive values (default: "[masked]")

createNodeHttpHandler(options) => (req, res) => void

Creates an HTTP handler for opening files in editors.

  • options: OpenInEditorOptions (optional)
    • projectRoot?: string - Project root directory
    • allowOutsideProject?: boolean - Allow opening files outside project

Returns an Express/Node.js compatible request handler.

CLI Example

import { Ono } from "@visulima/ono";

const ono = new Ono();

try {
    throw new Error("Something went wrong!");
} catch (error) {
    // Basic ANSI output
    const result = await ono.toANSI(error);
    console.log(result.errorAnsi);

    if (result.solutionBox) {
        console.log("\n" + result.solutionBox);
    }

    // With custom solution finder
    const resultWithCustom = await ono.toANSI(error, {
        solutionFinders: [
            {
                name: "custom-finder",
                priority: 100,
                handle: async (err, context) => ({
                    header: "Custom Solution",
                    body: "Try checking your configuration.",
                }),
            },
        ],
    });
}

Request Context Panel

Use createRequestContextPage() to create a "Context" tab with comprehensive debugging information:

  • Request Overview - cURL command with proper formatting and copy functionality
  • Headers - HTTP headers with smart masking for sensitive data (authorization, cookies, API keys, tokens, secrets)
  • Body - Request body content with sensitive-looking keys (password, token, secret, …) masked in both the rendered body and the copyable cURL
  • Session - Session data in organized key-value tables
  • Cookies - Cookie values are masked by default (the cookie header is treated as sensitive); pass an empty maskValue to disable
  • Dynamic Context Sections - Any additional context keys you provide are automatically rendered as sections with:
    • Proper titles (capitalized)
    • Copy buttons for JSON data
    • Organized key-value tables
    • Sticky sidebar navigation

Built-in sections (when data is provided):

  • app - Application routing details (route, params, query)
  • user - Client information (IP, User-Agent, geo)
  • git - Repository status (branch, commit, tag, dirty state)
  • versions - Package versions and dependencies

Custom sections - Add any context data you want:

  • database - Database connection info, queries, etc.
  • cache - Cache status and keys
  • environment - Environment variables
  • performance - Performance metrics
  • And more!

Deep Object & Array Support - The context panel intelligently renders:

  • Nested objects with proper indentation and visual hierarchy
  • Arrays with indexed items and collapsible structure
  • Complex data types (strings, numbers, booleans, null, undefined)
  • Performance-optimized rendering with depth limits (max 3 levels)
  • Smart truncation for large datasets (shows first 10 items/keys)

All sections include copy buttons for easy data extraction and debugging.

Custom Solution Finders

Create custom solution finders to provide specific guidance for your application's errors:

import { Ono } from "@visulima/ono";

const customFinder = {
    name: "my-app-finder",
    priority: 100, // Higher priority = checked first
    handle: async (error, context) => {
        if (error.message.includes("database connection")) {
            return {
                header: "Database Connection Issue",
                body: "Check your database configuration and ensure the server is running.",
            };
        }

        if (error.message.includes("authentication")) {
            return {
                header: "Authentication Error",
                body: "Verify your API keys and authentication tokens are valid.",
            };
        }

        return undefined; // No solution found
    },
};

const ono = new Ono();
const html = await ono.toHTML(error, {
    solutionFinders: [customFinder],
});

Copy to Clipboard

All data sections in the Request Context Panel include copy buttons that:

  • Copy data in JSON format for easy debugging
  • Provide visual feedback (button changes to "Copied!" with green styling)
  • Support both modern navigator.clipboard API and fallback methods
  • Work across all browsers and environments

Adding custom pages/tabs via options.content

You can add any number of custom pages using the content option:

import { Ono } from "@visulima/ono";
import createRequestContextPage from "@visulima/ono/page/context";

const ono = new Ono();

// Create a context page with request information
const contextPage = await createRequestContextPage(request, {
    context: {
        request: request,
        app: { routing: { route: "/api/users", params: {}, query: {} } },
        user: { client: { ip: "127.0.0.1", userAgent: "Mozilla/5.0..." } },
        database: { connection: "active", queries: ["SELECT * FROM users"] },
    },
});

// Add custom pages
const customPages = [
    contextPage, // Context page with request info
    {
        id: "performance",
        name: "Performance",
        code: {
            html: "<div><h3>Performance Metrics</h3><p>Custom performance data here...</p></div>",
        },
    },
    {
        id: "debug",
        name: "Debug Info",
        code: {
            html: "<div><h3>Debug Information</h3><pre>" + JSON.stringify(debugData, null, 2) + "</pre></div>",
        },
    },
];

const html = await ono.toHTML(error, {
    content: customPages,
    cspNonce: "your-nonce",
});

Examples

The examples/ directory contains working examples for different use cases:

CLI Example (examples/cli/)

Demonstrates basic ANSI output and custom solution finders:

cd examples/cli
node index.js

Node.js HTTP Server (examples/node/)

Complete HTTP server example with rich context pages:

cd examples/node
node index.js

Try these routes:

  • /error - Basic error with context
  • /esm-cjs - ESM/CJS interop error
  • /export-mismatch - Export mismatch error
  • /custom-solution - Custom solution finder demo

Hono Framework (examples/hono/)

Hono framework integration example:

cd examples/hono
node index.js

Try these routes:

  • /error - Basic error handling
  • /error-html - HTML error page
  • /api/error-json - JSON error response

Server helpers

From @visulima/ono/server/open-in-editor:

  • createOpenInEditorMiddleware(options) — universal handler factory (Node http and Connect/Express), backed by launch-editor-middleware
  • createNodeHttpHandler(options) — returns (req, res) => Promise<void> for Node http servers
  • createExpressHandler(options) — returns an Express/Connect-compatible handler

Options:

  • projectRoot?: string — defaults to process.cwd()
  • allowOutsideProject?: boolean — defaults to false

Editor selector

  • Always visible
  • Persists user choice in localStorage (ono:editor)
  • Used for both the server opener (sent as editor in the POST body) and the client-side fallback

Client-side fallback editor links

  • If openInEditorUrl is not set, clicking “Open in editor” uses editor URL schemes on the client. The default editor is VS Code. The selected editor in the header is respected.
  • Supported editors and templates (placeholders: %f = file, %l = line, %c = column when supported):
    • textmate: txmt://open?url=file://%f&line=%l
    • macvim: mvim://open?url=file://%f&line=%l
    • emacs: emacs://open?url=file://%f&line=%l
    • sublime: subl://open?url=file://%f&line=%l
    • phpstorm: phpstorm://open?file=%f&line=%l
    • atom: atom://core/open/file?filename=%f&line=%l
    • atom-beta: atom-beta://core/open/file?filename=%f&line=%l
    • brackets: brackets://open?url=file://%f&line=%l
    • clion: clion://open?file=%f&line=%l
    • code (VS Code): vscode://file/%f:%l:%c
    • code-insiders: vscode-insiders://file/%f:%l:%c
    • codium (VSCodium): vscodium://file/%f:%l:%c
    • cursor: cursor://file/%f:%l:%c
    • emacs: emacs://open?url=file://%f&line=%l
    • idea: idea://open?file=%f&line=%l
    • intellij: idea://open?file=%f&line=%l
    • macvim: mvim://open?url=file://%f&line=%l
    • notepad++: notepad-plus-plus://open?file=%f&line=%l
    • phpstorm: phpstorm://open?file=%f&line=%l
    • pycharm: pycharm://open?file=%f&line=%l
    • rider: rider://open?file=%f&line=%l
    • rubymine: rubymine://open?file=%f&line=%l
    • sublime: subl://open?url=file://%f&line=%l
    • textmate: txmt://open?url=file://%f&line=%l
    • vim: vim://open?url=file://%f&line=%l
    • visualstudio: visualstudio://open?file=%f&line=%l
    • vscode: vscode://file/%f:%l:%c
    • vscodium: vscodium://file/%f:%l:%c
    • webstorm: webstorm://open?file=%f&line=%l
    • xcode: xcode://open?file=%f&line=%l
    • zed: zed://open?file=%f&line=%l&column=%c
    • android-studio: idea://open?file=%f&line=%l

Keyboard Shortcuts

  • Shift+/ (or ?) — Open shortcuts help dialog
  • Esc — Close dialogs
  • Enter/Space — Activate focused control (e.g., toggles, tabs)

Tooltips

Components emit HTML with data-tooltip-trigger; a single script exported by the tooltip module is imported once by the layout (so there's no duplication).

Extend the VisulimaError

import { VisulimaError } from "@visulima/error";

class MyError extends VisulimaError {
    constructor(message: string) {
        super({
            name: "MyError",
            message,
        });
    }
}

throw new MyError("My error message");

// or

const error = new MyError("My error message");

error.hint = "My error hint";

throw error;

Pretty code frame

import { codeFrame } from "@visulima/error";

const source = "const x = 10;\nconst error = x.y;\n";
const loc = { column: 16, line: 2 };

const frame = codeFrame(source, loc);

console.log(frame);
//   1 | const x = 10;
// > 2 | const error = x.y;
//     |                ^

Supported Node.js Versions

Libraries in this ecosystem make the best effort to track Node.js’ release schedule. Here’s a post on why we think this is important.

Contributing

If you would like to help take a look at the list of issues and check our Contributing guild.

Note: please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

Credits

Made with ❤️ at Anolilab

This is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. Anolilab is a Development and AI Studio. Contact us at [email protected] if you need any help with these technologies or just want to say hi!

License

The visulima error is open-sourced software licensed under the MIT