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

koguma

v2.3.10

Published

🐻 A little CMS with big heart β€” schema-driven, runs on Cloudflare's free tier

Downloads

3,747

Readme

Koguma gives you a headless CMS with a beautiful admin dashboard, all powered by Cloudflare Workers + D1 + R2. Define your content types in code, and Koguma handles the rest β€” API routes, admin UI, media storage, and a file-based content/ directory for version-controlled content.


Quickstart

1. Install

bun add koguma
# or
npm install koguma

2. Define your content

Create a site.config.ts in your project root:

import { defineConfig, contentType, field } from 'koguma';

export default defineConfig({
  siteName: 'My Site',
  contentTypes: [
    contentType({
      id: 'post',
      name: 'Blog Post',
      displayField: 'title',
      fields: {
        title: field.text('Title').required(),
        slug: field.text('Slug').required(),
        body: field.markdown('Body'),
        heroImage: field.image('Hero Image'),
        published: field.boolean('Published').default(false),
        date: field.date('Published Date')
      }
    })
  ]
});

3. Create your worker

Create a worker.ts:

import { createWorker } from 'koguma/worker';
import config from './site.config';

export default createWorker(config);

4. Configure

Create koguma.toml:

name = "my-site"
database_id = ""    # filled by `koguma init`

That's it β€” two lines. Everything else is derived by convention:

  • D1 database β†’ my-site-db
  • R2 bucket β†’ my-site-media
  • Worker name β†’ my-site

5. Deploy

koguma init    # Scaffold project, create D1 + R2 on Cloudflare
koguma push    # Build + deploy + sync content

Your admin dashboard is at /admin. Your API is at /api/content/:type.


Content Schema

Field Types

| Field | Usage | Stored As | | -------------------------------- | ---------------------- | ------------------------ | | field.text(label) | Titles, slugs, URLs | TEXT | | field.longText(label) | Descriptions, bios | TEXT | | field.markdown(label) | Rich formatted content | TEXT (markdown string) | | field.number(label) | Counts, order | number in JSON | | field.boolean(label) | Toggles | boolean in JSON | | field.date(label) | Timestamps | string (ISO 8601) | | field.select(label, {options}) | Dropdowns | string | | field.url(label) | URLs | string | | field.email(label) | Email addresses | string | | field.phone(label) | Phone numbers | string | | field.color(label) | Hex colours | string | | field.youtube(label) | YouTube video IDs | string | | field.instagram(label) | Instagram handles | string | | field.image(label) | Image from R2 media | string (asset ID) | | field.images(label) | Array of images | string[] (asset IDs) | | field.ref(typeId, label) | Link to another entry | string (entry ID) | | field.refs(typeId, label) | Array of entry links | string[] (entry IDs) |

All fields stored inside a JSON data blob in the entries table β€” no per-field columns, no migrations.

Content Type Options

contentType({
  id: "page",
  name: "Page",
  displayField: "title",
  singleton: true,           // Only one entry allowed (e.g. site settings)
  fields: { ... },
});

CLI Reference

All commands auto-detect your project root by looking for koguma.toml.

| Command | Description | | ------------------ | ----------------------------------------------------------------- | | koguma init | Interactive project setup β€” scaffold, create D1 + R2, set secret | | koguma dev | Auto-sync content/ β†’ local D1, generate types, start dev server | | koguma push | Build + deploy + sync content to remote | | koguma pull | Download remote content + media β†’ local content/ files | | koguma gen-types | Generate koguma.d.ts typed interfaces | | koguma tidy | Validate content/ against config, sync dirs, check fields |


Content Directory

Koguma uses a content/ directory for file-based content that lives in your git repo:

content/
β”œβ”€β”€ post/                          # collection β€” one file per entry
β”‚   β”œβ”€β”€ hello-world.md             # slug = "hello-world"
β”‚   β”œβ”€β”€ our-mission.md
β”‚   └── our-mission.heroBody.md    # sibling markdown field
β”œβ”€β”€ landingPage/                   # singleton β€” always index.md
β”‚   β”œβ”€β”€ index.md                   # slug = content type ID
β”‚   β”œβ”€β”€ heroBody.md                # sibling markdown field
β”‚   └── ctaBody.md                 # sibling markdown field
└── media/                         # local images (synced to R2)
    └── hero-banner.jpg

