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

@aries0d0f/fetch-worker

v0.2.2

Published

Native fetch, offloaded to a Web Worker — a tiny, dependency-light HTTP client with a comlink-powered worker transport and automatic main-thread fallback.

Readme

fetch-worker

CI npm GitHub Packages license

An axios-style HTTP client built entirely on native fetch — with requests run on a dedicated Web Worker thread, in parallel with your main thread, via comlink. TypeScript-first from the ground up: every method, response, and error shape is fully typed and generic over your payloads.

  • Axios-style ergonomics, zero axios — http.get/post/put/patch/delete, typed generics, structured error rejections, and abortable requests — all on top of the platform's own fetch, with no polyfills or XHR fallback.
  • Runs in parallel with your app — request/response handling, JSON/YAML parsing, and error normalization all happen on a separate Web Worker thread, so they proceed alongside your main thread instead of contending with rendering and user input for the same event loop.
  • TypeScript-first — request bodies, response payloads, and error shapes are generic and inferred end-to-end; there's no any in the public API.
  • Same API everywhere — the client detects its environment at import time. In a browser it spins up a worker automatically; in Node or during SSR (no Worker global) it calls fetch directly on the current thread instead.
  • No bundler lock-in — the worker is loaded via the portable new URL('./worker.js', import.meta.url) pattern, which Vite, Webpack 5+, and native ESM all understand. No ?worker suffix or plugin required.
  • Small surface area — two real dependencies: comlink (the worker RPC transport) and js-yaml (for the .yaml() response helper).

Install

This package is published to both npmjs.org and GitHub Packages.

From npmjs.org, no extra setup is needed:

bun add @aries0d0f/fetch-worker
# or: npm install @aries0d0f/fetch-worker / pnpm add @aries0d0f/fetch-worker

To install from GitHub Packages instead, note that it requires authentication even for public packages, so point your package manager at the registry with a token that has read:packages scope first.

Add to .npmrc in your project (or ~/.npmrc globally):

@aries0d0f:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}

Then install as usual with the commands above.

Usage

import http from '@aries0d0f/fetch-worker';

const res = await http.get<{ id: number; name: string }>('/api/users/1');
const user = await res.json();

await http.post('/api/users', { name: 'Ada' });

Aborting a request

Pass controllable: true to get back the in-flight request alongside its AbortController:

const { request, controller } = await http.get('/api/slow', { controllable: true });

setTimeout(() => controller.abort(), 1000);

const res = await request;

Response helpers

Every response exposes the usual fetch Response members (status, ok, text(), blob(), arrayBuffer(), ...) plus:

  • res.json<T>() — parse the body as JSON
  • res.yaml<T>() — parse the body as YAML
  • res.headers — a Headers-like object whose accessors (get, has, entries, keys, values) return Promises, since Headers instances themselves can't cross the worker boundary

Error handling

A non-2xx response rejects with a plain object rather than throwing. The error field depends on the response's Content-Type:

  • application/problem+json — parsed as an RFC 9457 Problem Details object: { type, title?, status?, detail?, instance?, ...extensions }.
  • Anything else — the raw response body text, exactly as native fetch would give you (no parsing or reshaping attempted).

If your API always sends application/problem+json errors, just use err.error as a Problem Details object directly:

try {
  await http.get('/api/missing');
} catch (err) {
  // err: { ok, status, statusText, url, headers, error }
  console.error(err.error.title, err.error.detail);
}

Otherwise, check Content-Type before assuming err.error is a Problem Details object rather than raw text:

if (/^\s*application\/problem\+json\s*(;|$)/i.test(err.headers['content-type'] ?? '')) {
  console.error(err.error.title, err.error.detail); // RFC 9457 problem details
} else {
  console.error(err.error); // raw body
}

Skipping the worker

Import the core client directly to always run on the current thread (useful in tests, or when you deliberately don't want a worker):

import { http } from '@aries0d0f/fetch-worker/core';

Framework-reactive values

If you pass a reactive object (e.g. a Vue ref/reactive value) as a request body or options, it's unwrapped to its plain, cloneable form before being sent across the worker boundary — no extra step required on your end.

API

| Method | Signature | | --------------------------------- | ---------------------------------------------- | | http.get(url, options?) | GET request | | http.head(url, options?) | HEAD request | | http.post(url, body, options?) | POST request | | http.put(url, body, options?) | PUT request | | http.patch(url, body, options?) | PATCH request | | http.delete(url, options?) | DELETE request | | http.request(method, url, ...) | Low-level entry point used by all of the above |

options extends RequestInit (minus headers/body/signal, which are handled specially) with one addition: controllable?: boolean.

Why a worker?

Parsing large JSON/YAML payloads, and the general bookkeeping fetch does around headers/body streams, all run on whichever thread calls it. Moving that work onto a dedicated worker thread lets it run in parallel with your main thread instead of on it — keeping the main thread free for rendering and user input, particularly useful in UI-heavy apps making frequent or large requests.

Development

bun install
bun run build       # bundle with tsup (ESM output + .d.ts)
bun run test        # vitest + msw
bun run lint         # eslint
bun run typecheck    # tsc --noEmit

This repo uses Changesets for versioning. Run bun run changeset alongside your PR to describe the change; CI opens a "Version Packages" PR and publishes to GitHub Packages on merge.

License

MIT