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/plugin-sdk

v0.1.0

Published

SDK for building Grove plugins: the Plugin base class, plugin context types, registry surface types, and the generated event catalog. The only Grove package a plugin needs to depend on.

Readme

@grove-notes/plugin-sdk

SDK for building Grove plugins: the Plugin base class, plugin context types, registry/surface shapes, and the generated event catalog. The only Grove package a plugin needs to depend on.

This is the runtime API plugin JavaScript imports — distinct from @grove-notes/manifest-schema, which describes the static plugin.json shipping shape an author commits to their repo. Plugins need both: the manifest schema for what they ship, the SDK for what their code links against.

| Package | Audience | What it describes | |---|---|---| | @grove-notes/plugin-sdk | Plugin JavaScript authors (only — not themes/icons/templates). | The runtime API a plugin imports: Plugin class, PluginContext, registry shapes (SlashMenuItem, SidebarPanel, …), EventMap. | | @grove-notes/manifest-schema | Authors of plugins, themes, icon packs, templates; the registry CI. | The static *.json metadata file an extension ships. |

The in-memory LoadedPluginManifest type a plugin sees through this.manifest lives here. The on-disk PluginManifest shape an author writes lives in @grove-notes/manifest-schema. They are different concepts at different abstraction layers.

Install

npm install --save-dev @grove-notes/plugin-sdk

Quick start

import { Plugin } from '@grove-notes/plugin-sdk';

export default class MyPlugin extends Plugin {
  async onload(): Promise<void> {
    this.addCommand({
      id: 'open',
      title: 'Open my plugin',
      command: () => this.logger.info('opened'),
    });

    this.registerEvent('document.saved', (p) => {
      this.logger.info('saved', p.path);
    });
  }

  async onunload(): Promise<void> {
    // Plugin-specific teardown only. Every addX() and registerEvent()
    // call above is torn down automatically in reverse registration order.
  }
}

Two ergonomic properties of the base class:

  • Auto-cleanup. Every addCommand / addSidebarPanel / addStatusBarItem / addSettingTab / addDocumentAction / addPropertyEditor / addInspectorView / addFileHandler / registerEvent / register call records a disposable. On unload, all disposables run in reverse registration order. Plugin authors never call unregister manually.
  • ID auto-prefix. Authors write short ids ({ id: 'open' }); the class prefixes with the plugin name before forwarding to the registry, so two plugins both registering id: 'open' cannot collide.

The host's contract with your default export

The host expects each plugin module to default-export a constructor that, when called with new Ctor(ctx), produces an instance with _activate() and _deactivate() methods. Extending Plugin inherits both for free — you don't see them; you override onload() / onunload(). Authors who want to roll their own class without the SDK can implement those two methods directly; the host duck-types the shape and does not care about class identity, so bundling a copy of the SDK's Plugin works the same as any other class.

Vanilla JS (no build step)

The host also exposes the runtime Plugin class on globalThis.grove.Plugin, so vanilla-JS plugins without a bundler can write:

const { Plugin } = globalThis.grove;

export default class MyPlugin extends Plugin {
  async onload() { /* ... */ }
}

This is purely a convenience for the no-build path; bundled plugins should import { Plugin } from '@grove-notes/plugin-sdk' instead.

Exports

  • Plugin — abstract base class. Extend it; override onload() / onunload().
  • PluginContext, PluginLogger, PluginDataApi, PluginPackageJson, PluginWorkspace, LoadedPluginManifest — what the host hands to a plugin and how a plugin's manifest looks once loaded.
  • Registry surfaces — SlashMenuItem, SidebarPanel, StatusBarItem, SettingsTab, DocumentAction (+ DocumentActionContext), PropertyEditor (+ PropertyEditorContext), InspectorView, FileHandler (+ FileHandlerContext), SlashCommandContext, RenderFn, Disposable, SurfaceRegistry<T>, RegistriesFacade.
  • EventBus<M>, EventHandler<P> — the typed bus interface plugins receive in ctx.bus.
  • EventMap — generated from contract/asyncapi.yaml. The single catalog of every typed Grove event and its payload shape.

Compatibility

EventMap and PluginContext are the SDK's contract with plugin code. EventMap is generated from contract/asyncapi.yaml, so adding a new event is a minor bump; renaming or removing one is a major. See docs/adr/event-bus-and-plugin-runtime.md for the lifecycle and docs/features/plugin-runtime.md for the wire surface.

Plugin authors can extend the catalog locally via TS module augmentation:

declare module '@grove-notes/plugin-sdk' {
  interface EventMap {
    'my-plugin.thing-happened': { id: string };
  }
}

Versioning & release cadence

The package follows semver:

  • Patch — internal changes, no observable surface change.
  • Minor — adding optional fields/methods, adding events to EventMap, broadening accepted shapes. Backward-compatible.
  • Major — renaming/removing exports, tightening types, removing events.

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/plugin-sdk publish after a version bump, gated by the prepack chain clean → build → test → smoke-test); a tag-based GitHub Action keyed on plugin-sdk-v* is follow-up work.

License

MIT. Plugin authors of any license — including proprietary or GPL-incompatible projects — can depend on this package without copyleft concerns. The host process and the SDK contract are deliberately decoupled licensing-wise so the plugin ecosystem can grow without forcing a single license on every author.