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

@velocms/plugin-sdk

v1.0.0-alpha.2

Published

Plugin SDK for VeloCMS — types, manifests, lifecycle hooks for first-party + community plugins

Downloads

14

Readme

@velocms/plugin-sdk

TypeScript SDK for VeloCMS plugin development.

Installation

npm install @velocms/plugin-sdk --save-dev

Quick Start

import type { PluginManifest, HookContext, AfterPostCreatePayload } from "@velocms/plugin-sdk";

// 1. Define your manifest
export const manifest: PluginManifest = {
  $schema: "https://velocms.org/schemas/plugin-v2.json",
  name: "@myorg/my-plugin",
  displayName: "My Plugin",
  version: "1.0.0",
  description: "Sends a Slack notification on every new post.",
  author: { name: "My Org", email: "[email protected]" },
  type: "integration",
  category: "social",
  icon: "./icon.png",
  engines: { velocms: ">=1.0.0" },
  capabilities: {
    content: { read: true },
    network: true,
    network_allowlist: ["hooks.slack.com"],
  },
  pricing: { model: "free" },
  entry: { runtime: "./dist/runtime.js" },
  permissions_displayed_to_user: [
    "Read your posts",
    "Make HTTP requests to Slack",
  ],
};

// 2. Export hook handlers
export async function afterPostCreate(
  payload: AfterPostCreatePayload,
  ctx: HookContext
): Promise<void> {
  await ctx.fetch("https://hooks.slack.com/services/YOUR/WEBHOOK/URL", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text: `New post published: ${payload.post.title}`,
    }),
  });
}

Phase 2.A: Event Bus

SDK version 1.0.0-alpha.2 adds a real-time event bus. Plugins can subscribe to VeloCMS system events and emit custom namespaced events.

Declare the capability

export const manifest: PluginManifest = {
  // ...
  capabilities: {
    events: {
      subscribe: ["post.published", "member.subscribed"],
      emit_custom: true,  // only if you call ctx.events.emit()
    },
  },
};

Subscribe to system events

Register subscriptions in onAppStart — VeloCMS fires this hook once when your plugin's runtime loads, which is where ctx.events.on() registrations belong (registering inside a request-scoped hook like afterPostCreate would re-subscribe the same handler on every invocation).

import type { OnAppStartPayload, HookContext } from "@velocms/plugin-sdk";

export async function onAppStart(
  _payload: OnAppStartPayload,
  ctx: HookContext
): Promise<void> {
  ctx.events.on("post.published", async ({ post }) => {
    await ctx.fetch("https://hooks.slack.com/services/xxx/yyy/zzz", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ text: `New post: ${post.title}` }),
    });
  });

  ctx.events.on("member.subscribed", async ({ member, source }) => {
    await ctx.kv.set("last_signup_source", source);
  });
}

Emit a custom event

Custom events must be namespaced to your plugin's org prefix (@org/). Other plugins can subscribe to your events by name. Emitting requires capabilities.events.emit_custom: true in your manifest.

// Emitting (from within any hook handler that receives ctx)
await ctx.events.emit("@myorg/slack-notifier:webhook-sent", {
  webhookUrl: "https://hooks.slack.com/...",
  postTitle: post.title,
});

// Subscribing (from another plugin's onAppStart)
ctx.events.on("@myorg/slack-notifier:webhook-sent", async (payload) => {
  await ctx.kv.set("last_slack_event", JSON.stringify(payload));
});

Full example: Slack Notifier

A plugin is just a module exporting a manifest plus one function per hook name it wants to handle — there is no definePlugin() wrapper. VeloCMS looks up handlers by matching the exported function name to the HookName union.

import type {
  PluginManifest,
  OnAppStartPayload,
  AfterPostPublishPayload,
  HookContext,
} from "@velocms/plugin-sdk";

export const manifest: PluginManifest = {
  $schema: "https://velocms.org/schemas/plugin-v2.json",
  name: "@myorg/slack-notifier",
  displayName: "Slack Notifier",
  version: "1.0.0",
  description: "Posts to Slack whenever a new post is published.",
  author: { name: "My Org", email: "[email protected]" },
  type: "integration",
  category: "social",
  icon: "./icon.png",
  engines: { velocms: ">=1.0.0" },
  capabilities: { network: true, network_allowlist: ["hooks.slack.com"] },
  pricing: { model: "free" },
  entry: { runtime: "./dist/runtime.js" },
  permissions_displayed_to_user: ["Make HTTP requests to Slack"],
};

export async function onAppStart(
  _payload: OnAppStartPayload,
  ctx: HookContext
): Promise<void> {
  ctx.log.info("Slack Notifier activated");
}

export async function afterPostPublish(
  payload: AfterPostPublishPayload,
  ctx: HookContext
): Promise<void> {
  const webhookUrl = await ctx.kv.get("slack_webhook_url");
  if (!webhookUrl) return;
  await ctx.fetch(webhookUrl, {
    method: "POST",
    body: JSON.stringify({ text: `New post: ${payload.post.title}` }),
  });
}

System event catalog

| Event | Payload | |-------|---------| | post.created | { post: HookPost } | | post.updated | { post: HookPost; changedFields: string[] } | | post.published | { post: HookPost } | | post.unpublished | { post: HookPost } | | post.deleted | { postId: string; slug: string } | | member.subscribed | { member: HookMember; source: string } | | member.unsubscribed | { memberId: string } | | member.tier_changed | { member: HookMember; previousTier: string } | | comment.posted | { commentId: string; postId: string; authorEmail?: string } | | comment.approved | { commentId: string; postId: string } | | comment.deleted | { commentId: string; postId: string } | | page.published | { page: PageHookData } | | media.uploaded | { mediaId: string; filename: string; mimeType: string } |

Delivery semantics

  • Best-effort, not guaranteed. If the Railway container restarts mid-flight, events in transit may be missed. Plugin handlers must be idempotent.
  • 5-second timeout per handler. A hung handler is killed; the next handler still runs.
  • Circuit-breaker. 3 consecutive handler errors → circuit opens for 5 minutes. Events are not dispatched to a circuit-open handler. Reactivating the plugin resets the circuit.
  • 30-day audit log. All events are persisted in plugin_events for 30 days. Use this for debugging via the PocketBase admin panel.

Documentation

Full SDK reference: velocms.org/developers/sdk

Publishing to the Marketplace

  1. Build your plugin: npm run build
  2. Upload to velocms.org/developers/submit
  3. The automated review pipeline will scan your bundle
  4. Manual review by the VeloCMS team (2-5 business days)
  5. Published to the marketplace

License

MIT