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

@azlib/cms

v0.8.0

Published

A framework-agnostic, modular Content Management System (CMS) runtime inspired by WordPress, designed for modern TypeScript and headless web applications.

Readme

@azlib/cms

A framework-agnostic, modular Content Management System (CMS) runtime inspired by WordPress, designed for modern TypeScript and headless web applications.

Key Features

  • ⚙️ Declarative Content Modeling: Define collections and custom fields using a clean, type-safe DSL (defineConfig, collection, fields).
  • ⚡ Zero-Config Storage: In-memory storage for rapid prototyping and unit testing, with pluggable persistence adapters.
  • 🪝 WordPress-Style Hook Pipeline: Full Action and Filter hook system (addAction, doAction, addFilter, applyFilters) with priority ordering and async support.
  • 🕒 Publishing Workflow & Scheduler: Draft, Pending Review, Scheduled, Published, Private, and Trash lifecycle states with auto-publishing.
  • 📜 Snapshots, Revisions & Rollback: Automatic version tracking, diff computation between revisions, and one-click rollback.
  • 🗂️ Hierarchical Taxonomies: Nested category trees, flat tags, and custom taxonomy binding.
  • 🖼️ Media & Asset Management: MIME validation, file sanitization, metadata, and asset queries.
  • 🔒 RBAC & Capability Matrix: Built-in Administrator, Editor, Author, Contributor, and Subscriber roles with content ownership checks.
  • 🔌 Extensible Plugin System: Package and distribute reusable plugins (definePlugin) with schema extensions, custom API routes, lifecycle hooks, and filter pipelines.
  • 🌐 Universal Web Standard Router: Request -> Response HTTP router ready for Next.js App Router, Remix, Vite, Astro, Express, or Cloudflare Workers.
  • 🚀 Type-Safe Headless Client SDK: Query content in-process or over HTTP with zero boilerplate (createCmsClient).

Quickstart

1. Define your CMS Configuration (azlib.config.ts)

import { defineConfig, collection, fields } from "@azlib/cms";

export default defineConfig({
  site: {
    name: "Alex's Tech Journal",
    url: "http://localhost:3000",
  },
  collections: [
    collection({
      slug: "posts",
      label: "Articles",
      timestamps: true,
      revisions: true,
      taxonomies: ["categories", "tags"],
      fields: [
        fields.text({ name: "title", label: "Title", required: true }),
        fields.slug({ from: "title" }),
        fields.richText({ name: "content", label: "Content" }),
        fields.image({ name: "featuredImage", label: "Featured Image" }),
        fields.select({
          name: "status",
          options: ["draft", "published"],
          defaultValue: "draft",
        }),
      ],
    }),
  ],
});

2. Initialize the Engine

import { createCMSEngine } from "@azlib/cms";
import config from "./azlib.config.js";

const cms = createCMSEngine(config);
await cms.init();

// Create a post
const post = await cms.collection("posts").create({
  title: "Hello World",
  data: {
    content: "Welcome to my new blog powered by @azlib/cms!",
  },
});

console.log(`Created post with slug: ${post.slug}`);

3. Query with the Headless Client SDK

import { createCmsClient } from "@azlib/cms";

const client = createCmsClient({ engine: cms });

// Find published articles
const { items: posts } = await client.collection("posts").find({
  status: "published",
  limit: 10,
});

REST API Integration

Mount the universal router in any Web Standards or Node.js framework:

import { createCMSRouter } from "@azlib/cms";

const router = createCMSRouter(cms);

// In Next.js App Router (app/api/[...cms]/route.ts)
export async function GET(request: Request) {
  return router.handle(request);
}

export async function POST(request: Request) {
  return router.handle(request);
}

Plugin System

Create modular, decoupled plugins that extend collections, inject custom fields, register custom Web Standard routes, and hook into lifecycle pipelines:

import { definePlugin, fields } from "@azlib/cms";

