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

@stationhold/content-schema

v0.3.0

Published

Zod schemas for [Stationhold](https://stationhold.com) content: the frontmatter of modules, failure notes, worksites, journal entries and video tutorials, the portable exercise manifest, the extension manifest, and the extension index that the site serves

Readme

@stationhold/content-schema

Zod schemas for Stationhold content: the frontmatter of modules, failure notes, worksites, journal entries and video tutorials, the portable exercise manifest, the extension manifest, and the extension index that the site serves.

It is the contract shared by the Stationhold site, @stationhold/harness-kit and @stationhold/cli: a manifest that passes these schemas is one the CLI accepts.

Install

npm install @stationhold/content-schema

Requires Node.js 22 or later. The package is an ES module.

Use

Each export is both a Zod schema and the TypeScript type of the same name: ExerciseManifest, ExtensionManifest, IndexExtensions, ModuleFrontmatter, PanneFrontmatter, ChantierFrontmatter, JournalFrontmatter, TutorialFrontmatter, and the smaller pieces they are built from.

import { CHEMIN_INDEX_EXTENSIONS, IndexExtensions } from '@stationhold/content-schema';

const response = await fetch(new URL(CHEMIN_INDEX_EXTENSIONS, 'https://stationhold.com'));
const result = IndexExtensions.safeParse(await response.json());

if (result.success) {
  for (const extension of result.data.extensions) {
    console.log(extension.id, extension.version, extension.sha256);
  }
}

Since 0.2.0, an index entry may also carry title, the title of the manifest, and translations, keyed by language: { "en": { "title": "…", "description": "…" } }. Both fields are optional, so an index without them still passes. translations accepts any language key, so a new language on the site does not break an older CLI; the CLI only reads the languages it knows. LANGUES (fr and en), the Langue type and estLangue() are exported for the site, @stationhold/harness-kit and @stationhold/cli.

Since 0.3.0, the package also carries the proof format shared by stationhold verify, the verify.sh of each kit and the site. encoderPreuve() writes a proof block (plain text, one fact per line, French keywords: verdicts, the version, the date and the sha256: fingerprint of the deliverable, never its content), lirePreuve() finds and reads that block in any pasted text, and normaliserLivrable() is the normalisation applied before the fingerprint is taken (leading BOM dropped, line endings and trailing blanks normalised, in linear time; an equivalent awk program is documented next to it). A deliverable field may now carry label, pattern (on string, date and list), the choice type with its options, and decimal min/max on number; a deliverable may declare relations between numeric answers. A pattern is tested as written (RegExp.test), so it matches anywhere unless anchored with ^…$: [0-9a-f]{7} accepts zzz1234567zzz, ^[0-9a-f]{7,40}$ does not. validerReponses() checks the YAML header of a deliverable against its manifest, judging text answers on the text as written (0123456 keeps its zero), and returns verdicts and reason codes; it never throws. A scalar answer is read line by line: the text after key:, without the one pair of quotes around it, with no escape sequence interpreted ("pnpm test -- -t "facture"" reads pnpm test -- -t "facture"). Lists keep YAML reading, each on its own. An answer that cannot be read is ABSENT with the reason reponse-illisible and its line; the other answers are still judged.

The manifests in this repository pass 0.3.0. A manifest written for 0.2.0 may not: 0.3.0 now refuses a check id outside [A-Za-z0-9_-] or used twice, an answer key that is not lowercase snake case or is reserved (exercice, version_manifeste, date, len), decimal or negative min/max on list or string, min/max on date, boolean or choice, and pattern outside string, date and list.

Validation messages are in French, the source language of the content.

License

MIT © 2026 Yeppao. See LICENSE.

stationhold.com