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

libfw-client

v0.4.5

Published

High-performance streaming file & folder transfer SDK for the browser (libfw WASM engine + File System Access API + IndexedDB resume).

Readme

libfw-client SDK

The browser SDK for libfw: a zero-config wrapper around the WASM engine, the File System Access API and IndexedDB.

The same protocol engine is also available as a native Rust client for non-browser programs — libfw_client::native::NativeClient (async tokio + reqwest), with a runnable CLI in examples/rust-client.

Usage

import { LibfwClient } from 'libfw-client';

const client = new LibfwClient({
  baseUrl: '/',         // server origin (same-origin when empty)
  concurrency: 4,       // max parallel file transfers (independent HTTP streams)
  uploadWindow: 8,      // in-flight chunks per single file upload (raise to
                        // reduce upload stutter on high-latency links)
  downloadWindow: 4,    // parallel byte-range GETs per single file download
                        // (raise to reduce download stutter on high-latency links)
  compress: true,       // zrip per-block compression
  compressLevel: 'auto',// zrip level policy: 'auto' benchmarks uploads
                        // (needs autoTune) / 'fast' / 'balanced' / 'max' / N
  autoTune: true,       // adaptive tuning: probes /capabilities and ramps
  tuneTtlMs: 3600000,   // how long a settled result stays cached (browser,
                        // localStorage); 0 = never cache, re-ramp each time
                        // concurrency/windows/chunk size from real stats
  onEvent: (e) => {
    if (e.type === 'progress') updateProgressBar(e.done, e.total);
    else if (e.type === 'tuning') renderTuning(e.phase, e.params, e.stats);
    // other types: fileStart, fileCompleted, ...
  },
});

// Download a whole folder. Uses showDirectoryPicker when the File System
// Access API is available; otherwise the folder is zipped and saved via a
// traditional browser download — no feature detection needed by the caller.
await client.downloadFolder('your_token_here');

// Upload a FileList
const input = document.querySelector('input[type=file]');
await client.upload('your_token_here', input.files);

// Or upload a whole folder
await client.upload('your_token_here');

// Controls
client.pause();
client.resume();
client.cancel();

How it works

  • HTTP transport, not WebSocket — the engine drives all control commands (directory listing, metadata) and data over plain HTTP. This is what keeps transfers robust on lossy/unstable links: each transfer uses independent parallel HTTP streams, so a lost packet stalls only that one stream (which retries just its own bytes) instead of blocking a whole multiplexed WebSocket connection.
  • Downloads — downloadFolder(token, dirPath?) / downloadFile(token, filePath) list the tree (for folders) and fetch each large file as downloadWindow concurrent Range GETs (tus-style parallel transfer, one independent connection per range). Each chunk is retried independently (only the lost part is re-fetched); the engine reorders the chunks in memory and pushes Uint8Arrays to the SDK strictly in order. Range/If-Range/416 give natural resume against the server ETag (the server is the source of truth). With the File System Access API the SDK streams chunks to disk via fileHandle.createWritable(); without it (or with downloadMode: 'browser') it buffers the chunks and saves the result through a traditional browser download — a single file as-is, a folder packed into a .zip.
  • Uploads — upload(token, files?) slices each file into chunks, reads them via readFile, compresses each into one zstd frame and POSTs the missing chunks concurrently (out of order) with x-libfw-offset into a shared per-session temp on the server (positional writes). A final x-libfw-final commit validates the size and atomically renames the temp into place. Only the chunks the server still misses are re-sent (x-libfw-session-status probe seeds resume), so interrupted uploads resume BitTorrent-style (only the broken/lost parts are re-transmitted). A dropped connection (page refresh, crashed tab) keeps that partial on the server, so a reloaded page resumes exactly where it stopped.
  • Resume state (etag, offset, size) is persisted per path in IndexedDB and re-validated on every retry. createWritable() only publishes a file on close(), so a download checkpoints its prefix to disk every time the engine reports a durable offset (~4 MiB) — a hard page refresh mid-download therefore resumes from the last checkpoint instead of restarting from byte 0.
  • Pause/resume/cancel drive the WASM state machine (idle → downloading/uploading → paused → resumed → completed/failed).

Build

# 1. Compile the WASM engine + generate the web glue (requires wasm-pack)
npm run build:wasm

# 2. (optional) bundle a UMD build
npm run build:umd

