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 🙏

© 2024 – Pkg Stats / Ryan Hefner

decouter

v2.1.3

Published

* **Trie-based structure:** The main goal is to deliver a tighter UX around URL's. As your routes grow, it becomes difficult to reason about your them as a flat-list, and they often become flaky. Decouter allows you to specifiy your routes as a tree. * *

Downloads

17

Readme

Decouter

Coverage Status Build

  • Trie-based structure: The main goal is to deliver a tighter UX around URL's. As your routes grow, it becomes difficult to reason about your them as a flat-list, and they often become flaky. Decouter allows you to specifiy your routes as a tree.
  • Fast: O(1) with respect to number of routes
  • Client & Server: Avoid sending client to wrong page just to redirect + avoid duplicating route logic
  • Intuitive & Expressive: Each level can return a boolean to indicate a match, a string to indicate a redirect, an object to go a level deeper, or a promise that resolves to either of these values, or a function that returns either of these values.
  • Pure + Batteries-Included Version: Side-effect free inner function, plus works as middleware with express, go(url) for programmatic control, automatic redirects, works with history API, anchor tags (event inside Shadow DOM), extends location with params object after successful match.
  • Small: ~1kB min+gzip (ES6 version is smaller)
  • Lazy-loading: Because any segment can return a promise, you can chunk up your route logic into separate parts.

Decouter offers two main functions

  • resolve - a pure function that given a request, will resolve it against a set of routes, returning the url and params.
  • router - uses the resolve function, but takes the appropiate side-effects on the client and server.

By separating the pure resolution from the side-effects, it becomes really nice and easy to test your route logic for your app in isolation.

Resolution

import { resolve } from 'decouter'

const routes = {
  foo: {
    bar: true
  , baz: '/foo/bar'
  }
}

const { url, params } = resolve(routes)({ url: '/foo/bar' })
assert(url, '/foo/bar')

const { url, params } = resolve(routes)({ url: '/foo/baz' })
assert(url, '/foo/bar')

For variable segments, just prepend the key with a :. You can then access the value using a function:

const routes = {
  foo: {
    ':bar': bar => bar == 'boo' ? true : '/foo/boo'
  }
}

const { url, params } = resolve(routes)({ url: '/foo/bar' })
assert(url, '/foo/boo')

You can have a default (:) handler for when there is no further segment to match, or other fixed and variable handlers didn't match:

const routes = {
  foo: {
    ':fail1': false
    ':fail2': false
    ':' true
  }
}

const { url, params } = resolve(routes)({ url: '/foo/bar' })
assert(url, '/foo/bar')

You can also do relative redirects (.., ../). Here is an example with multi-level variables:

const routes = {
  ':org': org => !isValidOrg(org) ? '..' : {
    ':repo': repo => !isValidRepo(repo) ? '..' : true
  }
}

const { url, params } = resolve(routes)({ url: '/valid-org/valid-repo' })
assert(url, '/valid-org/valid-repo')

const { url, params } = resolve(routes)({ url: '/valid-org/invalid-repo' })
assert(url, '/valid-org')

const { url, params } = resolve(routes)({ url: '/invalid-org' })
assert(url, '/')

Normally you will want to do some auth before proceeding a level deeper. You can access the request object as the first parameter of functions (or the second for variable functions):

const isLoggedIn = req => req.token === 'valid'

const routes = {
  dashboard: req => !isLoggedIn(req) ? '/login' : true
, login: req => isLoggedIn(req) ? '/dashboard' : true
}

const { url, params } = resolve(routes)({ url: '/dashboard', token: 'valid' })
assert(url, '/dashboard')

const { url, params } = resolve(routes)({ url: '/dashboard' })
assert(url, '/login')

const { url, params } = resolve(routes)({ url: '/login', token: 'valid' })
assert(url, '/dashboard')

const { url, params } = resolve(routes)({ url: '/login' })
assert(url, '/login')

Server-Side

app.use(router(routes))

This will use the same routing logic to redirect requests to the right place before proceeding.

Client-side

In your top-level component/application:

function app(node, { routes }){
  const { url, params } = router(routes) 
}

This will use the same routing logic to return the correct URL and params the user should be on. You can use the result to determine which components to draw. It will also:

  • Change the URL to match accordingly
  • Extend the location object with any params it has matched, in case you don't want to manually pass this down deeply to children

You can also programmatically control navigation with:

go('/login')

Any navigation (including redirects) will emit a change event on window so you can redraw your app:

window.addEventListener('change', app.draw)