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

double-meh

v1.2.0

Published

A modern fetch-native HTTP I/O library for browsers and CLIs: caching, request dedup, retry, streaming, SSE, and transparent bundling. Zero dependencies.

Readme

double-meh :// NPM version

double-meh is a modern, fetch-native HTTP I/O library for browsers and CLIs (Node, Bun, Deno) — a thin, DX-first layer over fetch(). Spend the effort on setup (inspectors, services, defaults) so that use stays trivial: const person = await io.get(url). REST correctness — conditional writes, idempotency, problem+json, content negotiation — is a per-call one-liner, not a project.

The name is the :// symbol — Alex Sexton's "walrus", a.k.a. a "double meh".

Why it might be for you:

  • The method declares the return shape. io.get(url) → parsed data; io.full.get(url) → the full response envelope; io.stream.get(url) → a ReadableStream. Options tune behavior, never the return type — no resolveWithFullResponse-style flags.
  • One envelope contract. {data, status, ok, headers, response} plus lazily-parsed validators, Link pagination, Retry-After, Server-Timing — and a thrown BadStatus carries the same shape, so error handling reads like success handling.
  • Composable services. In-flight dedup and an app-governed cache (both on by default for GETs), verb-safety-aware retry, and a mock service that runs through the real pipeline — your tests need no server, and a mocked 503 really gets retried.
  • Web-native streaming, both directions. Response streams, request streams, and {writable, readable, response} duplexes that drop straight into a stream-chain pipeline — plus parsed record iteration (JSONL / json-seq) and a reconnecting SSE client on top.
  • Solid. Zero dependencies, ESM, bundled TypeScript typings, tested across Node, Bun, and Deno.

Examples

The everyday path — and the envelope when you need metadata:

import io from 'double-meh';

const person = await io.get('https://api.example.com/people/42'); // parsed data

const {data, etag} = await io.full.get('https://api.example.com/people/42');

Everyday writes are just as terse — objects go out as JSON:

const created = await io.post('https://api.example.com/people', {name: 'Ann'});

await io.put('https://api.example.com/people/42', {...person, email});

await io.patch('https://api.example.com/people/42', {email}); // JSON merge-patch et al. via `as`

await io.del('https://api.example.com/people/42'); // io.delete / io.remove — same verb

Safe writes — the library lowers intent to the correct headers and refuses unsafe retries:

// conditional update: If-Match from the read above; a lost race is a 412, not a lost write
await io.put('https://api.example.com/people/42', {...data, email}, {ifMatch: etag});

// or the whole read → apply → conditional PUT loop, with 412 → re-read → retry built in
await io.update('https://api.example.com/people/42', person => ({...person, email}));

// effectively-once POST: one Idempotency-Key minted per logical op, reused across retries
await io.post('https://api.example.com/orders', order, {idempotencyKey: true, retry: true});

Failures carry the envelope — problem+json is already parsed:

try {
  await io.get('https://api.example.com/missing');
} catch (error) {
  if (error instanceof io.BadStatus) console.error(error.status, error.data?.detail);
}

Stream a request body up and the response back down through one duplex:

const {writable, readable, response} = io.stream.put('https://api.example.com/bulk', {as: 'jsonl'});
source.pipeTo(writable); // request streams up
const envelope = await response; // status/headers arrive before the body drains

The lay of the land

  • Verbs: get, head, post, put, patch, delete (+ del/remove), options — on the callable io, mirrored under io.full (envelopes) and io.stream (streams/duplexes). A reusable options bag makes an endpoint descriptor shared across verbs.
  • Options lower intent: ifMatch, ifNoneMatch, idempotencyKey, accept, as, decode, timeout, retry, cache, track, bust, fields/sort/expand query builders — the full reference.
  • Services: track (dedup + adopt), cache (TTL, 304 revalidation, pattern invalidation), retry (incl. polling), mock. Scope defaults per host with predicates, or isolate consumers entirely with io.create().
  • Code-forward: an inline <head> prelude fires requests before the library loads; the library adopts them seamlessly — Concepts: code-forward.
  • Extensible everywhere: transports, request/response inspectors (URL-scoped), data & MIME processors, lifecycle events.

Install

npm i double-meh

ESM-only. CJS consumers can require('double-meh') on Node ≥ 20.19 (require(esm)).

Documentation

The canonical documentation lives in the project wiki — guides, concepts, cookbooks, and the per-module reference, with ranked search.

Migrating from heya/io / heya/io-node? double-meh is their fetch-native successor — the feature parity map is in the wiki: Heya-io parity.

Release history

  • 1.2.0 Streamed bodies over the sw transport, a malformed SSE retry: no longer storms reconnects, and {transport: 'sw'} resolves relative URLs against the page.
  • 1.1.1 io.paginate no longer loops forever when a server's offset fails to advance.
  • 1.1.0 Streamed bundles: io.bundle.streaming resolves each waiter as its part arrives.
  • 1.0.0 The initial release.

See the release notes for the long-form history.

License

BSD-3-Clause © Eugene Lazutkin