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

@nickpaustian/tethys

v0.2.0

Published

A shared fetch pool for streaming modules: one connection budget, cross-module priorities and a bandwidth estimate

Readme

Tethys

A shared fetch pool for modules that stream: one connection budget, priorities that cross module boundaries, and a bandwidth figure. Optional; Atlas and Theia each stream on their own without it, and take a client of it when both are present.

Tethys is the Titaness of the fresh water that feeds every river, which is what the pool is to the modules that drink from it.

Install

npm install @nickpaustian/tethys

No dependencies. Atlas and Theia do not depend on this package: each declares its own PoolClient interface (submit returning a ticket with setPriority, cancel and started), which Tethys's client satisfies, as does any object with the same shape.

Why

The browser gives one bandwidth pool and a small connection budget per host. Two streaming modules each running their own concurrency compete blind: texture tiles can starve geometry pages, and neither knows how fast bytes are arriving. The pool owns when each fetch starts and measures what comes in. Each module keeps its own queue semantics (what it wants, restated every pass, dropped when no longer wanted, aborted when stale), so nothing about their behaviour changes except who says go.

Usage

import { FetchPool } from "@nickpaustian/tethys"

const pool = new FetchPool({ concurrency: 12 })
const mesh = await ClusteredMesh.load(url, { pool: pool.client("theia", { weight: 1 }) })
const vt = new VirtualTexture({ channels, pool: pool.client("atlas", { weight: 0.8 }) })

pool.stats.bytesPerSecond // over the last two seconds, for a detail cap policy

A client submits each fetch with a priority normalised within the module (its most wanted item is 1). The pool starts the waiting request with the highest weight × priority whenever a slot is free, oldest first on ties, after any client that is still under its reserve. Giving geometry the higher weight is a reasonable default, because a missing page reads as a hole where a missing tile reads as a soft texture. A waiting request can be withdrawn or re-prioritised; a running one is left to its module's own abort handling, which frees the slot when the fetch settles.

Theia submits a ticket per page and gathers its coalesced range request only when the pool starts one, so neighbours that arrived in the queue since are still pulled in. Atlas submits a ticket per tile.

Options

new FetchPool(options):

| option | default | meaning | | --- | --- | --- | | concurrency | 12 | fetches in flight at once across every client | | bandwidthWindowMs | 2000 | milliseconds of received bytes the bandwidth estimate averages over |

pool.client(name, options) returns the client for name, creating it on first use:

| option | default | meaning | | --- | --- | --- | | weight | 1 | relative share of the pool; a request is ordered by weight × its own priority | | reserve | 0 | connections the client is owed before weight and priority decide: while it has fewer than this in flight, its waiting request starts ahead of every other client's. A module with small frequent fetches (tiles, a few kilobytes each) is otherwise starved by one whose fetches rank higher for seconds at a time (geometry pages after a zoom); a reserve of a quarter of the pool for each keeps both moving |

Writing a client

const client = pool.client("tiles", { weight: 0.8 })

const ticket = client.submit(priority, async ({ progress }) => {
  const response = await fetch(url)
  for await (const chunk of response.body) progress(chunk.byteLength)
}, () => { /* called if the ticket is cancelled before it starts */ })

ticket.setPriority(0.4)   // while still waiting
ticket.cancel()           // withdraw a waiting request
ticket.started            // true once the run has begun

submit(priority, run, onCancel?) queues a run and returns a ticket. client.inFlight is how many of its runs are going. run receives a context whose progress(bytes) feeds the bandwidth estimate; the slot is freed when the run's promise settles, however it settles. The pool never aborts a running fetch; that stays with the client. client.weight can be changed at any time.

Stats

pool.stats returns:

| field | meaning | | --- | --- | | concurrency | the pool's budget | | inFlight, queued | requests running and waiting across all clients | | bytesPerSecond | received bytes over the last bandwidthWindowMs | | clients[] | per client: name, weight, reserve, queued, inFlight, started, bytes |

Development

npm ci
npm test            # vitest: ordering, re-prioritising and cancelling, slot accounting, the bandwidth window
npm run typecheck
npm run build       # dist/

CI runs typecheck, tests and build on every push and pull request, and publishes to npm with provenance on a v* tag.

Licence

MIT.