File Format

Every content file uses YAML frontmatter + markdown body:

---
title: Our Mission
slug: our-mission
heroImage: media-hero-banner
published: true
date: 2026-03-01
---

This body content maps to the **first** `field.markdown()` in the content type.

Singletons vs Collections

  • Collections (default): Each entry is a file named {slug}.md
  • Singletons (singleton: true): One entry, always index.md. The slug is auto-set to the content type ID.

Sibling Markdown Fields

When a content type has multiple field.markdown() fields, the first one maps to the file body. Additional markdown fields are stored as sibling files (pure markdown, no frontmatter):

| | Collections | Singletons | | ------------------- | --------------------- | -------------- | | Main file | {slug}.md | index.md | | Sibling pattern | {slug}.{fieldId}.md | {fieldId}.md |

Example singleton with 3 markdown fields:

content/landingPage/
β”œβ”€β”€ index.md           # frontmatter + 1st markdown field body
β”œβ”€β”€ heroBody.md        # 2nd markdown field (pure markdown)
└── ctaBody.md         # 3rd markdown field (pure markdown)

Git is your version history. No custom versioning table needed.


API Routes

Public (no auth)

| Method | Route | Description | | ------ | ------------------------ | --------------------------------------- | | GET | /api/content/:type | List all entries of a content type | | GET | /api/content/:type/:id | Get a single entry (with resolved refs) | | GET | /api/media/:key | Serve a media file from R2 |

Admin (auth required)

| Method | Route | Description | | -------- | ---------------------- | ----------------------------------- | | POST | /api/auth/login | Login with { password } | | POST | /api/auth/logout | Clear session | | GET | /api/auth/me | Check authentication | | GET | /api/admin/schema | Content type schemas (for admin UI) | | GET | /api/admin/:type | List entries | | GET | /api/admin/:type/:id | Get entry | | POST | /api/admin/:type | Create entry | | PUT | /api/admin/:type/:id | Update entry | | DELETE | /api/admin/:type/:id | Delete entry | | GET | /api/admin/media | List media assets | | POST | /api/admin/media | Upload media (multipart form) | | DELETE | /api/admin/media/:id | Delete media |

References and images are automatically resolved to nested objects in the public API (up to 2 levels deep).


Package Exports

import { defineConfig, contentType, group, field, type Infer } from 'koguma';
import { createWorker } from 'koguma/worker';
import { createClient } from 'koguma/client';
import { Markdown, useEntry, useEntries } from 'koguma/react';
import type { KogumaAsset, EntryReference } from 'koguma/types';

Local Development

# Create .dev.vars with your local admin password
echo "KOGUMA_SECRET=your-password" > .dev.vars

# Start dev server (auto-syncs content/, generates types)
koguma dev

# Admin dashboard is at http://localhost:8787/admin
# API is at http://localhost:8787/api/content/:type

Architecture

Your Project
β”œβ”€β”€ site.config.ts     ← Content type definitions
β”œβ”€β”€ worker.ts          ← Entry point (imports koguma/worker)
β”œβ”€β”€ koguma.toml        ← Project config (name + database_id)
β”œβ”€β”€ .dev.vars          ← Local secrets (KOGUMA_SECRET)
└── content/           ← Version-controlled content files
    β”œβ”€β”€ post/
    β”‚   └── hello-world.md
    └── siteSettings/
        └── index.md

Koguma (this package)
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ config/        ← Schema definitions (defineConfig, field types)
β”‚   β”œβ”€β”€ api/           ← Hono router (CRUD + media + auth)
β”‚   β”œβ”€β”€ db/            ← D1 queries (JSON document store)
β”‚   β”œβ”€β”€ auth/          ← HMAC-signed cookie sessions
β”‚   β”œβ”€β”€ media/         ← R2 upload/serve/delete
β”‚   β”œβ”€β”€ admin/         ← Dashboard HTML shell + JS/CSS bundle
β”‚   β”œβ”€β”€ client/        ← Fetch client for consuming the API
β”‚   └── react/         ← React hooks + <Markdown> component
β”œβ”€β”€ admin/             ← Vite + React admin dashboard source
└── cli/               ← CLI commands (init, dev, push, pull, gen-types, tidy)

License

MIT