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

@grove-notes/manifest-schema

v0.1.1

Published

Static metadata schema for Grove marketplace extensions (plugins, themes, icon packs, templates).

Readme

@grove-notes/manifest-schema

Static metadata schema for Grove marketplace extensions: plugins, themes, icon packs, templates, and (stubbed) widgets.

This package owns the on-disk shipping format every Grove extension publishes — the plugin.json, theme.json, icon-pack.json, template.json, or widget.json file an author commits to their repo. It is intentionally separate from @grove-notes/plugin-sdk, which exposes Grove's runtime API surface that plugin JavaScript code calls into. The two coexist:

| Package | Audience | What it describes | |---|---|---| | @grove-notes/manifest-schema | Authors of plugins, themes, icon packs, and templates; the registry CI; any external tooling that needs to validate extension metadata. | The static JSON file an extension ships. | | @grove-notes/plugin-sdk | Authors of plugins (only — not themes/icons/templates). | The runtime API a plugin's JavaScript imports (Plugin class, PluginContext, etc.). |

The runtime representation Grove holds in memory after loading a plugin is LoadedPluginManifest in @grove-notes/plugin-sdk. The on-disk shipping shape an author writes is PluginManifest in this package. They are different concepts at different abstraction layers.

Quick start — JSON Schema (no install)

Add a $schema reference at the top of your manifest and your IDE will offer autocomplete and validation:

{
  "$schema": "https://unpkg.com/@grove-notes/manifest-schema@0/schemas/plugin.schema.json",
  "id": "kanban",
  // ...
}

The hosted schemas under unpkg.com/@grove-notes/manifest-schema@{version}/schemas/ are generated from the Valibot validators in this package and committed to the repo.

Install (for pre-submit validation in your own CI)

npm install --save-dev @grove-notes/manifest-schema
import { parsePluginManifest } from '@grove-notes/manifest-schema';
import { readFileSync } from 'node:fs';

const raw = JSON.parse(readFileSync('plugin.json', 'utf-8'));
const manifest = parsePluginManifest(raw); // throws ValiError on failure

Every per-type parser has a safeParse…Manifest counterpart that returns { success, issues } instead of throwing.

Manifest types

  • PluginManifest — JavaScript-extensible feature. Plugin code is loaded into Grove's runtime.
  • ThemeManifest — CSS-based visual customisation; may optionally bundle icons via providesIcons + iconEntry.
  • IconPackManifest — SVG sprite (single file) defining a reusable icon set.
  • TemplateManifest — Markdown templates (directory of .md files).
  • WidgetManifest — v1 stub. Accepts any object that satisfies CommonManifest; the full widget format ships in v1.x and will break this shape.

All extend CommonManifest, which carries id, name, description, version, author, minGroveVersion, license, and other shared fields. See manifest-schema.md for the field-by-field reference.

Validator behaviour

  • Strict on known field types. No coercion. "version": "1.2.0" is valid; "version": 1.2 is not.
  • Lenient on unknown top-level fields. They are silently dropped during parsing. This lets older Grove clients read manifests authored against newer schemas without failing — they pick up what they understand and ignore the rest. Authors targeting newer Grove features bump minGroveVersion so older clients filter them out entirely.
  • Strict on locale keys inside a LocalizedString. Translations are only accepted for languages Grove actually renders (default, en, de, es, zh-Hans, zh-Hant). An author writing "fr": "Bonjour" for a Grove that doesn't support French is dead data — the validator fails fast rather than silently dropping. The plugin manifest's i18n array follows the same allowlist. When Grove adds a language, this package gets a minor bump and the new code becomes valid.
  • Validation errors include the offending field path. The registry's PR-comment pipeline depends on this.

Supported locales

The accepted locale keys in LocalizedString and i18n are exported as SUPPORTED_LANGUAGES and SupportedLanguage:

import { SUPPORTED_LANGUAGES, type SupportedLanguage } from '@grove-notes/manifest-schema';
// → ['en', 'de', 'es', 'zh-Hans', 'zh-Hant'] as const

This list is the single source of truth — Grove's i18n runtime (packages/frontend/src/i18n/locale.ts) imports SUPPORTED_LANGUAGES and SupportedLanguage directly from this package, so the validator and the renderer cannot drift. Adding a language is a minor bump; removing one is a major.

Security contract (consumers must follow)

This package validates shape, not safety. Manifest fields that surface human-readable strings — name, description, author, and any future free-text fields — are untrusted user input from the perspective of any UI that renders them.

  • HTML-escape these fields before rendering. Use {{ }} / v-text interpolation; never v-html.
  • README content fetched via the readme field is untrusted markdown. Run it through a sanitiser (e.g. marked + DOMPurify) before rendering.

The SiYuan bazaar shipped without this discipline and was hit by a stored-XSS-to-RCE via displayName in 2025. Grove's schema can't enforce sanitisation at the validator boundary — but every consumer (Grove itself, the marketplace site, any external tool) must apply it at the rendering boundary.

Versioning & release cadence

The package follows semver with these definitions (see the spec, §11):

  • Patch — internal changes only. No observable behaviour change.
  • Minor — adding optional fields, loosening existing validation. Backward-compatible.
  • Major — removing/renaming fields, tightening validation, changing types.

minGroveVersion in each manifest is the lever that lets the ecosystem migrate across major bumps without graceful-degradation gymnastics. Pre-1.0 versions are unstable by convention.

Release cadence is decoupled from Grove app releases. This package's version, changelog, and publish trigger are independent of the desktop/web app's release cycle. A Grove app release does not auto-publish this package. The current trigger is manual (pnpm --filter @grove-notes/manifest-schema publish after a version bump); a tag-based GitHub Action (e.g. manifest-schema-v* tags) is planned but not yet wired.

License

MIT. Plugin authors (any license), the registry CI, and any external tooling can use this package without copyleft concerns.