The resulting package contains:

pkg/                  wasm-pack output (wasm + wasm-bindgen web glue)
index.js              ESM SDK
zip.js                dependency-free ZIP writer (browser-download fallback)
index.d.ts            TypeScript types
dist/libfw-client.umd.js   UMD bundle (after build:umd)

API

  • new LibfwClient(options?)
    • downloadWindow: number (default 4) — in-flight byte-range window per single file download (how many concurrent Range GETs); raise it on high-latency links. 1 disables parallelism (sequential downloads).
    • chunkSize: number (default 2097152, 2 MiB) — shared size used for both upload chunks and parallel download ranges; the engine reorders in-flight chunks in memory (worst case ≈ downloadWindow * chunkSize bytes) so the SDK still receives data in order.
    • compress: boolean (default true) — master switch for zrip compression; false sends every body as identity.
    • compressLevel: number | 'auto' | 'fast' | 'balanced' | 'max' (default 'balanced') — zrip level policy when compress is on. 'fast' is the advertised minimum (least CPU, worst ratio), 'balanced' the advertised default, 'max' the advertised maximum (best ratio); a number is clamped into the advertised range. 'auto' additionally micro-benchmarks the advertised range against a real sample of the first uploaded file (while autoTune is enabled) and picks the best bytes-saved-vs-CPU-time trade-off for the measured link speed; downloads request the resolved level from the server.
    • downloadMode: 'auto' | 'fs' | 'browser' (default 'auto') — 'fs' streams downloads through the File System Access API; 'browser' buffers and triggers a traditional browser download (folders become .zip); 'auto' uses 'fs' when the API exists and falls back to 'browser'. Download resume requires 'fs' (or an injected directoryHandle): an interrupted fs-mode download is continued from the bytes already on disk, while the memory-backed 'browser' fallback always restarts from byte 0 — there is no partial file to continue from.
    • maxFallbackBytes: number (default 536870912, 512 MiB) — memory cap for the in-memory 'browser' fallback. File sizes are pre-checked against it before buffering; a download that would exceed it rejects with a too-large LibfwError instead of risking an OOM. 0 disables.
    • autoTune: boolean (default false) — enable the adaptive tuning engine. The engine probes the server's /capabilities limits and TCP-style ramps the per-file window and cross-file concurrency from the advertised minimums using real transfer stats; the chunk size follows the measured throughput (~100 ms of it, clamped into the advertised range) and the zrip level is a client policy from compressLevel, never ramped. When disabled, the configured static values are used as-is. Tuning state is in memory for the lifetime of the client (a settle is reused by later transfers and dropped on failure) and is cached in localStorage per origin + direction, so a page refresh does not re-ramp — see tuneTtlMs.
    • tuneTtlMs: number (default 3600000, 1 hour) — how long a cached tuning result stays usable. The cache is keyed by origin and direction (an upload settle says nothing about a download), is tagged with the /capabilities it was measured against, and expires tuneTtlMs after the ramp settled — not after the last reuse — so a link measured long ago is re-measured even if it is used constantly. An entry is also discarded early when the capabilities change or a transfer fails. 0 disables the cache entirely (every transfer re-ramps). Ignored unless autoTune is enabled. Storage failures (private mode, quota, disabled storage) are swallowed: caching is an optimisation and never fails a transfer.
  • downloadFolder(token, dirPath?) → Promise<number>
  • downloadFile(token, filePath) → Promise<number>
  • upload(token, files?) → Promise<number>
  • clearResumeStore(direction?) → Promise<number>
  • pause(), resume(), cancel()
  • state(), progress(), doneBytes(), totalBytes()
  • tuneStatus() → { phase, params, stats, capsHash } | null — live adaptive-tuning status. phase is uninitialized | ramping | settled | degraded; params is { concurrency, uploadWindow, downloadWindow, chunkSize, compressLevel } (the zrip level is the resolved policy, i.e. what downloads request — uploads may use an 'auto'-benchmarked level for the session); stats is { rttMs, mbps } (EWMA request RTT, last-window throughput). null until the WASM engine is initialised.
  • Events: with autoTune enabled, onEvent additionally receives { type: 'tuning', phase, params, stats } on every phase transition / window evaluation.
  • Errors: every rejection is a LibfwError with a stable code.

See index.d.ts for the full type surface.