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

zip-peek

v0.2.2

Published

Service worker based ZIP asset loader for browser applications.

Readme

zip-peek

zip-peek lets browser applications load files from a remote .zip archive by only requesting the needed range of bytes without downloading and extracting the full ZIP file.

It registers a service worker that intercepts requests like:

https://cdn.example.com/packages/session.zip/path/to/asset.png

The service worker fetches only the byte range needed for the requested ZIP entry, inflates the file when required, caches the extracted response, and returns it to the browser as a normal asset response.

When Is This Package Useful?

Use this package when your app receives a base path and later appends folders and file names to fetch assets from that path.

For example, your app may normally treat the base path as a directory:

const basePath = "https://cdn.example.com/packages/session";
const imageUrl = `${basePath}/slides/slide-1/image.png`;

If the same assets are sometimes delivered as a ZIP file instead:

const basePath = "https://cdn.example.com/packages/session.zip";
const imageUrl = `${basePath}/slides/slide-1/image.png`;

zip-peek lets the app keep using the same URL-building pattern. The app does not need to know whether the base path points to a normal directory or a ZIP file, and the ZIP-specific work stays inside the service worker.

What It Does

The package provides two pieces:

  • A browser API, initZipPeek(), used by your application.
  • A standalone service worker file, zipServiceWorker.js, that must be copied into your app's public output and served by your app. You will find a webpack sample below.

At runtime, the package:

  1. Registers the ZIP service worker with the configured workerUrl and scopeUrl.
  2. Reloads once after first service worker install when needed, so the page becomes controlled.
  3. Intercepts matching .zip/... asset requests within the service worker scope.
  4. Loads and caches the ZIP central directory on first use, or during init when allowedZipUrls is provided.
  5. Fetches only the byte range needed for the requested file.
  6. Inflates deflated files using fflate.
  7. Returns the extracted file bytes to the browser request as a normal Response.
  8. Caches extracted assets in the browser Cache API.

Installation

pnpm add zip-peek

or:

npm install zip-peek

Basic Usage

import { initZipPeek } from "zip-peek";

const zipPeekInit = await initZipPeek({
  workerUrl: new URL("/zipServiceWorker.js", window.location.origin).href,
  scopeUrl: new URL("/", window.location.origin).href,
});

if (zipPeekInit.reloaded) {
  return;
}

After initialization, your app can continue building asset URLs under the ZIP URL:

const zipUrl = "https://cdn.example.com/packages/zipfile.zip";
const imgUrl = `${zipUrl}/slides/slide-1/image.png`;

// This request will work normally and zip-peek will do the magic in the background:
fetch(`${imgUrl}`);

The browser requests that URL normally. The service worker intercepts it and serves slides/slide-1/image.png from inside the ZIP.

API

initZipPeek(options)

type InitZipPeekOptions = {
  workerUrl: string;
  scopeUrl: string;
  reloadOnFirstInstall?: boolean;
  cacheClearingStrategy?: ZipCacheClearingStrategy;
  allowedZipUrls?: string[];
  zipAssetCacheName?: string;
  assetCacheTtlMs?: number;
  logPrefix?: string;
};

type ZipCacheClearingStrategy = "keep-all" | "keep-allowed-urls" | "clear-all";

Returns:

type InitZipPeekResult = {
  reloaded: boolean;
  initialized: boolean;
};

Options

workerUrl

The URL where the browser can download the service worker JavaScript file. The service worker file must be served from the same origin as the page.

Example:

workerUrl: new URL("./zipServiceWorker.js", window.location.href).href;

In production, you will usually prefer hashed file to give it a long Cache-Control duration:

zipServiceWorker.a1b2c3d4.js

scopeUrl

The service worker scope. The worker can only intercept requests inside this scope. See Service Worker API for more details.

Examples:

scopeUrl: new URL("/", window.location.origin).href;
scopeUrl: new URL("/app/", window.location.origin).href;

Choose the smallest scope that includes the pages and asset requests you want the service worker to intercept. For local development, the scope often differs from production because the app may be served from a different path.

reloadOnFirstInstall

Defaults to true. When a service worker is installed for the first time, the current page may not yet be controlled by it. With this option enabled, the package reloads once and returns { reloaded: true, initialized: false }.

cacheClearingStrategy

Defaults to 'keep-all'. Controls which zip-peek cache entries are cleared during initialization.

  • 'keep-all': do not clear cached ZIP manifests or extracted assets.
  • 'keep-allowed-urls': requires non-empty allowedZipUrls; clears cached ZIP data for URLs outside the allow-list.
  • 'clear-all': clears all zip-peek cached manifests and extracted assets for a fresh start.

allowedZipUrls

Restricts zip-peek to a known set of ZIP package URLs. When omitted, zip-peek lazily serves any matching .zip/... request inside the service worker scope. When provided, the array must contain at least one ZIP URL, and zip-peek transparently passes matching requests for ZIP URLs outside the list.

