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

@sigx/resume

v0.15.6

Published

Resumability for SignalX SSR — QRL event handlers, zero-JS pages, upgrade-on-write hydration

Readme

@sigx/resume

Resumability for SignalX SSR — the second first-party strategy pack riding @sigx/server-renderer's public plugin API.

Server pages render fully; the browser ships only a tiny delegation loader. Event handlers are extracted at build time by sigxResume() (@sigx/vite/resume) into lazily-imported QRL chunks that run against a resumed scope of named signals — component setup never re-runs on load, and the component chunk itself loads only when a handler writes state (upgrade-on-write):

  1. 0 JS on load — the page's only script is the generated loader entry.
  2. First interaction — the handler chunk (runtime-free, usually <1 kB) loads and runs with $scope.signals.<name> rebuilt from serialized state. The triggering event is replayed.
  3. State changes — only then does the component chunk load and that one boundary hydrate; buffered writes replay through the live signals.

Server

The pack installs on the app — app.use(...) is the one install shape (#413). The natural home is the entry-server's per-request app factory; the manifest comes from virtual:sigx-manifests (inlined by the SSR build, undefined under dev, where resume runs manifest-less):

// src/entry-server.tsx
import { defineApp } from 'sigx';
import { resumePlugin } from '@sigx/resume';
import { resumeManifest } from 'virtual:sigx-manifests';

export function createApp(url: string) {
    return defineApp(<App />).use(resumePlugin({ manifest: resumeManifest }));
}

Any render method that receives the App picks the pack up from there — createSSR().render(app), createRequestHandler({ app }), createFetchHandler({ app }).

Components stamped by the transform (__resumeId) become boundaries with hydrate: 'never' — core schedules nothing; the pack's delegation owns all waking. Fully-extracted components resume through their QRL attributes; components whose handlers could not all be extracted carry data-sigx-wake:* attributes instead, and the first interaction fully hydrates them (no replay). A component used with a client:* directive belongs to @sigx/ssr-islands — register islandsPlugin() first when combining the packs.

Single-flight boundary refresh (rfc-server §6.3)

createBoundaryRefresh builds the server half of single-flight refresh: a mutation server function names boundaries to refresh, and the endpoint hands their client descriptors here to be re-rendered — fresh HTML plus fresh tracking-signal state in one response, so a never-hydrated boundary can update without ever loading its chunk.

import { createBoundaryRefresh } from '@sigx/resume/server';

const renderBoundaries = createBoundaryRefresh({
    plugins: [resumePlugin({ manifest })], // or omit and let `app` carry them
    components: { Tracker, Cart }          // registry key → server component
});
// handleServerFnRequest(request, { fns, renderBoundaries })  (wire phase of #313)

The registry is explicit — same posture as the server-fn registry, never ambient. Descriptors the re-render cannot honor (unknown key, a snapshot the render can't reproduce, a component failure) are omitted, never errors: the mutation already succeeded, and declined boundaries converge through $cache invalidation. Boundaries whose usage-site props don't serialize (children/slots/render props) are stamped refreshable: false at initial SSR and decline the same way.

The client half is automatic: @sigx/resume/client stamps the __SIGX_SERVERFN_BOUNDARIES__ seam when it loads, so any invalidates-declaring mutation sends the page's boundary inventory (each boundary's recorded useData deps included — the endpoint admits on deps ∩ invalidates) and applies the fresh entries — a never-hydrated boundary is DOM-swapped (delegation re-wires itself off the fresh attributes; its chunk never loads), an upgraded one gets live-signal writes. An in-flight upgrade, buffered writes, or a focused text field inside the boundary all win over a refresh — drops converge through cache invalidation. In dev, pass sigxServer({ renderBoundaries: '/src/dev-refresh.ts' }) a module exporting the same createBoundaryRefresh result (see examples/resume/src/dev-refresh.ts).

Client

Two postures, and picking the wrong one is silent — so pick deliberately.

Coexisting with a hydrated app (the common case)

An app that has a root — a shell, a router, ordinary interactive components — and also wants some resumable components on the page. Install the plugin on the server app, and on the client import only the generated loader entry:

// entry-client.tsx
import { defineApp } from 'sigx';
import 'virtual:sigx-resume/entry';   // the delegation loader — that's it
import { App } from './App';

const app = defineApp(<App />);
app.hydrate(document.getElementById('app')!);

Nothing else is needed, and that is the point: hydrate() walks the root as usual and skips resume boundaries on the way past — they are recorded hydrate: 'never' by the server plugin — while everything around them hydrates normally. The loader wakes a boundary on first interaction.

You may also app.use(resumePlugin()) on the client (it is harmless — the client half only registers provides), but it is not required.

Before #483 this was a trap. resumePlugin()'s install declared boundaries: 'explicit' unconditionally, which switches hydrate() to the no-root-walk path. Combined with hydrate: 'never' records, that meant an app installing the plugin on the client hydrated nothing at all — a dead shell, with no error and no warning. The plugin no longer declares a mode unless you ask for one.

App-less resumable pages

A page whose entire bootstrap is the generated loader entry — no root app, no hydration walk, ~1 KB of JS. Nothing to configure; just don't create an app. If you do have an app and deliberately want no root walk (the islands posture), say so explicitly:

app.use(resumePlugin({ boundaries: 'explicit' }));

Writing resumable components

Ordinary sigx components in resume modules (*.resume.tsx or under a resume/ directory — configurable on the Vite plugin):

export const Counter = component<{ label: string }>((ctx) => {
    const count = ctx.signal(0);
    return () => (
        <button onClick={() => count.value++}>
            {ctx.props.label}: {count.value}
        </button>
    );
});

No QRL API, no registration — the transform derives everything. Named = transferred: signals declared as const x = ctx.signal(…) are keyed by their declaration name and serialized; anything else stays local. A handler is resumable when its captures can be expressed through the resumed scope (named signals, ctx.props reads, imports, globals); anything else (loop variables, setup helpers, ctx.emit, …) makes the whole component fall back to wake-on-interaction — first interaction hydrates it, with a build-time warning naming the capture.

Verification

The full ladder is verified in a real browser: examples/resume/smoke.mjs asserts (via JS coverage — execution, not fetches) that only the loader executes on load, the first click replays through its QRL and upgrades on write, read-only handlers never execute their component chunk, and wake-on-interaction hydrates without replay. The server half is WinterCG-clean: after pnpm build, pnpm test:edge renders a resumable boundary from the prod dist with node: imports forbidden.

Platform findings from building this pack: docs/resume-stress-test-findings.md.

Credits

The resumability model — serialized handler references, global event delegation with replay, and no client re-execution of component setup — was pioneered by Qwik. This pack adapts it to sigx signals and the @sigx/server-renderer plugin platform.