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

@xcwds/plugin-update

v0.5.0

Published

Updates that wait for the user in @xcwds apps: a new version waits until the user taps Update, postponed while something a reload would interrupt is running.

Readme

@xcwds/plugin-update

App updates on the user's terms, for an @xcwds app on @xcwds/sveltekit. A new version installs in the background and waits; the page offers it, and it takes over only when the user taps Update. Until then a relaunch keeps the old version, since precached files come from the old version's cache.

// xcwds.config.ts
import { defineConfig } from '@xcwds/core';
import offline from '@xcwds/plugin-offline';
import update from '@xcwds/plugin-update';

export default defineConfig({ brand, plugins: [offline(), update({ askBeforeReload: true })] });
<!-- src/routes/+layout.svelte, inside <App> -->
<script lang="ts">
	import UpdateBanner from '@xcwds/plugin-update/UpdateBanner.svelte';
</script>

<UpdateBanner />

Options

| Option | Default | What it does | | ----------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | checkEveryMs | 3600000 | How often an open page looks for a new version (0 turns it off). It also looks on launch, when the app comes back into view and when the browser goes online. | | askBeforeReload | true | While something is busy, the banner asks before Update or Reload goes ahead. | | marker | xcwds:just-updated | The sessionStorage key the update hands over in (see below). Set it to the key an app used before moving onto @xcwds. |

Busy work

A reload throws away what a page holds in memory: a running timer, an unsaved form. Say so in either of two ways, and the banner shows "Finish your … first." and asks before reloading:

// State a component already holds:
onMount(() => useApp().update?.markBusy('timer', () => running));
// Or a hook in any client plugin:
app.addHook('onBeforeReload', () => (running ? 'timer' : undefined));

Other tabs

When one tab applies the update, the new worker takes over every tab. A hidden tab with nothing busy reloads quietly; any other tab shows "Updated in another tab" with a Reload button.

Handing over to the new version

Update leaves a marker in sessionStorage for the reloaded page: app.update.state.justUpdated is true on that first load. Plugins can carry values across with it: app.update.carry(name, () => value) adds one to the marker when it is written (the old version's code runs it), and app.update.handover() reads them in the new version (null when this load isn't an update; {} for a marker without values, such as one an app wrote before @xcwds). @xcwds/plugin-changelog carries the newest entry the old version had, so What's new knows what's new even on a device that never opened it.

app.update

state (available, reloadNeeded, and justUpdated on the first load after an update, for what's-new notes), subscribe(listener), check(), busyReasons(), markBusy(name, isBusy), carry(name, value), handover(), apply() (what Update does) and reload() (what Reload does). To change how the banner looks, pass text to <UpdateBanner>, set --xcwds-update-bg, --xcwds-update-fg and --xcwds-update-accent, or render your own from app.update.

The worker entry answers the page's { type: 'SKIP_WAITING' } message with app.worker.skipWaiting(); nothing else makes a new version take over.