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

@galaxy-foundry/wiki-links

v0.4.0

Published

The shared [[Target]] wiki-link grammar for Foundry-pattern instances — slug, parser, exact resolver, and a dependency-free remark transform. The link map stays per-instance.

Readme

@galaxy-foundry/wiki-links

The [[Target]] wiki-link grammar Foundry-pattern instances write in, and the resolver both their renderers and their validators run on.

npm install @galaxy-foundry/wiki-links

What this package does not do

It ships no link map. That half is the instance's, and it does not transfer: one Foundry keys notes by basename plus a Mold's name field plus a tool command pair, another by a dashed collection id, the pattern site by a filename with its ordering prefix stripped.

What transfers is the grammar and the lookup rule — which three repos had independently arrived at, and written four byte-identical copies of slugify for.

The two rules

Both were the source of real divergence, so both are stated here rather than left implicit.

Resolution is exact

There is no prefix fallback. A survey of ~4,200 links across two Foundries found exactly two that resolved by prefix alone, and both were bugs:

| link | what prefix matching did | | --------------- | ----------------------------------------------------------------------------------- | | [[...]] | slugifies to "", which prefixes every key — linked to 1 of 264 notes, arbitrarily | | [[murrell-*]] | a deliberate glob meaning two papers — silently narrowed to one |

Every other link in both corpora already matched exactly. A rule that has never once done its intended job is not worth the unpredictability, so an unresolved stub stays visibly unresolved.

A backtick means the syntax, not a link

`[[Target]]` is how documentation names the token, and how a note names a slot it cannot link — `[[summary-<source>]]` is a template placeholder, not a broken reference. The remark transform therefore rewrites text nodes only, never inlineCode, code, or html.

Usage

The string layer

import { slugify, parseWikiLink, resolveWikiLink } from '@galaxy-foundry/wiki-links';

// Build your map with slugify; both sides of a lookup must run through it.
const targetMap = new Map([[slugify('Summarize Nextflow'), { id: 'molds/summarize-nextflow' }]]);

resolveWikiLink('[[Summarize Nextflow]]', targetMap); // → { id: 'molds/summarize-nextflow' }
resolveWikiLink('[[summarize-next]]', targetMap); // → undefined (no prefix fallback)

parseWikiLink('[[tests-format#has_text|the assertion]]');
// → { target: 'tests-format', anchor: '#has_text', display: 'the assertion' }

resolveWikiLink is generic in the target, so an instance keeps whatever shape it already stores — a route id, a path plus a summary for the tooltip.

WIKI_LINK_RE matches a string that is nothing but one link, which is the frontmatter-field form. WIKI_LINK_SCAN_RE finds them embedded in prose.

Anchor targets

parseWikiLink carries #section straight through to the href and never asks whether anything answers to it. For a glossary rendered from loose markdown, nothing does — unless something mints the ids. That is the other half of the same contract, so it lives here.

import { addBoldTermAnchors } from '@galaxy-foundry/wiki-links';

addBoldTermAnchors('<p><strong>Mold</strong> is a thing.</p>');
// → '<p id="mold"><strong>Mold</strong> is a thing.</p>'

It operates on rendered HTML rather than mdast because a glossary is typically rendered outside the remark pipeline, from a file the content collections do not own.

slugifyTerm is not slugify. They diverge on spaced hyphens (A - Ba---b vs a-b), underscores (kept vs dropped) and repeated hyphens (kept vs collapsed). Both instances' glossaries already carry ids minted by slugifyTerm, so unifying the two would silently repoint every existing #term deep link. A test pins the divergence.

The remark transform

import remarkWikiLinks from '@galaxy-foundry/wiki-links/remark';
import { resolveWikiLink } from '@galaxy-foundry/wiki-links';

remarkPlugins: [
  remarkWikiLinks({
    resolve: (link) => {
      const t = resolveWikiLink(link.target, map);
      return t ? { href: `${base}/${t.id}/`, title: t.summary } : null;
    },
  }),
];

The transform owns the tree walk and the grammar; resolve is the instance's half. A link that resolves becomes an anchor with the anchor fragment appended and title set; one that does not renders bold — visible to a reader, obvious to an author, and never claiming to lead somewhere.

It also never rewrites inside an existing link or linkReference, which would produce a nested anchor.

The raw-markdown rewriter

Some pages resolve links before the markdown is parsed — a glossary rendered with marked from a file the content collections do not own has no mdast to walk. That path gets the same rule, applied to the string:

import { resolveWikiLinksInMarkdown, resolveWikiLink } from '@galaxy-foundry/wiki-links';

const resolved = resolveWikiLinksInMarkdown(raw, {
  resolve: (link) => {
    const t = resolveWikiLink(link.target, map);
    return t ? { href: `${base}/${t.id}/` } : null;
  },
});
// then render `resolved` with whatever renderer the page already uses

Same resolve callback as the remark transform, and the same fallbacks: a resolved link becomes [display](href#anchor), an unresolved one **display**.

Use it instead of a regex over the document. Both instances wrote that regex, and it rewrites inside code spans — where a backtick means the syntax. Each corrupted its own glossary, on the entry defining the thing being named:

| glossary | how it rendered | | -------------------- | --------------------------------------------------------------------- | | statistical-genomics | **Wiki link** — `**Target**`. | | galaxy-workflow | … Either Mold-shaped (`mold: **...**`, optionally `loop: true`) … |

Neither was reported anywhere: a validator strips code spans before scanning, so the renderer and the checker went blind on the same text.

Masking covers fenced blocks (backtick and tilde, respecting fence length and info strings) and inline spans (a run of N backticks closed by the next run of exactly N; an unmatched run is literal text). It does not cover indented four-space blocks or raw HTML blocks — telling those from a list continuation line needs real block parsing, and a glossary of **Term** — … paragraphs has neither. Reach for a markdown parser before teaching this function to guess.

Dependency-free on purpose

The tree walk is short, and depending on unist-util-visit or @types/mdast would couple every consuming site to whichever version this package pinned. The node types in ./remark are structural, so an mdast Root satisfies them with nothing imported through here.

License

MIT