export const seoPlugin = definePlugin<{ defaultTitleSuffix?: string }>(
  (options = {}) => ({
    name: "seo-plugin",
    version: "1.0.0",

    // 1. Inject custom fields into existing collections
    extendCollections: {
      posts: [
        fields.text({ name: "metaTitle", label: "Meta Title" }),
        fields.text({ name: "metaDescription", label: "Meta Description" }),
      ],
    },

    // 2. Programmatic setup for hooks and routes
    setup({ hooks, registerRoute, engine }) {
      // Intercept content before saving
      hooks.addFilter("cms.before_create_input", (input: any) => {
        if (
          input.title &&
          options.defaultTitleSuffix &&
          !input.data?.metaTitle
        ) {
          input.data = {
            ...input.data,
            metaTitle: `${input.title} | ${options.defaultTitleSuffix}`,
          };
        }
        return input;
      });

      // Expose custom Web Standard API route
      registerRoute("GET", "/api/seo/sitemap", async () => {
        const posts = await engine
          .collection("posts")
          .find({ status: "published" });
        const urls = posts.items.map(
          (p) => `https://example.com/posts/${p.slug}`,
        );
        return new Response(JSON.stringify({ urls }), {
          headers: { "Content-Type": "application/json" },
        });
      });
    },

    // 3. Lifecycle callbacks
    async onInit(engine) {
      console.log("[SEO Plugin] Initialized");
    },
  }),
);

Register plugins declaratively in defineConfig or dynamically via cms.use():

// Declarative registration
export default defineConfig({
  plugins: [seoPlugin({ defaultTitleSuffix: "Alex's Journal" })],
});

// Or dynamic registration
cms.use(seoPlugin());

Built-in E-commerce Plugin

Turn @azlib/cms into a complete commerce backend with pre-defined schemas, catalog taxonomies, image uploading, coupon validation, cart calculation, inventory tracking, and orders:

import {
  createCMSEngine,
  defineConfig,
  ecommercePlugin,
  getEcommerceService,
} from "@azlib/cms";

// 1. Enable the plugin in your config
export default defineConfig({
  plugins: [
    ecommercePlugin({
      defaultCurrency: "USD",
      inventoryManagement: true,
      defaultShippingCost: 15,
      defaultTaxRate: 0.08, // 8%
    }),
  ],
});

1. Catalog & Product Management

const cms = createCMSEngine();
cms.use(ecommercePlugin());
await cms.init();

const commerce = getEcommerceService(cms);

// Create hierarchical catalog category
const apparel = await commerce.createCategory({
  name: "Apparel",
  slug: "apparel",
});
const shoes = await commerce.createCategory({
  name: "Shoes",
  slug: "shoes",
  parentId: apparel.id,
});

// Create product with SKU, pricing, inventory, variants
const product = await commerce.createProduct({
  title: "Aero Running Shoes",
  price: 139.99,
  compareAtPrice: 169.99,
  sku: "RUN-AERO-01",
  stock: 25,
  status: "published",
  categoryIds: [shoes.id],
  variants: [
    { id: "v-42", title: "Size 42", price: 139.99, stock: 10 },
    { id: "v-43", title: "Size 43", price: 139.99, stock: 15 },
  ],
});

// Upload and attach product images (auto-updates featured image and gallery)
await commerce.uploadProductImage(product.id, {
  filename: "aero-shoes.jpg",
  mimeType: "image/jpeg",
  sizeBytes: 1048576,
  url: "https://example.com/uploads/aero-shoes.jpg",
  isFeatured: true,
});

2. Discounts & Coupon Engine

// Create promotional coupon
await commerce.createDiscount({
  title: "Summer 20%",
  code: "SUMMER20",
  discountType: "percentage",
  value: 20,
  minOrderAmount: 50,
  maxDiscountAmount: 30,
});

// Validate discount for a cart
const validation = await commerce.validateDiscount("SUMMER20", 100);
// => { valid: true, discountAmount: 20, code: "SUMMER20" }

3. Cart Calculation & Orders

