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

plgg-router

v0.0.1

Published

A pure client-side path toolkit (compile/match patterns, parse query) built from scratch on the plgg framework, consumed by plgg-view's application runtime

Downloads

132

Readme

plgg-router

UNSTABLE - Experimental study work. Part of the plgg monorepo.

A pure client-side path toolkit built from scratch on the plgg framework. It compiles a route pattern into Segments, matches a concrete pathname (capturing :params and a trailing *wildcard), and parses the query string — returning data, never a view. There is no DOM, no History API, and no view dependency: the toolkit is consumed by plgg-view's application runtime, which owns the Url → Model → Html loop and the popstate/link-interception seam. The only runtime dependency is plgg.

Naming: plgg-router vs. plgg-server's Routing

The word "router" has history in this repo (plgg-web → plgg-http-router → plgg-server), so to be unambiguous:

  • plgg-server's Routing is server-side: it matches an incoming request path to an HttpResponse (method-aware, Result/HttpError).
  • plgg-router is client-side path matching: it turns the browser's current path + query into a Location (path, captured params, parsed query) — pure data an app maps to a route in its Model.

The two share the same path vocabulary — Segment, :param, *wildcardby parallel definition, not by import: peer experimental packages don't depend on each other (see Considerations), so the pure path machinery is cloned rather than coupled. A reader of both packages sees the same words mean the same things.

The plgg-native model

| Concern | Type | |--------|------| | string values (path, params, query) | SoftStr | | param / query maps | Dict<string, SoftStr> | | compiled path segment | Segment = Box<"Static" \| "Param" \| "Wildcard", SoftStr> | | a matched location | Location = { path, params, query } (pure data) | | lookups | Option<SoftStr> |

Lookups are Option, never raw strings; everything here is pure, immutable data and pure functions — no classes, no methods, no platform globals.

One entry point

The package is a single environment-agnostic core import — plgg-router. It has no window/history/document, so it is safe to import anywhere, including under SSR/Node. (Earlier revisions shipped a plgg-router/client DOM/History seam and a Location → VNode router; both moved into plgg-view's application runtime when the view layer became an Elm Architecture.)

The usecases

| Function | Signature | Description | |--------|-----------|-------------| | compilePattern | (pattern) => ReadonlyArray<Segment> | compiles /users/:id / /files/*path / * into segments | | matchSegments | (segments, pathname) => Option<Dict> | matches a pathname, returning captured params on success | | parseQuery | (search) => Dict<string, SoftStr> | parses ?k=v&… into a Dict (percent-decoded) | | param | (name) => (loc) => Option<SoftStr> | reads a captured path param: pipe(loc, param("id")) | | query | (name) => (loc) => Option<SoftStr> | reads a query param: pipe(loc, query("q")) | | makeLocation | (path, params?, query?) => Location | constructs a Location |

Quick Start

See example.ts for a runnable (browser-free) version. The toolkit resolves a path to data; an app then maps that to a Msg/Model under plgg-view's application runtime.

import {
  compilePattern,
  matchSegments,
  parseQuery,
  makeLocation,
  param,
  query,
} from "plgg-router";
import { pipe, getOr, mapOption, isSome } from "plgg";

const userRoute = compilePattern("/users/:id");

// match a pathname → captured params (Option)
const matched = matchSegments(userRoute, "/users/42");
isSome(matched); // true

// fold params + query into a Location, then read them with param/query
const loc = makeLocation(
  "/users/42",
  { id: "42" },
  parseQuery("?tab=posts"),
);
pipe(loc, param("id"), getOr("?")); // "42"
pipe(loc, query("tab"), getOr("")); // "posts"

Routing

  • Static segments match verbatim: /health.
  • Params capture one segment: /users/:idpipe(loc, param("id")).
  • Wildcards capture the remainder: /files/* (named *) or /files/*path. A wildcard is effectively terminal; without one the part count must match exactly.
  • Query is parsed from a search string into a Dict; read it with pipe(loc, query("q")). Keys and values are percent-decoded, degrading to the raw token on malformed input.
  • Path params are percent-decoded; malformed encodings fall back to the raw value. Compiled segments are a plgg Box union (Static / Param / Wildcard) — defined verbatim parallel to plgg-server.

To resolve a route table, scan compiled patterns in registration order and take the first matchSegments success (first-match-wins, so register a * catch-all last). example.ts shows the few lines this takes; an app typically does it inside its update/init when mapping a Url to a route.

Considerations

  • Dependency direction. Peer experimental packages don't import each other's internals — only plgg core. That is why Segment/compilePattern/ matchSegments are cloned from plgg-server (verbatim, parallel definition) rather than shared, and why this package no longer depends on plgg-view: the view/DOM concerns live entirely in plgg-view's application runtime, which consumes this pure toolkit. If drift becomes a problem, a follow-up can lift the shared path machinery into plgg core.
  • Zero third-party runtime deps. No history, path-to-regexp, or qs — the path machinery and parseQuery are small and plgg-native.
  • Where the DOM/History loop went. Link interception (<a> click guards), popstate, pushState, and the render loop now live in plgg-view's application runtime — routing folds into the Elm Architecture (the URL lives in the Model, navigation is a Msg). This package stays pure so it is trivially testable and reusable from both client and SSR code.