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

svelte-selfheal

v0.3.1

Published

Generate SEO friendly URLs with IDs and redirect to the canonical URL even if the URL is not correct.

Readme

svelte-selfheal

Live demo · npm · GitHub

Self-healing URLs for SvelteKit. A route like /blog/my-fancy-title-5312 looks right in search results; your load still fetches article 5312. Break the slug and the library sends a 301 to the canonical path — as long as the ID is still in the URL.

Inspired by Aaron Francis and Laravel self-healing URLs.

svelte-selfheal-gif

Canonical: /blog/my-fancy-title-5312. These all redirect to it:

  • /blog/my-fancy-but-spelled-wrong-title-5312
  • /blog/5312
  • /blog/-5312
  • /blog/THIS should NOT be r3alURL -5312

Titles change. Links get shared with typos. URLs get truncated. The ID stays stable; the slug does not. Zero runtime dependencies — Svelte 5 peer only. The library never imports SvelteKit; you call error() and redirect() in your own load functions.

Install

pnpm add svelte-selfheal

Upgrading from 0.1.x

0.3.x replaces guard with heal layers (layer / run / stack). Move fetch + segment mapping into healer.layer(); in load, check result.notFound and result.redirect instead of calling guard after a manual fetch.

See CHANGELOG → Migration for before/after snippets.

Use

Define a healer and a heal layer once (e.g. $lib/healer.ts):

import { selfheal } from 'svelte-selfheal';
import { getArticle } from '$lib/db.js';
import type { Article } from '$lib/db.js';

export const healer = selfheal();

export const healArticle = healer.layer<Article>({
  fetch: getArticle,
  segment: (article) => ({ identifier: article.id, slug: article.title })
});

Single segment (/[id]) — run parses the param, fetches the row, compares the slug:

import { healArticle, healer } from '$lib/healer.js';
import { error, redirect } from '@sveltejs/kit';

export const load = async ({ params, url }) => {
  const result = await healer.run(healArticle(params.id), url.searchParams);
  if (result.notFound) error(404, 'Article not found');
  if (result.redirect) redirect(301, result.redirect);

  const [article] = result.resources;
  return { article };
};

Nested segments (/[id]/details/[innerId]) — stack heals every layer in one redirect, including a wrong parent slug:

import { healArticle, healer } from '$lib/healer.js';
import { error, redirect } from '@sveltejs/kit';

export const load = async ({ params, url }) => {
  const result = await healer.stack(
    [healArticle(params.id), 'details', healArticle(params.innerId)],
    url.searchParams
  );
  if (result.notFound) error(404, 'Article not found');
  if (result.redirect) redirect(301, result.redirect);

  const [, article] = result.resources;
  return { article };
};

Behavior

  • Wrong or missing slug, valid ID → 301 to canonical.
  • Nested routes: each heal layer fixes its own segment; static path parts (e.g. 'details') stay as written.
  • Query strings carry over on redirect.
  • Fetch miss → notFound: true; you call error(404).

Deploying

redirect(301) in load runs on the server. Use an adapter that executes server load at request time — adapter-vercel, adapter-node, adapter-cloudflare, and similar.

adapter-static with full prerender is a poor fit for self-healing URLs: build output cannot emit real HTTP 301s. Prerender only bakes client-side redirect stubs for paths you enumerate ahead of time. Canonical pages and marketing sites without dynamic slug healing are fine on static; arbitrary wrong URLs are not.

This repo's demo uses @sveltejs/adapter-vercel on Vercel so try links show real 301 responses.

Default hyphen IDs cannot contain - (e.g. UUIDs). Use TildeIdentifierHandler for those.

Customize

Pass only the strategies you need; everything else keeps the default:

import {
  selfheal,
  SnakeSlugSanitizer,
  CaseInsensitiveComparator,
  TildeIdentifierHandler
} from 'svelte-selfheal';

export const healer = selfheal({
  sanitize: SnakeSlugSanitizer,
  isEqual: CaseInsensitiveComparator,
  identifier: TildeIdentifierHandler
});

| Export | Behavior | | ----------------------------- | --------------------------------------- | | KebabSlugSanitizer | kebab-case, diacritic folding (default) | | SnakeSlugSanitizer | snake_case | | PassthroughSlugSanitizer | trim only — slug already normalized | | NamedComparator | strict === (default) | | CaseInsensitiveComparator | ignore casing differences | | HyphenIdentifierHandler | slug-id (default) | | UnderscoreIdentifierHandler | slug_id | | TildeIdentifierHandler | slug~id — safe for UUIDs |

Development

The npm package and the demo site share this repo:

pnpm install
pnpm dev          # http://localhost:5173
pnpm test
pnpm check
pnpm lint
pnpm knip
pnpm build        # production build (demo deploys to Vercel)
pnpm package      # dist/ for npm

Set PUBLIC_SITE_URL (no trailing slash) in your deployment environment — canonical URLs and Open Graph tags read it at runtime. On Vercel, add it under Project → Settings → Environment Variables.

PUBLIC_SITE_URL=https://svelte-selfheal.vercel.app pnpm dev

Deploy the demo

The demo deploys to Vercel with @sveltejs/adapter-vercel. Connect the GitHub repo; Vercel detects SvelteKit automatically. Set PUBLIC_SITE_URL to your production origin (e.g. https://svelte-selfheal.vercel.app).

Demo load functions: src/routes/[id]/+page.server.ts, src/routes/[id]/details/[innerId]/+page.server.ts.

Copy-ready route layouts in examples/:

| Folder | API | Route shape | | ------ | --- | ----------- | | single-segment-run | healer.run() | /[id] | | nested-stack | healer.stack() | /[id]/details/[innerId] | | sync-canonical-redirect | healer.canonicalRedirect() | data already in load |

License

MIT