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

@komeilm76/km-plugins

v0.1.2

Published

Typed plugin registry for km-packages — dependency resolution, conflict detection, semver checking, and lifecycle management.

Readme

@komeilm76/km-plugins

Typed plugin registry for the km-geoboard suite (or any TypeScript app) — dependency resolution, conflict detection, semver checking, and lifecycle management (setup/teardown). No decorators, no class hierarchies, no globals: one factory function and plain objects.

Works in Node.js ≥ 18, browsers, and edge runtimes.

Install

npm install @komeilm76/km-plugins
# or
pnpm add @komeilm76/km-plugins

No peer dependencies — this package is pure TypeScript (zod is not used here).

Quick start

import { createPluginRegistry } from '@komeilm76/km-plugins';
import type { Plugin } from '@komeilm76/km-plugins';

// 1. Define a plugin — `Plugin<API>` is generic over the API it exposes
type GreeterApi = { greet: (name: string) => string };

const greeterPlugin: Plugin<GreeterApi> = {
  id: 'com.project.greeter',     // reverse-domain notation recommended
  name: 'Greeter',
  version: '1.0.0',
  setup(_deps) {
    return { greet: (name) => `Hello, ${name}!` };
  },
};

// 2. Register it
const registry = createPluginRegistry();
const reg = registry.register(greeterPlugin);
if (!reg.success) console.error(reg.error.code); // e.g. 'already-registered'

// 3. Use its API anywhere
const api = registry.getApi<GreeterApi>('com.project.greeter');
console.log(api?.greet('world')); // "Hello, world!"

Dependencies, versions, conflicts

const mapPlugin: Plugin<MapApi> = {
  id: 'com.project.map',
  name: 'Map',
  version: '2.1.0',
  dependencies: [
    { pluginId: 'com.project.coords', minVersion: '>=1.0.0' },           // required
    { pluginId: 'com.project.theme',  minVersion: '>=1.0.0', optional: true }, // loads without it
  ],
  conflicts: [
    { pluginId: 'com.legacy.map', reason: 'Replaces the legacy map plugin' },
  ],
  setup(deps) {
    // deps: Map<PluginId, API> of resolved required + present-optional deps
    const coords = deps.get('com.project.coords') as CoordApi;
    return makeMapApi(coords);
  },
  teardown() {
    // called on unregister() / reset() — clean up timers, listeners, …
  },
};

The registry refuses registration (returning an error Result, never partial state) when a required dependency is missing or version-mismatched, a declared conflict is already registered, or the dependency graph would contain a cycle.

API

createPluginRegistry(): PluginRegistry

| Method | Returns | Description | |---|---|---| | register(plugin) | Result<PluginRegistration<API>> | Validate conflicts + deps, call setup, store the API | | unregister(id) | Result<void> | Call teardown, remove — fails if another active plugin requires it | | getApi<API>(id) | API \| null | Resolved API of a registered plugin | | has(id) | boolean | Registration check | | list() | PluginRegistration[] | All active registrations, insertion order | | reset() | void | Unregister everything in reverse order (teardown errors ignored) |

register error codes: 'already-registered', 'conflict', 'missing-dependency', 'version-mismatch', 'circular-dependency', 'setup-error'. unregister error codes: 'not-found', 'dependency-active'.

Dependency resolver (also exported standalone)

| Function | Description | |---|---| | satisfiesVersion(version, range) | Check a semver string against a ">=X.Y.Z" range | | resolveDependencyOrder(plugins) | Topological sort — returns a dependency-safe load order |

debugRegistry(registry): RegistrySnapshot

Plain-object snapshot for logging: per-plugin { id, name, version, status, dependencies, error? } plus the resolved dependencyOrder.

Types

| Type | Description | |---|---| | Plugin<API> | Plugin definition: id, name, version, dependencies?, conflicts?, setup(deps), teardown?() | | PluginId / PluginVersion | string aliases (reverse-domain / semver) | | PluginDependency | { pluginId, minVersion, optional? } | | PluginConflict | { pluginId, reason } | | PluginDepsMap | Map<PluginId, Record<string, unknown>> — passed to setup; cast each entry to its API type | | PluginRegistration<API> | { plugin, api, status: 'active' \| 'error', error? } | | PluginRegistry | The registry interface (returned by the factory; never implemented by consumers) | | RegistrySnapshot | Debug snapshot shape |

Error handling

Every fallible method returns Result<T> from @komeilm76/km-shared — the registry never throws, and a failed register leaves no partial state behind.

Related packages

| Package | Purpose | |---|---| | @komeilm76/km-geoboard | Umbrella package — this API under the plugins namespace | | @komeilm76/km-shared | The Result<T> type used throughout |

Full API reference: help.md

License

MIT — komeilm76