// Calculate totals (items, discounts, shipping, and tax)
const cart = await commerce.calculateCart({
  items: [{ productId: product.id, quantity: 2 }],
  discountCode: "SUMMER20",
});

// Place an order (automatically decrements inventory and increments coupon counter)
const order = await commerce.createOrder({
  customerEmail: "[email protected]",
  customerName: "Jane Doe",
  items: [{ productId: product.id, quantity: 2 }],
  discountCode: "SUMMER20",
  shippingAddress: {
    address1: "456 Market St",
    city: "San Francisco",
    country: "US",
    postalCode: "94103",
  },
});

console.log(
  `Order placed: #${order.data.orderNumber} (Total: $${order.data.total})`,
);

4. REST API & Headless Client

Mount the universal createCMSRouter(cms) to automatically expose REST endpoints:

  • GET /api/ecommerce/products (supports ?category=...&minPrice=...&maxPrice=...&inStock=true&search=...)
  • GET /api/ecommerce/products/:idOrSlug
  • POST /api/ecommerce/products
  • POST /api/ecommerce/products/:id/images
  • GET /api/ecommerce/categories (supports ?tree=true)
  • POST /api/ecommerce/discounts/validate
  • POST /api/ecommerce/cart/calculate
  • POST /api/ecommerce/orders
  • PATCH /api/ecommerce/orders/:id/status

Or query via the Headless Client SDK:

import { createCmsClient, getEcommerceClient } from "@azlib/cms";

const client = createCmsClient({ baseUrl: "https://api.my-shop.com" });
const shop = getEcommerceClient(client);

const { items: products } = await shop.products.find({ minPrice: 50 });
const order = await shop.orders.create({ ... });

Built-in HRMS Plugin (hrmsPlugin)

@azlib/cms includes a full-featured Human Resource Management System (HRMS) plugin for multi-tenant companies, employee directory profiles, attendance tracking, and leave quota management.

