Files
stanza/REGISTRY.md
T
jake 9460bceffc feat: migrate CLI to Node/tsx/tsdown, add CI pipeline, and wire root-level test + typecheck turbo tasks
- Replace Bun runtime with Node.js + `tsx` for the CLI dev path (`#!/usr/bin/env node` shebang, `tsx watch ./src/bin.ts` for `pnpm dev`) and `tsdown` for publishing — emits `dist/bin.mjs` as plain ESM; `create-stanza` gets the same treatment with its own `tsdown.config.ts`
- Add `.github/workflows/ci.yml` running lint, fmt-check, registry build, web build, typecheck, vitest, CLI bundle build, and a smoke-test (`node dist/bin.mjs --version` / `--help`) on push/PR with concurrency cancel-in-progress
- Add `apps/cli/src/commands/commands.test.ts` (196 lines, vitest) with unit tests for `add`, `init`, `list`, `remove`, and `search` command handlers; add `apps/cli/vitest.config.ts`
- Replace `apps/web/scripts/copy-registry.ts` with a pure-bash `copy-registry.sh` — no Node/tsx prerequisite on the deploy target; drop the TS version and its `predev` auto-build hook (`pnpm registry:build` must be run before the first web build)
- Add root `pnpm test` and `pnpm check-types` scripts that fan out to all workspaces via turbo; CI uses these as canonical entry points
- Update `tsconfig.json` files for `packages/registry`, `packages/codemods`, and `packages/create-stanza` to stay source-only (`noEmit: true`); add `publishConfig` pointing at `dist/` in publishable `package.json` files so workspace dev resolution uses `./src/` while published tarballs resolve `./dist/`
- Update CLAUDE.md toolchain invariants: document Node-only runtime, tsdown build pipeline, and per-workspace `dist/` paths; remove stale `bun apps/cli/src/bin.ts` dev command and all "Bun runs the CLI directly" references
2026-05-20 18:04:11 -04:00

6.2 KiB

First-party module registry

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

Legend: [x] added · [ ] planned

Categories: slots vs add-ons

Stanza's module taxonomy splits into two categories. Slots are single-choice and constrain other modules' adapter dispatch (picking Next.js influences which auth adapters are available). Add-ons can coexist freely and don't influence anyone else's adapters (vitest doesn't constrain anything).

Category Type Examples
Slots (constraint-bearing, one choice each) framework, styling, db, orm, auth, api, ai, ui, payments next vs tanstack-start; drizzle vs prisma
Add-ons (no constraints, many allowed) testing, tooling, deploy, email, monorepo vitest + playwright together; eslint or biome

Today's CLI/manifest only models slots. The add-on schema (likely a manifest.addons[] array + kind discriminator on Module) is intentionally deferred — it should be designed around real add-on modules, not in a vacuum. The first add-on (probably testing-vitest) will force the schema decisions.

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-sdk — ai 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

  • postgres — postgres driver 3.4.x
  • sqlite — better-sqlite3 12.x

styling

  • tailwind — Tailwind v4, adapters per framework

ui

New slot (single-choice, constraint-bearing). Layered on top of styling; provides component primitives.

  • shadcn-radix — classic shadcn/ui (Radix primitives)
  • shadcn-base — shadcn on react-base-ui

payments

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

  • stripe — Checkout Sessions + webhooks
  • polar — Polar SDK

email

New add-on (multi-allowed, no constraints).

  • resend — Resend SDK + React Email templates

tooling

New add-on. Lint/format toolchain. Conventionally one per project but doesn't constrain others.

  • eslint-prettier — ESLint + Prettier
  • biome — Biome (lint + format)
  • oxlint-oxfmt — Oxlint + oxfmt

testing

New add-on. Vitest and Playwright are independent and routinely coexist.

  • vitest — unit + integration
  • playwright — e2e

deploy

New add-on.

  • vercel — vercel.json + framework-specific output
  • cloudflare — Workers / Pages adapter per framework
  • railway — railway.toml + Dockerfile
  • docker — generic Dockerfile + compose for self-host

monorepo

Currently hardcoded in bootstrapShell as Turborepo. Promotes to an add-on 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

Slot-package extraction

Generated projects place each slot's output in one of two homes:

  • App-scoped (framework, styling) — files land in manifest.appDir (e.g. apps/web/). These slots wire the app shell itself, so there's no useful extraction boundary.
  • Package-scoped (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.

The mapping lives in the canonical SLOTS array as the packageDir field — SLOT_PACKAGE_DIR is just a Record<SlotId, ...> view derived from it. When you add a new slot, decide upfront whether it extracts (data layer, observability, payments) or wires the shell (router, UI primitives that mount in <html>) and set packageDir accordingly.

Slot taxonomy changes required

Adding a new slot is now a two-line edit: append the id to KNOWN_SLOTS (the as const tuple Zod needs) and append a Slot entry to SLOTS with { id, label, description, packageDir }. Order is topological — earlier slots become peer candidates for later ones. Existing stanza.json files don't break: new slots are optional, so adding them is additive.

Adding an add-on category is a larger lift because the manifest schema doesn't model add-ons yet. When the first add-on lands, expect to introduce manifest.addons[] and a discriminator on Module. See the category table at the top of this file for which planned modules are add-ons vs slots.