The allow-list also acts as a warmup list: zip-peek loads and caches each allowed ZIP manifest during initialization.

Examples:

allowedZipUrls: ["https://cdn.example.com/packages/zipfile.zip"];
allowedZipUrls: [
  "https://d3jfe.cloudfront.net/somedir/zipfile.zip?Expires=1798761599&Signature=K2CJW4Z7B3DCM4&Key-Pair-Id=K2CJW4Z7B3DCM4",
];

zipAssetCacheName

Overrides the browser Cache API bucket used by the service worker for ZIP manifests and extracted assets.

Default:

zip-cache-v1

assetCacheTtlMs

Controls how long extracted assets remain valid in the Cache API.

Default:

2 * 60 * 60 * 1000; // 2 hours

logPrefix

Changes the service worker log prefix.

Default:

[zipSW]

Required Server Configuration

1. The ZIP server must support range requests

The ZIP package URL must support HTTP byte range requests. The package reads the ZIP central directory and individual files using Range headers.

The ZIP server should return:

Accept-Ranges: bytes

For range requests, it must return:

HTTP/1.1 206 Partial Content
Content-Range: bytes start-end/total

If the ZIP server does not support range requests, zip-peek cannot stream individual files.

2. The service worker file must be same-origin

Browsers require service worker scripts to be served from the same origin as the page.

Valid when the page is also served from https://app.example.com:

https://app.example.com/zipServiceWorker.a1b2c3d4.js

Invalid if the page is on another origin:

https://static-assets.example.com/zipServiceWorker.a1b2c3d4.js

3. Configure Service-Worker-Allowed

By default, a service worker can only control pages under its own directory. If the worker file is served from a nested folder but needs to control a wider scope, the response for the worker file must include Service-Worker-Allowed.

For an app under /app/, configure:

Service-Worker-Allowed: /app/

If the worker needs to control the full origin, configure:

Service-Worker-Allowed: /

The header must be returned on the zipServiceWorker.js or zipServiceWorker.<hash>.js response itself.

4. Scope and worker location must match

The registered scope must be allowed by both:

  • the worker file location
  • the Service-Worker-Allowed header

Example:

navigator.serviceWorker.register("/app/zipServiceWorker.a1b2c3d4.js", {
  scope: "/app/",
});

This requires:

Service-Worker-Allowed: /app/

or:

Service-Worker-Allowed: /

Bundler Configuration

The package ships the worker as:

zip-peek/zipServiceWorker.js

Your application must copy this file into its public build output. The browser registers service workers by URL, so the worker must exist as a real served JavaScript file.

Production with a hashed worker filename

When your app emits hashed assets, copy the worker with a content hash and pass the final worker URL to initZipPeek().

const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: require.resolve("zip-peek/zipServiceWorker.js"),
          to: "zipServiceWorker.[contenthash:8].js",
        },
      ],
    }),
  ],
};

You then need a way for your runtime code to know the emitted hashed filename. Common approaches include:

  • generating an asset manifest
  • injecting the filename at build time
  • using your framework or bundler's asset URL mechanism

For example, after resolving the emitted filename:

await initZipPeek({
  workerUrl: new URL(
    `/assets/${zipServiceWorkerFilename}`,
    window.location.origin,
  ).href,
  scopeUrl: new URL("/", window.location.origin).href,
});

where zipServiceWorkerFilename is the final emitted file name, such as:

zipServiceWorker.a1b2c3d4.js

Development configuration

In development, it is usually simpler to copy the worker without a hash:

const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: require.resolve("zip-peek/zipServiceWorker.js"),
          to: "zipServiceWorker.js",
        },
      ],
    }),
  ],
};

Then use:

await initZipPeek({
  workerUrl: new URL("/zipServiceWorker.js", window.location.origin).href,
  scopeUrl: new URL("/", window.location.origin).href,
});

If your app is served from a subpath, adjust both workerUrl and scopeUrl to match that path.

Caching Behavior

The service worker stores:

  • the parsed ZIP manifest
  • extracted full assets

The default cache bucket is:

zip-cache-v1

Extracted assets expire after two hours by default. Set assetCacheTtlMs to customize this.

Use cacheClearingStrategy to control initialization-time cleanup. keep-all leaves existing entries untouched, keep-allowed-urls removes cached ZIP data outside allowedZipUrls, and clear-all removes all zip-peek cached ZIP data before warming any allowed manifests.

Internal Flow

For a detailed explanation of how the service worker intercepts requests, parses ZIP files, and handles caching, see the Zip Service Worker Flow documentation.

Limitations

  • The ZIP URL must end with .zip.
  • The ZIP server must support HTTP range requests.
  • The service worker file must be same-origin.
  • The service worker can only intercept requests inside its registered scope.
  • Supported ZIP compression methods are stored (0) and deflated (8).
  • ZIP64 is not currently supported.
  • The package is browser-only and depends on Service Worker, Cache API, and HTTP range request support.