Features

  • 🏢 Multi-Tenant Employers: Manage distinct companies/organizations with custom work schedules, timezones, and grace periods.
  • 👤 Employee Directory: Rich employee profiles, contract/document attachments, emergency contacts, manager hierarchies, and department taxonomies.
  • ⏱️ Daily Attendance Tracking: Clock-in and clock-out with automated duration calculation, overtime hours, and late-arrival detection against work schedules.
  • 🏖️ Leave Quota & Approval Workflow: Custom leave types (Annual, Sick, Unpaid), balance ledger reports, and manager approval/rejection pipelines.
  • 🌐 Web Standard REST APIs & Client SDK: Pre-mounted routes under /api/hrms/* and typed HRMSClient SDK.

Usage

import {
  createCMSEngine,
  defineConfig,
  hrmsPlugin,
  getHRMSService,
} from "@azlib/cms";

const config = defineConfig({
  plugins: [
    hrmsPlugin({
      workScheduleStart: "09:00",
      workScheduleEnd: "17:00",
      standardWorkDayHours: 8,
      gracePeriodMinutes: 15,
    }),
  ],
});

const cms = createCMSEngine(config);
await cms.init();

const hrms = getHRMSService(cms);

// 1. Create an Employer
const employer = await hrms.createEmployer({
  companyName: "Acme Corp",
  timezone: "America/New_York",
});

// 2. Add an Employee
const employee = await hrms.createEmployee({
  employerId: employer.id,
  employeeNumber: "ACME-001",
  firstName: "Jane",
  lastName: "Doe",
  email: "[email protected]",
  hireDate: "2024-01-15",
});

// 3. Employee Check-In & Check-Out
await hrms.checkIn({ employeeId: employee.id });
await hrms.checkOut({ employeeId: employee.id });

// 4. Request Leave & Check Balance
const leaveReport = await hrms.calculateLeaveBalance(employee.id, 2026);
console.log(`Remaining days: ${leaveReport.totalRemaining}`);

Built-in SEO & Metadata Plugin (seoPlugin)

Automates dynamic XML sitemap generation, search engine robots.txt, OpenGraph/Twitter social cards, JSON-LD structured data, and content readability/SEO auditing.

import {
  createCMSEngine,
  defineConfig,
  seoPlugin,
  getSeoService,
} from "@azlib/cms";

const config = defineConfig({
  plugins: [
    seoPlugin({
      siteUrl: "https://example.com",
      defaultTitle: "My Site",
      titleTemplate: "%s | My Site",
      autoExtendCollections: ["posts", "pages"], // Injects SEO fields into posts/pages
    }),
  ],
});

const cms = createCMSEngine(config);
await cms.init();

// Exposes REST endpoints:
// GET /sitemap.xml
// GET /robots.txt
// GET /api/seo/metadata/:collection/:id
// POST /api/seo/analyze

Built-in Search Indexing Plugin (searchPlugin)

High-performance inverted-index full-text search with automatic real-time indexing across content create, update, publish, and delete hooks.

import {
  createCMSEngine,
  defineConfig,
  searchPlugin,
  getSearchService,
} from "@azlib/cms";

const config = defineConfig({
  plugins: [
    searchPlugin({
      enableAutoIndex: true, // Automatically updates search index on content mutations
    }),
  ],
});

const cms = createCMSEngine(config);
await cms.init();

const searchService = getSearchService(cms);
const results = await searchService.search({ q: "microservices", limit: 10 });

// Exposes REST endpoints:
// GET /api/search?q=...&collections=...&limit=...
// POST /api/search/reindex

Built-in Audit Log Plugin (auditLogPlugin)

Immutable enterprise compliance logging tracking all mutations across content, media, taxonomies, options, and auth with field-level diffs.

import {
  createCMSEngine,
  defineConfig,
  auditLogPlugin,
  getAuditLogService,
} from "@azlib/cms";

const config = defineConfig({
  plugins: [auditLogPlugin()],
});

const cms = createCMSEngine(config);
await cms.init();

const auditService = getAuditLogService(cms);
const history = await auditService.query({ action: "update", limit: 20 });

// Exposes REST endpoints:
// GET /api/audit-logs?action=...&actorId=...
// GET /api/audit-logs/:id

Built-in Dynamic Forms Plugin (formsPlugin)

Dynamic form schema builder with honeypot anti-spam protection, submission validation, and storage.

import {
  createCMSEngine,
  defineConfig,
  formsPlugin,
  getFormsService,
} from "@azlib/cms";

const config = defineConfig({
  plugins: [formsPlugin()],
});

const cms = createCMSEngine(config);
await cms.init();

const formsService = getFormsService(cms);
await formsService.registerForm({
  slug: "contact",
  title: "Contact Us",
  fields: [
    { name: "fullName", label: "Full Name", type: "text", required: true },
    { name: "email", label: "Email", type: "email", required: true },
    { name: "message", label: "Message", type: "textarea", required: true },
  ],
});

// Exposes REST endpoints:
// GET /api/forms
// GET /api/forms/:slug
// POST /api/forms/:slug/submit
// GET /api/forms/:slug/submissions
// PUT /api/forms/submissions/:id/status

Built-in Frontend Cache & Revalidation Plugin (revalidationPlugin)

On-demand cache invalidation and ISR revalidation (Next.js, Vercel, Cloudflare, Astro) dispatching HMAC-signed webhook requests.

import {
  createCMSEngine,
  defineConfig,
  revalidationPlugin,
  getRevalidationService,
} from "@azlib/cms";

const config = defineConfig({
  plugins: [
    revalidationPlugin({
      endpoints: [
        {
          url: "https://my-frontend.com/api/revalidate",
          secret: process.env.REVALIDATION_SECRET,
        },
      ],
      collectionPathMap: {
        posts: (item) => [`/blog/${item.slug}`, "/blog"],
      },
    }),
  ],
});

const cms = createCMSEngine(config);
await cms.init();

// Automatically derives tags and paths and notifies frontends with HMAC signature.
// Exposes REST endpoints:
// POST /api/revalidate
// GET /api/revalidate/history

License

MIT © Google / azlib