mirror of
https://github.com/jakejarvis/stanza.git
synced 2026-10-16 07:25:43 -04:00
- Add `/m/$slot/$id` route (`apps/web/src/routes/m.$slot.$id.tsx`) with `module-detail.ts` server function — full adapter switcher (peer chip rows), deps/devDeps/scripts table, env-var table, templates list with click-to-expand Shiki preview, and a "Try it" copy-command bar; new detail components land under `apps/web/src/components/detail/` - Add ⌘K search popover (`apps/web/src/components/search/site-search.tsx`) backed by `apps/web/src/lib/module-search.ts` — fuzzy-matches id/label/description/slot exactly as the CLI does; wired into the header - Add dynamic OG image generation via `@vercel/og` + Satori: `server/routes/og/[slot]/[id].ts` renders a per-module card, `server/routes/og.ts` renders the default fallback; both served as Nitro route handlers with appropriate CDN cache headers; OG React components in `apps/web/src/server/og-card.tsx` - Add `server/routes/sitemap.xml.ts` (enumerates `/` + all `/m/$slot/$id` URLs from registry index) and `public/robots.txt` pointing at it - Add `apps/web/src/lib/seo.ts` — `buildHead()` helper that emits `title`, `og:*`, `twitter:*`, and `canonical` tags; consumed by `__root.tsx`, `index.tsx`, and `m.$slot.$id.tsx` - Extract `ModuleLogo` into `apps/web/src/components/module-logo.tsx` — shared between slot-cards and the new detail page; remove the duplicate from `slot-cards.tsx` - Add new shadcn-style UI primitives: `command.tsx` (cmdk wrapper), `dialog.tsx`, `input-group.tsx`, `kbd.tsx`, `popover.tsx`, `textarea.tsx` - Add `apps/web/src/server/registry-base.ts` (filesystem reads to avoid SSR loopback-fetch deadlock), `registry-index.ts`, and `module-detail.ts` as the server-side data layer - Add `@vercel/og` and `cmdk` to `apps/web` dependencies; add `vitest run` as `pnpm test` in web package - Mark all corresponding TODO items done
12 KiB
12 KiB
TODO
State at end of last session: pre-1.0 architectural cleanup landed.
- B1–B6 (mechanical cleanup):
SLOTSarray is now the source of truth (was 5 hardcoded copies);pickRegistryRootdeduped; deadtelemetryIdremoved; codemod-not-found error now includes module/adapter context; orphan HTTP-bundle comment dropped;lazyProjectreturns a clean{ get, save }object. - A3: deleted
Module.provides/Module.requires/Capability— they were declared but never read by the resolver. Peer constraints already encode the same info more precisely. - A1: hoisted module-level install fields (
dependencies,devDependencies,env,scripts,consumesPackages). Adapters override per-key when needed. Better Auth's 6 adapters no longer duplicatedependencies,env, or workspace deps. - A4: web app is now the canonical registry host.
prebuildbuilds + copies the registry intoapps/web/public/registry/; deployed Vercel output ships it; the CLI's default URL points at the same path. - A2 deferred: the add-on schema (
manifest.addons[]+kinddiscriminator) is intentionally postponed until the first real add-on module forces concrete design decisions. Categorization documented in REGISTRY.md. - Cross-package wiring is now declared via module-level
consumesPackages(was per-adapterpeerPackages).stanza addcomposes modules across slots (verified:framework next→db postgres→orm drizzle→auth better-authproduces a working tree with the right adapter selection and the auth package depending on the db package). 49 unit tests pass. apps/web is a minimal Vite-native TanStack Start scaffold — needs the actual builder UI.
Web app (apps/web) — priority
The builder is functionally wired but visually unfinished. It's inline-styled radios + a code block.
- Add Tailwind 4 to
apps/web—@tailwindcss/vitev4 wired invite.config.ts, theme variables instyles.cssvia@theme(OKLCH), dark mode via.darkclass - Hand-roll a small component layer or pull in shadcn (web variant) — shadcn-style primitives at
apps/web/src/components/ui/(Card, Button, Input, Badge, Tooltip, Tabs, DropdownMenu, Separator, Sonner). All use base-ui under the hood - Replace the radio-list
Builderwith a card grid per slot —apps/web/src/components/builder/slot-cards.tsx - Show why a module is filtered out (e.g. "needs
framework: next, you picked tanstack-start") instead of just dropping it — partly covered by the tooltip inslot-cards.tsx, could expand to inline why-disabled copy on the card itself - Deep linking: encode selections in URL search params (
?framework=next&orm=drizzle&...) — typedvalidateSearchonroutes/index.tsx, parser inlib/selection.ts - Copy-to-clipboard for the generated
pnpm create stanza ...command —command-bar.tsx - Preview pane: list of files stanza will write —
file-preview.tsxwith@pierre/trees+ Shiki-rendered server-side preview - Show pinned npm versions per selected module —
apps/web/src/components/detail/deps-table.tsxon/m/$slot/$id(also covers devDeps + scripts + env). Builder cards stay clean per the design call; versions surface on the detail page - Module detail route at
/m/$slot/$id— full description, adapter switcher (peer chip rows), deps tables, env table, templates list with click-to-expand Shiki preview, "Try it" command.apps/web/src/routes/m.$slot.$id.tsx - Search route — implemented as a header-only popover (
apps/web/src/components/search/site-search.tsx) bound to ⌘K, mirrors the CLI's id/label/description/slot match (lib/module-search.ts) - Layout: header (logo, GitHub link, search), footer —
apps/web/src/components/header.tsx,footer.tsx - Dark mode — Tailwind 4
.darkvariant + ThemeProvider, system pref aware - SEO: meta tags via
head()on routes, OG image, sitemap —apps/web/src/lib/seo.tsbuildshead()output with title/og:*/twitter:*/canonical for every route. Dynamic OG via@vercel/ogat/og/$slot/$idand/og(Nitro server routes underapps/web/server/routes/).sitemap.xmlserver route enumerates the index;public/robots.txtis static - Host the registry on the same domain —
prebuildscript copiesdist/registry/intoapps/web/public/registry/. CLI'sDEFAULT_REGISTRY_URLpoints at the same path. Server-side reads use the filesystem directly (apps/web/src/server/registry-base.ts) to avoid the SSR loopback-fetch deadlock - Vercel deploy config — Vercel auto-detects TanStack Start's
.output/directory, novercel.jsonneeded. Env vars (STANZA_REGISTRY) optional override - Docs section (could be MDX routes): overview, authoring guide, registry spec
CLI (apps/cli)
The wizard and verbs work but a few things from the plan are stubbed.
- Implement opt-out PostHog telemetry — wire
posthog-node, prompt on first run, respect--no-telemetryandDO_NOT_TRACK=1 --yesflag for non-interactiveinit— takes picks from--framework / --styling / --db / --orm / --auth / --pmflags; missing slots are skipped (explicit is better than auto-default)- HTTP registry loader path is implemented but unverified — smoke test against the static JSON output
stanza init: today'sbootstrapShelldoesn't emitturbo.json. Decide whether stanza ships that or a tooling module does. (The rootpnpm-workspace.yamlis now correctly emitted withpackages/*, andapps/<dir>/package.jsonis bootstrapped so the runner's dep merges aren't silent no-ops)- Better error messages for
RegionConflictError(current message is technical; should suggeststanza remove <slot>or manual cleanup) - Build the CLI for publish —
pnpm --filter @stanza/cli buildruns tsdown, producingapps/cli/dist/bin.mjs(~64 KB unminified; ~18 KB gzipped). External npm deps (ts-morph,zod, etc.) stay external — users get them via the normal npm install chain. Workspace deps (@stanza/codemods,@stanza/registry) are inlined.create-stanzabuilds the same way (744 bytes). The published bin runs on plainnode; dev runs viatsx watch ./src/bin.ts. Bun is now gone from the dev workflow except as a maintainer-convenience shebang onscripts/*.ts - Tests for command handlers (
init,add,remove) — 10 tests inapps/cli/src/commands/commands.test.ts, exercise--yesinit, add to existing project, remove + slot-package sweep, cross-package dep cleanup
Modules
Functional but a few real issues to fix.
auth-better-authVITE_BETTER_AUTH_URL: stale — auth-client.ts defaults to current origin; server readsBETTER_AUTH_URLautomatically. No template references VITE_BETTER_AUTH_URL.auth-better-authdrizzleauth.tshardcodedprovider: "pg"— split into per-db templates (auth.drizzle.postgres.ts+auth.drizzle.sqlite.ts) so sqlite getsprovider: "sqlite"auth-better-authsqlite schema variant exists (shared/auth-schema.drizzle-sqlite.ts); confirm it matches what better-auth actually expects on SQLite end-to-endstyling-tailwind+framework-nextapp/globals.cssconflict — fixed by switching styling-tailwind's next adapter to prepend@import "tailwindcss";via theappend-to-filecodemod's newposition: "start"mode. Framework retains ownership of base styles; revert restores cleanly- tanstack-start globals.css orphan — fixed symmetrically to Next. framework-tanstack-start now ships
src/globals.cssand__root.tsximports it; styling-tailwind's tanstack adapter prepends@import "tailwindcss";viaappend-to-fileinstead of writing the file. No new codemod needed; pattern is now identical across both framework adapters. - Authoring guide: docs page covering
defineModule, slot/peer/capability semantics, template vs. codemod choice, region ownership, and thescope: "package"+consumesPackagesstory
Registry expansion
The full first-party module roadmap lives in REGISTRY.md. These are the schema/resolver changes needed before most of those modules can land.
- Add new slots to
KNOWN_SLOTS+slotOrder:api,ai,ui,payments,email,tooling,testing,deploy. Decide ordering for the wizard's topological prompts (deploy last; testing/tooling near the end; api/ai after framework) - Multi-choice slot support —
testingis the first slot where two modules co-exist (vitest + playwright). Either (a) make the slot valuestring[]instanza.jsonand update the resolver/UI, or (b) split intotesting-unit+testing-e2esub-slots. Pick before implementing the testing modules - Capability tag expansion — current set is
web | native | react | node | edge | ssr | rsc. Adding Nuxt/Svelte/Solid needsvue,svelte,solidcapabilities; existing React-only modules must addrequires: ['react']so the resolver filters correctly monorepois hardcoded to Turborepo inbootstrapShell— promote to a real slot only when a second option landspackageManagerYarn support — different workspace/lockfile semantics than pnpm/bun/npm; needs its own bootstrap branch- Cross-framework adapter explosion — Better Auth, tRPC, oRPC, the AI SDKs, etc. each need a sub-adapter per framework (next/tanstack-start/nuxt/svelte/solid). Decide whether to ship them all in one module with many adapters, or split per framework. Lean toward many-adapters-per-module to keep the slot count sane
Infrastructure
- GitHub Actions CI: lint, format check, registry build, web build (generates routeTree.gen.ts), typecheck (every workspace via turbo), tests, CLI bundle + smoke. Single workflow at
.github/workflows/ci.yml - Golden snapshot tests per module combination (per the plan's verification section) — for each valid
(framework, orm, db, auth, styling, pm)tuple, runstanza initheadless, snapshot the tree, compare against fixture - Integration test for the canonical stack — Docker Postgres + Playwright sign-up flow
- Registry deploy pipeline — on push to main, build
dist/registry/and push to Vercel/CF - npm publish workflow — Changesets configured (
.changeset/config.json, public access,@changesets/changelog-githubfor PR links). Release workflow runschangesets/action@v1on push to main: opens a "Version Packages" PR when changesets queue up; merging publishes viapnpm release(build CLI + create-stanza, thenchangeset publish). Provenance attestations enabled. Tarball dry-run verified:@stanza/cliis 20 KB,create-stanzais 1.7 KB; bothnpm install-able from a clean tmpdir and--version/--helpwork. Repo needsNPM_TOKENsecret before the first publish .env.exampleat repo root listingSTANZA_REGISTRY, PostHog key, etc.
Open items from the plan
- Domain —
stanza.tools(registry served athttps://stanza.tools/registry, web builder athttps://stanza.tools) - npm scope clearance —
@stanzascope availability (the CLI assumes@stanza/cli); fall back to unscopedstanzaif taken - Better Auth vs Clerk feature parity — Clerk wraps its own UI, Better Auth is headless; document the difference or ship shared UI stubs (
SignInForm, callback page) that each adapter fills in
Out of scope for now
These are real future work but consciously deferred — don't pull them in opportunistically.
stanza swap <slot> <to>— manifest already recordsversion+regionsto support it; slot-package extraction now means swap can replace the contents ofpackages/<dir>/without touching app importsstanza update— pinned-version 3-way merge- Third-party registry hosting — the spec exists implicitly; publish it formally later
- React Native / Expo modules — needs the
nativecapability + cross-platform framework slot - Additional first-party modules — full catalog tracked in REGISTRY.md; land the slot taxonomy changes above first