Files
stanza/REGISTRY.md
T
jake 71f4c9d89b refactor: unify module taxonomy into a single Category concept with cardinality and home
- Replace the `slot` / `add-on` discriminated-union split with one `Category` shape carrying two orthogonal fields: `cardinality` (`"one"` for single-choice, `"many"` for coexisting) and `home` (`"app"` / `"repo"` / `"package"`) — the old `packageDir` + `repoScoped` pair is dropped
- `Module` is no longer a discriminated union; it carries a single `category` field referencing a `Category` id; `KNOWN_SLOTS`, `KNOWN_ADDONS`, `ADDON_CATEGORIES`, and all related helpers are replaced by a single `CATEGORIES` array and derived `categoryHome` / `categoryLabel` / `PACKAGE_DIRS` lookups
- Manifest schema bumped to version `0.2` — `modules` is now one record keyed by category holding arrays; `cardinality: "one"` categories are enforced to ≤ 1 entry at install time; no migration (stanza is pre-1.0 and unpublished)
- Peer/constraint logic is now emergent from cardinality: the resolver treats only `cardinality: "one"` categories as peers, so `testing` (many) can never accidentally become a peer; install routing is a single `categoryHome` lookup shared by the CLI runner, codemod runner, and web preview
- Update all CLI commands (`add`, `remove`, `init`, `list`, `search`), the wizard, `codemod-runner`, `registry-loader`, all web builder/detail/search components, server functions, OG route, sitemap, analytics, and selection logic to use the new category API
- Update all first-party `module.ts` files, `registry-build.ts`, tests, and docs (`AGENTS.md`, `REGISTRY.md`, `SKILL.md`) to reflect the unified taxonomy
2026-05-21 23:03:02 -04:00

7.3 KiB

First-party module registry

This is the canonical roadmap for the first-party modules stanza ships. Each entry maps to a registry/modules/<category>-<id>/ directory. Update this file when a module lands, gets renamed, or is dropped.

Legend: [x] added · [ ] planned

Categories

Every module fills exactly one category. A category has two independent, explicit properties (CATEGORIES in module.ts):

  • cardinality"one" (single-choice: framework, auth) or "many" (coexisting: testing, deploy). This is the only thing that decides single- vs multi-select in the wizard/web and length in the manifest.
  • home — where the module's output lands: app (manifest.appDir), repo (monorepo root), or package (packages/<dir>/).

Constraint-bearing is emergent, not a category property: a category is a peer candidate only if some module declares peers/match against it, and the resolver only treats cardinality: "one" categories as peers (PEER_CATEGORIES).

Cardinality Categories Examples
one (single) framework, styling, db, orm, auth, tooling, api, ai, ui, payments next vs tanstack-start; drizzle vs prisma
many (coexist) testing, deploy, email, monorepo vitest + playwright together

A Module carries a single category field (no kind/slot/category discriminator). The manifest stores everything in one modules record keyed by category, each holding an array (Partial<Record<CategoryId, StanzaModuleRecord[]>>); cardinality: "one" categories are kept to ≤ 1 record at install time. selectedOne/selectedAll read it ergonomically.

framework

  • tanstack-start — TanStack Start on Vite (no Vinxi). Provides web, react, ssr, node
  • next — Next.js 16 (App Router). Provides web, react, ssr, rsc, node, edge
  • nuxt — Vue. Will require relaxing the React-implicit assumption in many peer modules (capability tag vue)
  • svelte — SvelteKit. Capability svelte
  • solid — SolidStart. Capability solid

api

New slot (single-choice, constraint-bearing). Optional layer between the framework and the database/services.

  • trpc — tRPC v11; per-framework adapters (next, tanstack-start, nuxt, svelte, solid)
  • orpc — oRPC

ai

New slot (single-choice, constraint-bearing).

  • vercel-ai-sdkai package + provider sub-recipes
  • tanstack-ai — TanStack AI

