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

yam-db

v0.1.1

Published

yam-db is an exploratory CLI for inspecting and editing small, Git-tracked YAML catalogs with inline JavaScript. YAML is durable state.

Readme

yam-db

yam-db is an exploratory CLI for inspecting and editing small, Git-tracked YAML catalogs with inline JavaScript. YAML is durable state.

Requires Node.js 20.18 or newer.

pnpm add -D yam-db

Catalog layout

Create .yamrc at the project root:

{
  "formatVersion": 1,
  "dataDirectory": "catalog"
}

The data directory contains schema.yaml and one <table>.yaml file per declared table:

catalog/
├── schema.yaml
├── categories.yaml
└── items.yaml

The experimental schema supports id, string, boolean, integer, enum, and json fields; nullability; unique field groups; ID references; and explicit ordering. See the complete synthetic catalog for the current format.

Validate

Run from the project or any directory beneath it:

yam validate

Validation covers the schema, values, UUIDv4 IDs, uniqueness, references, and record order.

Portable source boundary

yam-db accepts a deliberately small YAML 1.2 profile. Mappings must have string keys; anchors, aliases, tags, merge keys, and YAML directives are unsupported. Plain null, booleans, and numbers must use lowercase JSON spellings and finite JSON decimal syntax; integer values must also be safely representable by JavaScript. YAML 1.2 tokens such as yes and date-looking text remain strings. Duplicate keys are rejected in both YAML and the JSON .yamrc.

.yamrc accepts only formatVersion and a relative dataDirectory. Forward slashes are required so the same config resolves consistently on every supported operating system, and both lexical and symlink escapes from the project or data directory are rejected. See the hostile-input experiment for the complete case matrix and deliberate pre-alpha deferrals.

Inspect

Use yam run with ordinary JavaScript. all() and get(id) return isolated record copies, so reads cannot mutate the catalog accidentally. Return any JSON-serializable value to print it as YAML.

yam run '
const items = db.table("items").all()
const prompts = db.table("prompts").all()

const counts = items.map(item => ({
  slug: item.slug,
  prompt_count: prompts.filter(prompt => prompt.item_id === item.id).length,
}))

return counts
'

Pass --json to print the returned data as formatted JSON instead:

yam run --json 'return db.table("items").all()'

Returning undefined prints nothing. Read-only runs do not rewrite YAML or print a mutation summary. Scripts may still use console.log for incidental output.

Mutate

Pass one synchronous inline JavaScript function body:

yam run '
const item = db.table("items").insert({
  grouping_id: "00000000-0000-4000-8000-000000000101",
  slug: "composing-music",
  label: "Composing music",
  active: true,
  presentation: null,
  position: 40,
})

db.table("prompts").insert({
  item_id: item.id,
  intent: "origin_story",
  text: "How did you begin composing music?",
  position: 10,
})
'

yam-db wraps the supplied body automatically:

export default function ({ db }) {
  // supplied inline code
}

The table API is:

  • db.table("name").all()
  • db.table("name").get(id)
  • db.table("name").insert(recordWithoutId)
  • db.table("name").update(id, patchWithoutId)
  • db.table("name").delete(id)

Inserts generate UUIDv4 IDs. IDs cannot be supplied or updated. Inserted and updated records are checked immediately for unknown or missing fields and invalid values, so a script may catch an operation error without retaining an invalid record. Catalog-wide uniqueness and reference checks remain deferred until the script completes, allowing one script to repair temporary cross-record or cross-table inconsistency. The final records are sorted and the entire in-memory result is validated before affected table files are rewritten deterministically. If the script throws or validation fails, no table file is written.

An update whose patch is already logically equal returns the existing record without incrementing the update count or rewriting the table. Equality follows yam-db's JSON value semantics: object key order is ignored, while array order is significant.

Script files, stdin, TypeScript, imports, async scripts, SQL, SQLite, sharding, caching, export commands, and production durability are intentionally unsupported.

Materialize downstream

Use the package API to read a validated logical snapshot. With no options, discovery starts at process.cwd() and walks upward to the nearest .yamrc:

import { fingerprintContent, readCatalogSnapshot } from 'yam-db'

const snapshot = readCatalogSnapshot()
const prompt = snapshot.tables.prompts[0]
const promptRevision = fingerprintContent({
  text: prompt.text,
  options: prompt.options,
})

The snapshot contains the schema, logical tables, and a deterministic SHA-256 fingerprint. An application-owned importer can skip work when that fingerprint is already materialized. Otherwise, it should transactionally upsert every record by immutable ID, stamp each row with the current import ID, reactivate rows that reappear, and soft-delete managed rows not stamped by the current import. Scope soft deletion to the catalog source so application-owned rows are never affected.

Disposable development databases do not need content revisions; update their current projection in place. Before retaining production or production-like events whose exact historical content matters, the importer should choose the revision fields and pass only those fields to fingerprintContent. If unnecessary Postgres updates begin advancing timestamps or firing triggers, use IS DISTINCT FROM guards for domain fields while still updating the import marker required for absence detection. yam-db intentionally provides no Postgres driver, SQL generation, migrations, or materialization command. See the downstream materialization convention.

Agent workflow

The repository includes a yam-db skill that teaches agents to validate, inspect, mutate, revalidate, and review the Git diff.

Development

pnpm typecheck
pnpm lint
pnpm test --run
pnpm build

The broader architecture remains documented under design.