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

directus-extension-permalinks

v0.3.0

Published

Permalink management extension for Directus — unique cross-collection URL slugs with an interface, hook, endpoint, and module

Readme

directus-extension-permalinks

Permalink management for Directus 11. Maintain a unique URL registry across every collection in your CMS — with real-time conflict detection, URL restrictions, and automatic redirects when slugs change.


Features

  • Cross-collection uniqueness — no two items can share the same permalink, regardless of collection.
  • permalink interface — replaces the plain text input. Adds auto-generation from a sibling field (e.g. title), real-time availability checking, and a frontend preview link.
  • permalink-link interface — a link picker for fields that reference internal pages or external URLs. Autocomplete from the registry, with FK storage so links stay fresh when the target URL changes.
  • FK-based storage — both interfaces store the UUID of the ext_permalinks row, not the plain text. Changing a page's URL updates one row and all references resolve to the new value automatically.
  • /permalinks/resolve endpoint — lets your frontend resolve any URL path to a collection + item without hard-coded routing logic.
  • Automatic 301 redirects — when a permalink changes, a redirect is created automatically from the old path to the new one.
  • Loop and chain protection — the redirect engine prevents loops (A→B + B→A) and collapses chains (A→B→C becomes A→C).
  • URL restrictions — block specific paths, prefixes, or regex patterns from being used as permalinks (e.g. /admin, /api).
  • Admin module — a dedicated Directus UI for the full permalink registry, settings, and redirect management.

Installation

npm install directus-extension-permalinks

Then restart Directus. The extension creates its database tables and registers itself as Directus collections automatically on startup.

Manual installation

If you prefer to install without npm, copy the dist/ folder into your Directus extensions directory and restart:

extensions/directus-extension-permalinks/dist/

todo

TODO : Marketplace installation notes


Setup

1. Add a permalink field to a collection

In the Directus admin, add a new field to any collection (e.g. pages):

  • Type: UUID
  • Interface: Permalink

Interface options: | Option | Description | |---|---| | Generate From | Sibling field(s) to auto-generate the slug from. Use {{ field }} syntax (e.g. {{ title }}). | | Prefix | Display-only path prefix shown in the preview URL (e.g. /blog). Not stored. |

2. Configure the frontend URL

In the Directus admin, navigate to Permalinks → Settings and enter your frontend base URL (e.g. https://mysite.com). This is used to render preview links in the permalink interface.

3. Use the resolve endpoint in your frontend

Instead of querying a collection directly by slug, call:

GET /permalinks/resolve?path=/your-slug

Response (200 — found):

{
  "type": "record",
  "collection": "pages",
  "field": "permalink",
  "item_id": "abc-123",
  "permalink": "/your-slug"
}

Response (200 — redirect):

{
  "type": "redirect",
  "destination": "/new-slug",
  "statusCode": 301
}

The endpoint always returns JSON. Your frontend decides whether to issue an HTTP redirect or fetch the new URL directly.


How permalink-link works

When an editor picks an internal page via the link interface, the field stores the UUID of the ext_permalinks row — not the text. This means:

  • If the target page's URL later changes (e.g. /about/about-us), every item that linked to it will automatically resolve to /about-us — no content updates needed.
  • External URLs (https://…) are also stored as ext_permalinks rows, deduplicated by URL.

REST API Reference

Public (no auth required)

| Method | Path | Description | |---|---|---| | GET | /permalinks/resolve?path= | Resolve a URL path to collection + item | | GET | /permalinks/check?value=&collection=&field= | Check if a permalink is available | | GET | /permalinks/search?q=&limit= | Search the registry (used by permalink-link autocomplete) |

Authenticated (any logged-in user)

| Method | Path | Description | |---|---|---| | GET | /permalinks/:id | Fetch a single permalink record by UUID | | POST | /permalinks/pending | Create a pending permalink row for a new item |

Admin only

| Method | Path | Description | |---|---|---| | GET | /permalinks | List all permalinks (paginated, ?type=internal\|external) | | PATCH | /permalinks/:id | Update permalink text (creates redirect automatically) | | DELETE | /permalinks/:id | Delete permalink and nullify referencing link fields | | GET/POST/DELETE | /permalinks/restrictions[/:id] | Manage URL restrictions | | GET/PATCH | /permalinks/settings | Read / update settings | | GET/POST/DELETE | /permalinks/redirects[/:id] | Manage redirects |


URL Restrictions

Restrictions block certain paths from being registered as permalinks.

| Type | Example pattern | Matches | |---|---|---| | exact | /sitemap.xml | Exactly /sitemap.xml | | prefix | /admin | /admin and /admin/* | | regex | ^/api/ | Any path starting with /api/ |

Manage restrictions in Permalinks → Settings → URL Restrictions.


Redirects

Redirects are stored in ext_permalink_redirects and served by GET /permalinks/resolve.

  • Automatic — created whenever a permalink field value is changed via the interface.
  • Manual — add, edit, and delete via Permalinks → Settings → Redirects.

The redirect engine (upsertRedirect) handles edge cases:

  • Loop prevention: A→B + B→A → only B→A is kept.
  • Chain collapsing: A→B + B→C → A→C (single hop).

Development

npm run dev     # watch mode (rebuilds on change)
npm test        # run all tests
npm run build   # production build

See docs/architecture.md for the full internal design.


License

MIT