auth

  • better-auth — headless, peers orm: [drizzle, prisma], framework: [next, tanstack-start]
  • clerk — hosted UI, peers framework: [next] (TanStack Start adapter planned)
  • workos — WorkOS AuthKit

orm

  • drizzle — Drizzle ORM 0.45, peers db: [postgres, sqlite]
  • prisma — Prisma 7, peers db: [postgres, sqlite]

db

  • postgrespostgres driver 3.4.x
  • sqlitebetter-sqlite3 12.x

styling

  • tailwind — Vanilla Tailwind v4, adapters per framework
  • shadcn-radix — classic shadcn/ui (Radix primitives)
  • shadcn-base — shadcn on react-base-ui

payments

New slot (single-choice, constraint-bearing). In addition to adding example files to the framework app, each of these have a plugin for better-auth -- add those too if better-auth is selected, either before or after this selection.

  • stripe — Checkout Sessions + webhooks
  • polar — Polar SDK
  • autumn
  • dodo payments

email

cardinality: many, app-scoped.

  • resend — Resend SDK + React Email templates

tooling

cardinality: one, repo-scoped. Lint/format toolchain — single-choice because the three toolchains are mutually exclusive substitutes. Bears no outbound dispatch constraints (nothing peers on tooling) but consumes a framework peer where the config varies.

  • eslint-prettier — ESLint flat config + Prettier; per-framework adapters (next, tanstack-start)
  • biome — Biome (lint + format), framework-agnostic
  • oxlint-oxfmt — Oxlint + oxfmt, framework-agnostic

testing

cardinality: many, app-scoped. Vitest and Playwright are independent and routinely coexist.

  • vitest — unit + integration; per-framework adapters (next, tanstack-start), jsdom + RTL, test/test:watch scripts
  • playwright — e2e; per-framework webServer (next dev / vite dev), test:e2e/test:e2e:ui scripts (disjoint from vitest's)

deploy

cardinality: many, repo-scoped.

  • vercelvercel.json + framework-specific output, add the nitro vite plugin for tanstack start
  • cloudflare — Workers / Pages adapter per framework
  • railwayrailway.toml + Dockerfile
  • docker — generic Dockerfile + compose for self-host

monorepo

cardinality: many, repo-scoped. Currently hardcoded in bootstrapShell as Turborepo; becomes a real category when a second option (Nx, Moonrepo) lands.

  • turborepo — Turbo 2.x (current default; not yet a configurable choice)

packageManager

Not a slot — a top-level field in stanza.json (packageManager: "pnpm" | "bun" | "npm"). Wizard prompts; codemods only touch package.json, never lockfiles.

  • pnpm (default)
  • bun
  • npm
  • yarn — needs lockfile/workspace handling that differs from the others

Install homes (package extraction)

A category's home places its modules' output in one of three places:

  • app (framework, styling, testing, email) — files land in manifest.appDir (e.g. apps/web/). For categories that wire the app shell or test it.
  • package (auth, db, orm) — files land in packages/<dir>/, named @<manifest.name>/<dir>, and the app gets a workspace:* dep. db and orm share a single packages/db/ package so the ORM client sits next to the schema it queries.
  • repo (tooling, deploy, monorepo) — config files land at the repo root and scripts/devDeps merge into the root package.json, because one config governs every workspace.

The mapping lives in the canonical CATEGORIES array as the home tagged union. categoryHome(id) and PACKAGE_DIRS are derived views. When adding a category, pick the home that matches: data layer/payments → package; app shell/router → app; repo-wide tooling → repo.

Adding a category

A one-line edit: append a Category entry to CATEGORIES with { id, label, description, cardinality, home }. CategoryId, KNOWN_CATEGORIES, PEER_CATEGORIES, and PACKAGE_DIRS all derive from it — nothing else to keep in sync. Order is topological: a category must appear after every category it can peer on, and many (leaf) categories come last. Authoring a module sets a single category field on defineModule; the runner/CLI/web handle single- vs multi-choice off the category's cardinality. Existing stanza.json files don't break (new categories are optional).