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

@metaobjectsdev/cli

v0.21.1

Published

CLI for MetaObjects: scaffold, codegen, migrate, and drift-detection commands.

Readme

@metaobjectsdev/cli

The MetaObjects CLI — scaffolds metaobjects/ + .metaobjects/, runs codegen and migrations against MetaObjects metadata.

Status: v0.3. TS reference implementation with Projects D–G shipped end-to-end.

Install

bun add -D @metaobjectsdev/cli

The SQLite/Turso driver (@libsql/kysely-libsql) ships as a direct dependency — meta migrate against a sqlite/libsql URL works out of the box. Postgres remains an optional peer (install only if you target Postgres):

  • Postgres: bun add -D pg

If a required driver is ever missing, the CLI prints an install command matching your project's package manager (npm / pnpm / yarn / bun, detected from the lockfile).

Standalone binary (no Node/Bun toolchain to run)

The CLI can be compiled to a single-file native executable with bun build --compile. The Bun runtime is embedded in the binary, so the schema operations (meta migrate and meta verify --db) run on any machine with no Node or Bun installed — the same "schema is a standalone tool" packaging as Flyway or Atlas. This lets a non-TS backend (Java, C#, Python, Go, …) adopt MetaObjects' migration + drift-detection layer without bringing in a JS toolchain.

Build the host-target binary:

bun run build:binary        # → dist/meta (compiled for the build host's OS/arch)
./dist/meta --help

The build externalizes two optional Biome WASM backends (@biomejs/wasm-bundler, @biomejs/wasm-web) that the CLI never loads — only the native Biome backend is used.

Cross-compiling for other platforms

bun build --compile cross-compiles by passing --target. To build for a platform other than the host, append the target to the build:binary invocation:

bun build ./bin/meta.ts --compile --target=bun-linux-x64   --outfile dist/meta-linux-x64   --external @biomejs/wasm-bundler --external @biomejs/wasm-web
bun build ./bin/meta.ts --compile --target=bun-linux-arm64 --outfile dist/meta-linux-arm64 --external @biomejs/wasm-bundler --external @biomejs/wasm-web
bun build ./bin/meta.ts --compile --target=bun-darwin-arm64 --outfile dist/meta-darwin-arm64 --external @biomejs/wasm-bundler --external @biomejs/wasm-web
bun build ./bin/meta.ts --compile --target=bun-darwin-x64  --outfile dist/meta-darwin-x64  --external @biomejs/wasm-bundler --external @biomejs/wasm-web
bun build ./bin/meta.ts --compile --target=bun-windows-x64 --outfile dist/meta-windows-x64.exe --external @biomejs/wasm-bundler --external @biomejs/wasm-web

SQLite driver inside the binary

The standalone binary uses Bun's built-in bun:sqlite for the sqlite dialect (it ships inside the embedded Bun runtime). The npm/Node distribution uses the bundled @libsql/kysely-libsql dependency. This is automatic — the same --db file:./app.db --dialect sqlite flags work in both. (Postgres in the standalone binary still requires the pg peer to be resolvable, because pg is a native module that --compile cannot embed; sqlite is the fully self-contained path.)

Run schema ops from the compiled binary:

./dist/meta migrate --db file:./app.db --dialect sqlite --slug initial --apply
./dist/meta verify  --db file:./app.db --dialect sqlite    # exit 0 when in sync, 1 on drift

(dist/ is git-ignored — build the binary in CI/release, don't commit it.)

Quick start

# 1. Scaffold metaobjects/ + .metaobjects/ + codegen/generators/ + metaobjects.config.ts
meta init

# 2. Author entity metadata
$EDITOR metaobjects/meta.myapp.json    # see .metaobjects/AGENTS.md for format

# 3. Generate TS code (config-driven via metaobjects.config.ts)
meta gen

# 4. Diff metadata against your DB and emit migration SQL
meta migrate --db file:./local.db --slug initial

Global options

These apply to every command:

  • --cwd <path>, -C <path> — run as if launched from <path> (default: current directory)
  • --format <toon|json|text> — output format. The default is TTY-aware: a human at a terminal gets human-readable text, while a pipe or agent gets TOON (a compact, token-efficient structured format). Pass --format json for plain JSON, or force --format text when piping but still wanting the human view. Currently honored by gen and migrate.
  • --help, -h — print help. Bare meta --help prints the full command reference; meta <command> --help prints focused usage for that command (e.g. meta migrate --help).
  • --version, -v — print the CLI version.

Running meta with no arguments prints a concise status line (whether a metaobjects/ directory is present) plus the most relevant next-step commands, rather than the full manual.

Exit codes: 0 success (including idempotent no-op runs), 1 runtime error, 2 usage error (bad flag, missing required argument, invalid --format). For agent-friendliness, structured errors and next-step hints are emitted on stdout in the active --format (not stderr), so callers can parse them without scraping stderr.

Commands

meta init

Scaffolds metaobjects/ (visible entity declarations, with a placeholder meta.common.json), .metaobjects/ (hidden tool state: config.json, package.meta.json, AGENTS.md, CLAUDE.md, .gitignore, .gen-state/), the owned codegen generators at codegen/generators/{entity,queries,routes,barrel}.ts, and metaobjects.config.ts at the repo root.

The generators are copied from the codegen reference templates and are yours to edit (ADR-0034 scaffold-and-own); the scaffolded metaobjects.config.ts imports them locally, and meta gen runs from those local copies — not from the package. Each generator file is written only if absent, so re-running with --force never clobbers a hand-edited generator.

Flags:

  • --force — overwrite scaffold files (memory records preserved)
  • --quiet — suppress next-steps output
  • --print-only — show what would be created without writing
  • --refresh-docs — refresh AGENTS.md + CLAUDE.md after a CLI upgrade

meta gen [<entity>...]

Generates TS code (Drizzle schema, Zod validators, query helpers, TanStack Query hooks, etc.) from metaobjects/ entity metadata. Generator wiring lives in metaobjects.config.ts; meta gen errors out if that file is missing.

Flags:

  • --dry-run — informational; files still written (true no-write planned)
  • --no-antipatterns — suppress the advisory anti-pattern pass (see below)

Positional args filter entities by name. All other knobs (outDir, targets, dialect, dbImport, extStyle, apiPrefix, generator list) live in metaobjects.config.ts.

On a real write run (not --dry-run), meta gen also runs an advisory anti-pattern pass — the same "verify-as-teacher" scan as meta verify. It scans your authored source for hand-rolled aggregates, money-as-float, and CHECK (... IN (...)) enums and points you at the construct that models them (origin.aggregate / field.currency / field.enum). It emits warnings only and never changes the exit code. Opt out with --no-antipatterns or META_NO_ANTIPATTERNS=1.

meta types [<query>]

Searches the metadata vocabulary (types, subtypes, @attrs) without loading it all into context — apropos + kubectl explain over the live registry. Use it to find the declarative construct for a piece of data logic instead of hand-writing it.

meta types relationship                 # subtypes/attrs whose name matches "relationship"
meta types --all money                   # search names AND descriptions ("find by what it does")
meta types --type field --kind subtype   # all field subtypes (terse)
meta types field.enum --detail           # one construct: description + when-to-use + valid @attrs
meta types --type origin --json          # machine-readable subtree

QUERY is a case-insensitive substring matched on the name (type.subType / @attr).

Flags:

  • --desc / --all — also match QUERY against descriptions + when-to-use guidance (not just names)
  • --kind <type|subtype|attr> — filter by category (comma-list ok)
  • --type <name> — scope to one top-level type (e.g. --type field)
  • --detail — drill in: full description, when-to-use, and valid @attrs
  • --json — emit the matching registry subtree verbatim (stable-sorted)
  • --limit <N> — cap results (default 20; 0 = unlimited)
  • --no-headers — omit the N of M/match-count footer (parse-friendly)

Default output is one terse line per match.

meta migrate

Diffs metaobjects/ metadata against a live DB and emits paired migration SQL files (per-migration subdirectories with up.sql and down.sql).

Flags:

  • --db <url> (required or via $DATABASE_URL or .metaobjects/config.json)
    • Supported schemes: file:, libsql:, postgres:, postgresql:
  • --dialect sqlite|postgres|d1 — auto-detected from URL scheme; use d1 for Cloudflare D1
  • --out-dir <path> (default ./.metaobjects/migrations)
  • --slug <name> — required when changes are pending (e.g., add-user-shipping)
  • --allow <csv> — destructive-change permissions: drop-column,drop-table,type-change,drop-index,drop-fk,drop-check,drop-view,nullable-to-not-null
  • --on-ambiguous abort|rename|drop-add (default abort) — non-interactive
  • --dry-run — print SQL pair to stdout, write nothing
  • --apply — after writing migration files, immediately apply all pending migrations against the DB (runs up.sql for each unapplied entry, tracked in the migration ledger). Mutually exclusive with --rollback. Postgres and SQLite only (D1 uses --apply to invoke wrangler d1 migrations apply instead).
  • --rollback <version> — roll back applied migrations newer than <version> by running their down.sql in reverse order, ledger-tracked. Pass an empty string (--rollback "") to roll back everything. Mutually exclusive with --apply. Postgres and SQLite only.

D1-specific flags (only relevant with --dialect d1):

  • --d1 <binding> — explicit D1 binding from wrangler.toml (auto-detected when there's exactly one)
  • --remote — target remote D1 (default: local)
  • --apply — invoke wrangler d1 migrations apply after writing
  • --yes — skip the --remote --apply 2-second confirmation pause

Requirements for D1: wrangler >= 3 on PATH (npm i -D wrangler).

Configuration

Two config files, by design:

metaobjects.config.ts (at repo root) — generator wiring and codegen knobs, type-checked TS. The generators are imported from the owned local copies that meta init scaffolded into codegen/generators/ (ADR-0034 scaffold-and-own), not from the package:

import { defineConfig } from "@metaobjectsdev/cli";
import { entityFile } from "./codegen/generators/entity";
import { queriesFile } from "./codegen/generators/queries";
import { routesFile } from "./codegen/generators/routes";
import { barrel } from "./codegen/generators/barrel";

export default defineConfig({
  outDir: "packages/database/src/generated",
  extStyle: "none",
  dbImport: "../index",
  dialect: "sqlite",
  apiPrefix: "/api",
  generators: [entityFile(), queriesFile(), routesFile(), barrel()],
});

Importing these generator factories from @metaobjectsdev/codegen-ts/generators still works but is deprecated (ADR-0034) — own a local copy instead. The package export will be removed in a future major.

Multiple output targets

By default every generator writes to outDir. To route each generator's output to a different directory/package — so generated code lands with its runtime concern — declare named targets and point generators at them with target:

import { defineConfig } from "@metaobjectsdev/cli";
// Owned generators scaffolded by `meta init` (ADR-0034 scaffold-and-own).
import { entityFile } from "./codegen/generators/entity";
import { queriesFile } from "./codegen/generators/queries";
import { routesFile } from "./codegen/generators/routes";
import { barrel } from "./codegen/generators/barrel";
import { formFile } from "@metaobjectsdev/codegen-ts-react";
import { tanstackQuery, tanstackGrid } from "@metaobjectsdev/codegen-ts-tanstack";

export default defineConfig({
  // The top-level outDir is the implicit "default" target — it holds the entity
  // modules (Drizzle + Zod), so it needs an importBase that other targets use to
  // import them.
  outDir: "packages/database/src/generated",
  importBase: "@acme/database/generated",
  dbImport: "../index",
  dialect: "sqlite",
  outputLayout: "package",
  apiPrefix: "/api",

  targets: {
    // routes run on the server; import db from the database package
    api: { outDir: "apps/api/src/generated", dbImport: "@acme/database" },
    // hooks/forms/grids run in the browser
    web: { outDir: "apps/web/src/generated" },
  },

  generators: [
    entityFile(), queriesFile(), barrel(),     // → default (entity-module) target
    routesFile({ target: "api" }),
    formFile({ target: "web" }),
    tanstackQuery({ target: "web" }),
    tanstackGrid({ target: "web" }),
  ],
});

A target is { outDir, importBase?, outputLayout?, dbImport? }. outputLayout and dbImport fall back to the top-level values; importBase does not inherit (it is each target's own identity). Generators with no target use the implicit default target (the top-level outDir).

How cross-target imports resolve. Every generated artifact imports the entity module (queries, routes, hooks, columns, forms). When a generator's target differs from the entity-module target (the one holding entityFile() output), that import is emitted as an extension-less package path built from the entity-module target's importBase — e.g. @acme/database/generated/acme/commerce/Program — instead of a relative ./Program. Same-target imports stay relative and honor extStyle. The entity-module target therefore must set importBase whenever any generator routes elsewhere (the runner errors with a fix-it message otherwise), and the package it lives in must expose those modules via its exports map (e.g. "./generated/*": "./src/generated/*.ts").

With no targets and no per-generator target, output is byte-identical to a single-outDir project.

.metaobjects/config.json — static project state parseable by non-TS tooling:

{
  "schema_version": 1,
  "pending_in_git": true,
  "confidence_thresholds": { "pending_promote": 0.8, "drift_warn": 0.7 },
  "sources": [],
  "extract": {},
  "migrate": {
    "outDir": "./.metaobjects/migrations",
    "databaseUrl": "file:./local.db",
    "onAmbiguous": "abort",
    "allow": []
  }
}

For D1 projects, the migrate block instead looks like:

{
  "migrate": {
    "dialect": "d1",
    "d1": { "binding": "DB", "remote": false, "autoApply": false }
  }
}

Precedence for meta migrate: CLI flag > env var (DATABASE_URL only) > .metaobjects/config.json > built-in default.

Metadata format

See .metaobjects/AGENTS.md (scaffolded by meta init) for the metaobjects metamodel rules, attribute conventions, and worked examples. Deeper references:

  • @metaobjectsdev/metadata METAMODEL.md — full metaobjects metamodel reference
  • @metaobjectsdev/sdk FORGE-METADATA.md — MetaObjects metadata additions

Not yet shipped

  • meta gen --watch (dropped) — re-run on demand
  • True 3-way merge in meta gen — codegen-ts has overwrite/skip-existing only
  • Module-reference DB connections — URL-only
  • Reified SDK APIs for adding/promoting descriptive records — hand-edit JSON for now

License

Apache-2.0.