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

obsidiary

v0.1.2

Published

Isomorphic core for Obsidiary — parses an Obsidian vault into notes, links and a graph. Runs at build time in Node and at runtime in the browser.

Downloads

405

Readme

obsidiary

The isomorphic core of Obsidiary: parses a folder of Obsidian markdown into notes, links, a graph and a search index.

Nothing in this package imports node:*. That is the constraint the whole design hangs on — the static build and the in-browser live mode run this exact code, and a stray Node import is what turns "one renderer" back into two renderers that quietly disagree.

Most people want @obsidiary/astro, which wraps this into a site. Use this package directly if you are building a different front end.

Install

npm i obsidiary

Use

import { buildVault, createRenderer } from 'obsidiary';

const index = buildVault([
  { path: 'index.md', content: '# Home\n\nSee [[Ideas/Zettelkasten]].' },
  { path: 'Ideas/Zettelkasten.md', content: '# Zettelkasten\n\n#method' },
]);

index.notes['index.md'].links;   // resolved outgoing links
index.backlinks['Ideas/Zettelkasten.md'];
index.graph;                     // { nodes, edges, unresolved }

const renderer = createRenderer(index);
renderer.renderNote('index.md');            // HTML, embeds expanded
renderer.renderMarkdown('[[Home]]', 'a.md') // arbitrary markdown, resolved from a path
renderer.astFor('index.md');                // transformed mdast

buildVault(files, options) takes { path, content } pairs from anywhere — a filesystem, a git host, a database — which is why it works unchanged in a browser.

Reading a vault straight from GitHub

import { parseRepoRef, fetchVaultFiles, buildVault } from 'obsidiary';

const ref = parseRepoRef('you/notes@main');
const remote = await fetchVaultFiles(ref);
const index = buildVault(remote.files, {
  assetBase: remote.rawBase,
  preserveAssetPaths: true,
});

Two endpoints, both CORS-open, so this runs in a browser as happily as in a build. The ref is resolved to a commit SHA first and every file is read at that SHA — branch-name raw URLs are CDN-cached per edge with independent expiry, so reading a hundred files by branch can return a mixture of revisions.

What it handles

  • Wikilinks in every form: [[note]], [[note|alias]], [[note#heading]], [[note#^block]], [[#heading]], relative and absolute paths
  • Obsidian's real resolution rules, including proximity — a bare [[Zettel]] finds the one in your folder before the one at the root — with case-insensitive matching that still prefers an exact case hit, and aliases last
  • Transclusion that inlines real content: whole notes, sections, single blocks, with cycle and depth guards
  • Callouts, all types and aliases, folding via <details> with no JavaScript
  • Tags (nested, indexed by ancestor), frontmatter aliases, block anchors, GFM tables and task lists, LaTeX
  • Backlinks, a link graph, unresolved-link tracking
  • Attachments including |300x200 sizing, video, audio, PDF
  • .canvas files as first-class notes — a canvas gets a slug, a URL, a place in the tree, an entry in search (indexed by node text, not JSON) and its own graph links, because it is a Note with kind: 'canvas' rather than a special case bolted on the side

Also exported

  • buildSearchIndex() and search() — ranked, typo-tolerant, and it reports why something matched: title, alias, heading, tag or body
  • createSimulation() — a dependency-free force layout, ~150 lines, grid cutoff for repulsion, and no Math.random(), so the same vault always lays out identically. Less disorienting, and testable.
  • detectCommunities() — Louvain modularity optimisation, used to colour graph clusters
  • buildGraph(), fullGraph(), neighborhood(), orphanNotes() — the graph
  • buildFileTree(), buildBacklinks(), buildTagIndex(), buildLookups()
  • parseCanvas(), canvasLinks(), edgeGeometry() — canvas internals
  • resolveTarget(), parseNote(), splitFrontmatter(), noteSlug(), excerptAround(), summarize()

Options

explicitPublish · removeDrafts · ignore · base · assetBase · preserveAssetPaths — see the docs.

License

MIT