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

@statewalker/webrun-site-host

v0.2.0

Published

Host a webrun-site-builder site behind a same-origin ServiceWorker in one call: files + endpoints + dynamic server-module runners, no manual SW plumbing

Readme

@statewalker/webrun-site-host

Browser-side host for a SiteHandler. Registers a same-origin ServiceWorker, mounts the handler under a virtual path, and rewrites incoming requests to site-relative form before dispatching.

This package owns where a site runs (browser + SW). It does NOT own what the site does — endpoints, files, auth, and routing live in @statewalker/webrun-site-builder (or anywhere else that produces a SiteHandler = (Request) => Promise<Response>).

Getting started

import { SiteBuilder } from "@statewalker/webrun-site-builder";
import { HostedSiteBuilder } from "@statewalker/webrun-site-host";

const handler = new SiteBuilder()
  .setEndpoint("/api/time", () => new Response(new Date().toISOString()))
  .setFiles("/", clientFiles)
  .build();

const site = await new HostedSiteBuilder()
  .setSiteKey("demo")
  .setHandler(handler)
  .build();

iframe.src = site.baseUrl;

The split is intentional: the same SiteHandler works in every host — browser + SW via HostedSiteBuilder, any webrun-streams-* transport via DuplexSiteBuilder, and Node / Deno / Bun / Cloudflare Workers directly, since a SiteHandler already is their handler shape. Configuration lives in one place.

Install

npm install @statewalker/webrun-site-host @statewalker/webrun-files

@statewalker/webrun-files is a peer dependency (^0.7.0). Browser-only — it registers a ServiceWorker, so a secure context (https:// or localhost) is required.

Cross-application HTTP (no domains, no certificates)

Because the handler is just a function, you can point it at a remote peer over any webrun-streams-* transport (WebSocket, WebRTC, libp2p, LiveKit, MessagePort, …). The browser-side host doesn't care:

import { fetchOverDuplex } from "@statewalker/webrun-http-streams";
import { connect } from "@statewalker/webrun-streams-ws";

const { call } = await connect({ url: "wss://peer.example" }); // any adapter
const site = await new HostedSiteBuilder()
  .setHandler((request) => fetchOverDuplex(call, request))
  .build();

iframe.src = site.baseUrl;
// Every fetch inside the iframe is now proxied across the peer connection.

apps/livekit-demo/client-page/main.ts and apps/p2p-demo/client-page/main.ts are both this pattern against a real transport.

API

class HostedSiteBuilder {
  constructor(options?: HostedSiteBuilderOptions);
  setSiteKey(key: string): this;
  setServiceWorkerUrl(url: string): this;
  setHandler(handler: SiteHandler): this;
  build(): Promise<HostedSite>;
}

interface HostedSite {
  readonly siteKey: string;
  readonly baseUrl: string;
  stop(): Promise<void>;
}

interface HostedSiteBuilderOptions {
  adapterFactory?: AdapterFactory;
}

build() throws if setHandler was not called. siteKey defaults to a generated UUID and serviceWorkerUrl to /sw-worker.js. adapterFactory is the seam the tests use to inject a fake instead of a real ServiceWorker; it also takes SiteAdapter, SiteAdapterRegistration and AdapterFactory, all exported.

Also exported, for callers that accept "a FilesApi or a plain path → content map" in their own APIs:

type FilesSource = FilesApi | Record<string, string | Uint8Array>;
function resolveFilesSource(source: FilesSource): Promise<FilesApi>;

HostedSiteBuilder itself never calls it — it hosts a SiteHandler and owns no file configuration.

Plus a standalone utility for the "endpoint is a JS module dynamically imported from the site itself" pattern:

export function newServerRunner(
  modulePath: string,
  getBaseUrl: () => string,
  env?: Record<string, unknown>,
): EndpointHandler;

Use it with SiteBuilder.setEndpoint:

let getBaseUrl = () => "";
const handler = new SiteBuilder()
  .setFiles("/server", serverFiles)
  .setEndpoint("/api", newServerRunner("/server/api/index.js", () => getBaseUrl()))
  .build();

const site = await new HostedSiteBuilder().setHandler(handler).build();
getBaseUrl = () => site.baseUrl;

What build() does

  1. Resolve siteKey (generated UUID if not set) and swUrl (/sw-worker.js if not set).
  2. Construct and start the adapter (SwHttpAdapter by default — registers the ServiceWorker and awaits activation).
  3. Register a fetch interceptor under <origin>/<siteKey>/ that:
    • Strips the SW prefix from the incoming Request.url.
    • Dispatches to your handler.
  4. Return a HostedSite with the resolved baseUrl and a stop() for teardown.

See also

Dependencies

| Dependency | Kind | Why | | --- | --- | --- | | @statewalker/webrun-site-builder | runtime | The SiteHandler shape and file/endpoint composition. | | @statewalker/webrun-http-browser | runtime | SwHttpAdapter — the ServiceWorker registration and dispatch. | | @statewalker/webrun-files-mem | runtime | In-memory FilesApi used when resolving inline file maps. | | @statewalker/webrun-files | peer (^0.7.0) | The FilesApi interface itself. |

Browser-only: requires navigator.serviceWorker and therefore a secure context. ESM only ("type": "module").

Development

pnpm test        # vitest run
pnpm run build   # rolldown + tsc --emitDeclarationOnly
pnpm lint        # biome check src tests

License

MIT © statewalker — see LICENSE.