diff --git a/turbo/generators/__snapshots__/core-package/audit.snapshot.json b/turbo/generators/__snapshots__/core-package/audit.snapshot.json index a28f30d..81eece5 100644 --- a/turbo/generators/__snapshots__/core-package/audit.snapshot.json +++ b/turbo/generators/__snapshots__/core-package/audit.snapshot.json @@ -3,6 +3,14 @@ "path": "AGENTS.md", "sha256": "65b3fe18cf85c2fb23fe88b5d7bda102316eccee7539c0d326947eb1c0b9a645" }, + { + "path": "docs/library-decisions/2026-05-14-@trpc/server.md", + "sha256": "40217b9dd1938083adaf3ccf5faa586447b82c5df60c30f662809f3cd668e4d6" + }, + { + "path": "docs/library-decisions/2026-05-14-zod.md", + "sha256": "8f18b4c90031dd453c278f549b8d3dbc20083aacef2e6a422233a893afa29bd9" + }, { "path": "eslint.config.js", "sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745" diff --git a/turbo/generators/__snapshots__/core-package/events.snapshot.json b/turbo/generators/__snapshots__/core-package/events.snapshot.json index ee9e98b..6375f42 100644 --- a/turbo/generators/__snapshots__/core-package/events.snapshot.json +++ b/turbo/generators/__snapshots__/core-package/events.snapshot.json @@ -3,6 +3,10 @@ "path": "AGENTS.md", "sha256": "8f476285d06563b0d0a3b76ab31bec49311cdc81dffd81f71f4893126270d056" }, + { + "path": "docs/library-decisions/2026-05-14-zod.md", + "sha256": "6b159ae8890a51a0ff1cc94a8cb1ee70bc1343a87fc2e6e4d0b197a6c5801ff7" + }, { "path": "eslint.config.js", "sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745" diff --git a/turbo/generators/__snapshots__/core-package/realtime.snapshot.json b/turbo/generators/__snapshots__/core-package/realtime.snapshot.json index f8f8bb4..08328d9 100644 --- a/turbo/generators/__snapshots__/core-package/realtime.snapshot.json +++ b/turbo/generators/__snapshots__/core-package/realtime.snapshot.json @@ -3,6 +3,14 @@ "path": "AGENTS.md", "sha256": "a9b9527c327a4164ee4ffca81727118f3876711c1cf3f0b8f32542d08531ccc3" }, + { + "path": "docs/library-decisions/2026-05-14-socket.io.md", + "sha256": "7fd94c82037bdb92a90329355123950132050ca6b7b828292359438f8c9a562f" + }, + { + "path": "docs/library-decisions/2026-05-14-zod.md", + "sha256": "412f92181566e983259982cefc0c7c3b632f289d566971badc855f7a5a03f4a5" + }, { "path": "eslint.config.js", "sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745" diff --git a/turbo/generators/__snapshots__/core-package/trpc.snapshot.json b/turbo/generators/__snapshots__/core-package/trpc.snapshot.json index 55676ae..efd81a2 100644 --- a/turbo/generators/__snapshots__/core-package/trpc.snapshot.json +++ b/turbo/generators/__snapshots__/core-package/trpc.snapshot.json @@ -3,6 +3,34 @@ "path": "AGENTS.md", "sha256": "b1b9c2c1be6925f37576841d88929916286dc530a2ad979e675d038235aab6ad" }, + { + "path": "docs/library-decisions/2026-05-14-@tanstack/react-query.md", + "sha256": "9e3fe29d5e6f9a9b81fa70a01f966f485e8a7c1a841a1654e117e3171e07aa6c" + }, + { + "path": "docs/library-decisions/2026-05-14-@trpc/client.md", + "sha256": "0c871657850954ee7fb0141070c36f8aa0c79932c510773d292aaf8e65ceb871" + }, + { + "path": "docs/library-decisions/2026-05-14-@trpc/react-query.md", + "sha256": "fc02764ca7727d6353ccdc7230f90c5054f6976304ef6c81d7864a917815f002" + }, + { + "path": "docs/library-decisions/2026-05-14-@trpc/server.md", + "sha256": "094ae3505561e84090002a8646967a0ae9dcf4ce121bbbc240da06af2cc078c8" + }, + { + "path": "docs/library-decisions/2026-05-14-@trpc/tanstack-react-query.md", + "sha256": "f139e506a7b09bd0154818cb9616c7a80295d191a89b7fbd7e3fd2eb7912e8f2" + }, + { + "path": "docs/library-decisions/2026-05-14-react.md", + "sha256": "dbd8727184632f403404a34f7d26be572461bbbf8923cbbf747438ae4feb1529" + }, + { + "path": "docs/library-decisions/2026-05-14-superjson.md", + "sha256": "b6404b77aa0e222fd6100a7b29e2572f75103a01c7d905d112d90a31e8443eed" + }, { "path": "eslint.config.js", "sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745" diff --git a/turbo/generators/__snapshots__/core-package/ui.snapshot.json b/turbo/generators/__snapshots__/core-package/ui.snapshot.json index c9a9760..3a80cc0 100644 --- a/turbo/generators/__snapshots__/core-package/ui.snapshot.json +++ b/turbo/generators/__snapshots__/core-package/ui.snapshot.json @@ -3,6 +3,18 @@ "path": "AGENTS.md", "sha256": "626329a8a409b4428307ba0014fb8be179a5db7d1fc2a43b017941ab44476188" }, + { + "path": "docs/library-decisions/2026-05-14-clsx.md", + "sha256": "3507768e0ee216dac6983b35b1ac7a6326a85f35a659863d266a340655602429" + }, + { + "path": "docs/library-decisions/2026-05-14-react.md", + "sha256": "9d6178b1ec0b0ddba60b83ef8c5d65791f6890092e18d1813f7a2db1e8bb1e94" + }, + { + "path": "docs/library-decisions/2026-05-14-tailwind-merge.md", + "sha256": "e8e2474dfc62f95f0671c659c04055371dec7dc9a2f2d26fc318eccaa7d59504" + }, { "path": "eslint.config.js", "sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745" diff --git a/turbo/generators/config.ts b/turbo/generators/config.ts index 8140f7b..28cf81a 100644 --- a/turbo/generators/config.ts +++ b/turbo/generators/config.ts @@ -634,6 +634,11 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js"; return "Guard passed — packages/core-realtime does not exist yet."; }, ...emitTemplateTree("core-package/realtime", "packages/core-realtime"), + ...emitTemplateTree( + "core-package/realtime/docs/library-decisions", + "docs/library-decisions", + { force: true }, + ), ...emitTemplateTree( "core-package/realtime-eslint-rules", "packages/core-eslint/rules", @@ -677,6 +682,11 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js"; return "Guard passed — packages/core-events does not exist yet."; }, ...emitTemplateTree("core-package/events", "packages/core-events"), + ...emitTemplateTree( + "core-package/events/docs/library-decisions", + "docs/library-decisions", + { force: true }, + ), () => { addToTranspilePackages( "apps/web-next/next.config.mjs", @@ -700,6 +710,11 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js"; return "Guard passed — packages/core-trpc does not exist yet."; }, ...emitTemplateTree("core-package/trpc", "packages/core-trpc"), + ...emitTemplateTree( + "core-package/trpc/docs/library-decisions", + "docs/library-decisions", + { force: true }, + ), () => { addToTranspilePackages( "apps/web-next/next.config.mjs", @@ -715,6 +730,11 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js"; return "Guard passed — packages/core-ui does not exist yet."; }, ...emitTemplateTree("core-package/ui", "packages/core-ui"), + ...emitTemplateTree( + "core-package/ui/docs/library-decisions", + "docs/library-decisions", + { force: true }, + ), () => { addToTranspilePackages( "apps/web-next/next.config.mjs", @@ -730,6 +750,11 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js"; return "Guard passed — packages/core-audit does not exist yet."; }, ...emitTemplateTree("core-package/audit", "packages/core-audit"), + ...emitTemplateTree( + "core-package/audit/docs/library-decisions", + "docs/library-decisions", + { force: true }, + ), () => { addToTranspilePackages( "apps/web-next/next.config.mjs", diff --git a/turbo/generators/lib/core-package-utils.ts b/turbo/generators/lib/core-package-utils.ts index 7dabc9e..0279a60 100644 --- a/turbo/generators/lib/core-package-utils.ts +++ b/turbo/generators/lib/core-package-utils.ts @@ -1,4 +1,10 @@ -import { existsSync, readFileSync, writeFileSync, readdirSync, statSync } from "node:fs"; +import { + existsSync, + readFileSync, + writeFileSync, + readdirSync, + statSync, +} from "node:fs"; import { join, relative } from "node:path"; import type { PlopTypes } from "@turbo/gen"; @@ -61,8 +67,7 @@ export function splicePluginRulesAt( } const after = source.slice(idx + anchor.length); if (after.trimStart().startsWith(block.trim())) return; // idempotent - const updated = - source.slice(0, idx + anchor.length) + "\n" + block + after; + const updated = source.slice(0, idx + anchor.length) + "\n" + block + after; writeFileSync(filePath, updated); } @@ -97,7 +102,10 @@ export function addBoundariesEntry( } const modeFragment = opts.mode ? `, mode: "${opts.mode}"` : ""; const newEntry = ` { type: "core", pattern: "${packagePath}"${modeFragment} },\n`; - const updated = source.replace(wildcardLine[0], `\n${newEntry}${wildcardLine[1]}`); + const updated = source.replace( + wildcardLine[0], + `\n${newEntry}${wildcardLine[1]}`, + ); writeFileSync(baseJsPath, updated); } @@ -106,11 +114,13 @@ export function addBoundariesEntry( * file, returns a plop `add` action that emits the file (without .hbs * extension) at /. The actions are sorted so * directory creation is deterministic. + * + * Set `opts.force` to overwrite existing files (idempotent re-runs). */ export function emitTemplateTree( srcPrefix: string, destPrefix: string, - opts: { templatesRoot?: string } = {}, + opts: { templatesRoot?: string; force?: boolean } = {}, ): PlopTypes.AddActionConfig[] { // The templates directory is resolved in priority order: // 1. opts.templatesRoot — test injection (temp directory) @@ -120,13 +130,18 @@ export function emitTemplateTree( if (opts.templatesRoot) { root = opts.templatesRoot; } else { - const fromRepoRoot = join(process.cwd(), "turbo", "generators", "templates"); + const fromRepoRoot = join( + process.cwd(), + "turbo", + "generators", + "templates", + ); const fromGeneratorsDir = join(process.cwd(), "templates"); root = existsSync(fromRepoRoot) ? fromRepoRoot : fromGeneratorsDir; } const srcRoot = join(root, srcPrefix); const out: PlopTypes.AddActionConfig[] = []; - walkHbs(srcRoot, srcRoot, srcPrefix, destPrefix, out); + walkHbs(srcRoot, srcRoot, srcPrefix, destPrefix, out, opts.force ?? false); out.sort((a, b) => (a.path ?? "").localeCompare(b.path ?? "")); return out; } @@ -137,11 +152,12 @@ function walkHbs( srcPrefix: string, destPrefix: string, out: PlopTypes.AddActionConfig[], + force: boolean, ): void { for (const name of readdirSync(dir)) { const full = join(dir, name); if (statSync(full).isDirectory()) { - walkHbs(topRoot, full, srcPrefix, destPrefix, out); + walkHbs(topRoot, full, srcPrefix, destPrefix, out, force); continue; } if (!name.endsWith(".hbs")) continue; @@ -149,8 +165,12 @@ function walkHbs( out.push({ type: "add", path: join(destPrefix, rel).replace(/\\/g, "/"), - templateFile: join("templates", srcPrefix, relative(topRoot, full)).replace(/\\/g, "/"), + templateFile: join( + "templates", + srcPrefix, + relative(topRoot, full), + ).replace(/\\/g, "/"), + force, } as PlopTypes.AddActionConfig); } } - diff --git a/turbo/generators/templates/core-package/audit/docs/library-decisions/2026-05-14-@trpc/server.md.hbs b/turbo/generators/templates/core-package/audit/docs/library-decisions/2026-05-14-@trpc/server.md.hbs new file mode 100644 index 0000000..76a079e --- /dev/null +++ b/turbo/generators/templates/core-package/audit/docs/library-decisions/2026-05-14-@trpc/server.md.hbs @@ -0,0 +1,68 @@ +--- +package: "@trpc/server" +version: "^11.0.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: adr-018 +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view @trpc/server license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types; fully type-safe by design. + +## Filter: maintenance + +Active. Maintained by the tRPC team; v11 is the current stable line. + +## Filter: boundary-fit + +Core package. `@trpc/server` is already present in `core-api` (workspace dependency). Using it in `core-audit` for the audit API router does not violate boundary rules. + +## Filter: shadow-check + +`@trpc/server` is workspace-present via `core-api`. Same major version; no shadow. + +## Filter: eu-residency + +Server-side RPC library; no vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-audit` exposes an audit-log tRPC router used by the `apps/web-next` API layer. + +## Prompt: replaces + +Nothing new — tRPC is already the API layer; this extends it to the audit surface. + +## Prompt: migration-cost-out + +Hard: router procedures, input schemas, and error-mapping middleware are tRPC-shaped. Removal would require replacing the audit API surface. + +## Prompt: alternatives-considered + +1. **REST endpoints** — loses end-to-end type safety already established via tRPC. +2. **GraphQL** — much heavier; not aligned with existing workspace API shape. +tRPC is the locked workspace API library; extending it to audit is the natural fit. diff --git a/turbo/generators/templates/core-package/audit/docs/library-decisions/2026-05-14-zod.md.hbs b/turbo/generators/templates/core-package/audit/docs/library-decisions/2026-05-14-zod.md.hbs new file mode 100644 index 0000000..bc0f893 --- /dev/null +++ b/turbo/generators/templates/core-package/audit/docs/library-decisions/2026-05-14-zod.md.hbs @@ -0,0 +1,66 @@ +--- +package: zod +version: "^3.23.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: adr-018 +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view zod license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types in its distribution (`.d.ts` included). + +## Filter: maintenance + +Active. Regular releases by Colin McDonnell; widely adopted. + +## Filter: boundary-fit + +Core package. Zod is the workspace-canonical validation library locked in `core-shared` (ADR-018). + +## Filter: shadow-check + +Zod is already the workspace-locked validation library. No shadow. + +## Filter: eu-residency + +Pure computation; no network calls or vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-audit` uses zod to validate audit-log record input schemas. + +## Prompt: replaces + +Nothing — zod is the pre-existing workspace validation library. + +## Prompt: migration-cost-out + +Mechanical: swap schema definitions at call sites. No data-format lock-in. + +## Prompt: alternatives-considered + +Zod is workspace-locked (see `core-shared`). A replacement would require a workspace-wide ADR; no alternative was evaluated here. diff --git a/turbo/generators/templates/core-package/events/docs/library-decisions/2026-05-14-zod.md.hbs b/turbo/generators/templates/core-package/events/docs/library-decisions/2026-05-14-zod.md.hbs new file mode 100644 index 0000000..8060119 --- /dev/null +++ b/turbo/generators/templates/core-package/events/docs/library-decisions/2026-05-14-zod.md.hbs @@ -0,0 +1,66 @@ +--- +package: zod +version: "^3.23.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: adr-015 +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view zod license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types in its distribution (`.d.ts` included). + +## Filter: maintenance + +Active. Regular releases by Colin McDonnell; widely adopted. + +## Filter: boundary-fit + +Core package. Zod is the workspace-canonical validation library locked in `core-shared` (ADR-015). + +## Filter: shadow-check + +Zod is already the workspace-locked validation library. No shadow. + +## Filter: eu-residency + +Pure computation; no network calls or vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-events` uses zod for event-descriptor payload schemas. + +## Prompt: replaces + +Nothing — zod is the pre-existing workspace validation library. + +## Prompt: migration-cost-out + +Mechanical: swap schema definitions at call sites. No data-format lock-in. + +## Prompt: alternatives-considered + +Zod is workspace-locked (see `core-shared`). A replacement would require a workspace-wide ADR; no alternative was evaluated here. diff --git a/turbo/generators/templates/core-package/realtime/docs/library-decisions/2026-05-14-socket.io.md.hbs b/turbo/generators/templates/core-package/realtime/docs/library-decisions/2026-05-14-socket.io.md.hbs new file mode 100644 index 0000000..d52aaec --- /dev/null +++ b/turbo/generators/templates/core-package/realtime/docs/library-decisions/2026-05-14-socket.io.md.hbs @@ -0,0 +1,72 @@ +--- +package: socket.io +version: "^4.7.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: adr-016 +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: self-hostable + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view socket.io license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types in its distribution. + +## Filter: maintenance + +Active. Maintained by the Socket.IO team; frequent releases and active issue tracker. + +## Filter: maintenance + +Active. Regular releases; widely deployed in production. + +## Filter: boundary-fit + +ADR-016 §R2 explicitly designates `core-realtime` as the sole allowed home for `socket.io`. Boundary rule `no-direct-socket-io` enforces this in ESLint. + +## Filter: shadow-check + +No competing realtime transport in the workspace. No shadow. + +## Filter: eu-residency + +Self-hosted server; the library itself does not transmit data to any vendor endpoint. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-realtime` wraps socket.io to provide the `IRealtimeServer` abstraction (ADR-016). + +## Prompt: replaces + +Nothing — this is the initial realtime scaffolding. No prior transport to retire. + +## Prompt: migration-cost-out + +Hard: channel descriptors, handler signatures, and server-side broadcast API are all shaped around socket.io semantics. Replacing requires re-implementing the abstraction layer. + +## Prompt: alternatives-considered + +1. **ws** — lower-level, no rooms or namespaces; would require significant protocol work. +2. **Ably / Pusher** — vendor-hosted; eu-residency risk and ongoing cost. +Socket.IO is the established standard for this use-case and is fully self-hostable. diff --git a/turbo/generators/templates/core-package/realtime/docs/library-decisions/2026-05-14-zod.md.hbs b/turbo/generators/templates/core-package/realtime/docs/library-decisions/2026-05-14-zod.md.hbs new file mode 100644 index 0000000..fbf1f26 --- /dev/null +++ b/turbo/generators/templates/core-package/realtime/docs/library-decisions/2026-05-14-zod.md.hbs @@ -0,0 +1,66 @@ +--- +package: zod +version: "^3.23.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: adr-016 +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view zod license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types in its distribution (`.d.ts` included). + +## Filter: maintenance + +Active. Regular releases by Colin McDonnell; widely adopted. + +## Filter: boundary-fit + +Core package. Zod is the workspace-canonical validation library locked in `core-shared` (ADR-016). + +## Filter: shadow-check + +Zod is already the workspace-locked validation library. No shadow. + +## Filter: eu-residency + +Pure computation; no network calls or vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-realtime` uses zod for channel descriptor and payload schema validation. + +## Prompt: replaces + +Nothing — zod is the pre-existing workspace validation library. + +## Prompt: migration-cost-out + +Mechanical: swap schema definitions at call sites. No data-format lock-in. + +## Prompt: alternatives-considered + +Zod is workspace-locked (see `core-shared`). A replacement would require a workspace-wide ADR; no alternative was evaluated here. diff --git a/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@tanstack/react-query.md.hbs b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@tanstack/react-query.md.hbs new file mode 100644 index 0000000..92be119 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@tanstack/react-query.md.hbs @@ -0,0 +1,68 @@ +--- +package: "@tanstack/react-query" +version: "^5.66.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: null +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view @tanstack/react-query license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types in its distribution. + +## Filter: maintenance + +Active. Maintained by the TanStack team; v5 is the current stable major. + +## Filter: boundary-fit + +Core package (client-side). Used exclusively in `core-trpc` to power the React Query integration layer. No boundary rule violation. + +## Filter: shadow-check + +No other server-state library in the workspace. No shadow. + +## Filter: eu-residency + +Client-side state management; no network calls to vendor endpoints. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-trpc` exposes the `QueryClient` and React Query hooks via `@trpc/react-query`. + +## Prompt: replaces + +Nothing — this is the initial tRPC scaffold. + +## Prompt: migration-cost-out + +Hard: query keys, cache invalidation patterns, and mutation hooks are all shaped around React Query's API. + +## Prompt: alternatives-considered + +1. **SWR** — narrower feature set; less aligned with tRPC's first-class React Query adapter. +2. **RTK Query** — Redux-bound; heavier than needed for this use case. +React Query is tRPC's canonical client-side data-fetching companion. diff --git a/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/client.md.hbs b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/client.md.hbs new file mode 100644 index 0000000..8c33b8f --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/client.md.hbs @@ -0,0 +1,68 @@ +--- +package: "@trpc/client" +version: "^11.0.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: null +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view @trpc/client license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types; end-to-end type safety is the library's primary value. + +## Filter: maintenance + +Active. Maintained by the tRPC team; v11 is the current stable line. + +## Filter: boundary-fit + +Core package. `@trpc/client` is the companion to `@trpc/server` (present in `core-api`). No boundary rule violation. + +## Filter: shadow-check + +No competing RPC client in the workspace. No shadow. + +## Filter: eu-residency + +Client-side HTTP transport; calls the workspace's own tRPC server, no third-party vendor. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-trpc` uses `@trpc/client` to build the typed HTTP client exposed to feature packages. + +## Prompt: replaces + +Nothing — this is the initial tRPC scaffold. + +## Prompt: migration-cost-out + +Hard: the typed client is tightly coupled to the server router type; replacing requires re-implementing the client abstraction. + +## Prompt: alternatives-considered + +1. **Plain fetch** — loses end-to-end type safety. +2. **GraphQL client** — significantly heavier; not aligned with the workspace API shape. +`@trpc/client` is the only viable companion to `@trpc/server`. diff --git a/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/react-query.md.hbs b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/react-query.md.hbs new file mode 100644 index 0000000..99c2a30 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/react-query.md.hbs @@ -0,0 +1,66 @@ +--- +package: "@trpc/react-query" +version: "^11.0.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: null +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view @trpc/react-query license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types; deeply integrated with tRPC's type inference. + +## Filter: maintenance + +Active. Maintained by the tRPC team alongside `@trpc/server` and `@trpc/client`. + +## Filter: boundary-fit + +Core package. The React Query integration bridge belongs in `core-trpc` alongside its sibling tRPC packages. No boundary rule violation. + +## Filter: shadow-check + +No other tRPC–React Query bridge in the workspace. No shadow. + +## Filter: eu-residency + +Client-side integration adapter; no vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-trpc` re-exports `@trpc/react-query` hooks for use in Next.js feature pages. + +## Prompt: replaces + +Nothing — this is the initial tRPC scaffold. + +## Prompt: migration-cost-out + +Hard: hooks are tRPC-procedure-typed; migrating away requires replacing all call sites. + +## Prompt: alternatives-considered + +This package is the canonical integration point between `@trpc/client` and `@tanstack/react-query`. No viable alternative exists. diff --git a/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/server.md.hbs b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/server.md.hbs new file mode 100644 index 0000000..2bf1313 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/server.md.hbs @@ -0,0 +1,68 @@ +--- +package: "@trpc/server" +version: "^11.0.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: null +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view @trpc/server license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types; type-safe by design. + +## Filter: maintenance + +Active. Maintained by the tRPC team; v11 is the current stable line. + +## Filter: boundary-fit + +Core package. `@trpc/server` is also present in `core-api`; using the same version in `core-trpc` for router type inference is the intended usage pattern. + +## Filter: shadow-check + +`@trpc/server` is workspace-present via `core-api`. Same major version; no shadow. + +## Filter: eu-residency + +Server-side RPC framework; no vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-trpc` re-exports tRPC router types and the `t` initializer used by feature routers. + +## Prompt: replaces + +Nothing — tRPC is already the workspace API layer via `core-api`. + +## Prompt: migration-cost-out + +Hard: router types, procedure definitions, and middleware are tRPC-shaped across the entire workspace. + +## Prompt: alternatives-considered + +1. **REST + OpenAPI** — loses end-to-end type safety; see ADR-022 context for why this was rejected. +2. **GraphQL** — significantly heavier; not aligned with the workspace API shape. +tRPC is the locked workspace API library. diff --git a/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/tanstack-react-query.md.hbs b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/tanstack-react-query.md.hbs new file mode 100644 index 0000000..7cc3187 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-@trpc/tanstack-react-query.md.hbs @@ -0,0 +1,66 @@ +--- +package: "@trpc/tanstack-react-query" +version: "^11.1.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: null +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view @trpc/tanstack-react-query license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types; part of the tRPC v11 adapter suite. + +## Filter: maintenance + +Active. Maintained by the tRPC team as part of the v11 TanStack Start integration. + +## Filter: boundary-fit + +Core package. Required for the TanStack Start provider (`core-trpc/tanstack`). No boundary rule violation. + +## Filter: shadow-check + +No duplicate TanStack adapter in the workspace. No shadow. + +## Filter: eu-residency + +Client-side integration adapter; no vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-trpc` exposes a TanStack Start provider via `@trpc/tanstack-react-query` for `apps/web-tanstack`. + +## Prompt: replaces + +Nothing — this is the initial tRPC scaffold. + +## Prompt: migration-cost-out + +Hard: the TanStack provider is shaped around this adapter's API; replacing requires re-implementing the provider. + +## Prompt: alternatives-considered + +This is the official tRPC adapter for TanStack Start. No viable alternative exists. diff --git a/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-react.md.hbs b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-react.md.hbs new file mode 100644 index 0000000..d4f52fa --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-react.md.hbs @@ -0,0 +1,66 @@ +--- +package: react +version: "^19.0.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: null +filter-results: + license: MIT + types: "@types/react" + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view react license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +TypeScript types via `@types/react` (community-maintained but canonical; ships in sync with each React major). + +## Filter: maintenance + +Active. Maintained by Meta; v19 is the current stable major. + +## Filter: boundary-fit + +Core package (client-side). React is already present in app packages; exposing it from `core-trpc` for provider components is standard practice. + +## Filter: shadow-check + +React is already workspace-present in app packages. Same major version; no shadow. + +## Filter: eu-residency + +Client-side rendering library; no vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-trpc` uses React to implement the `TRPCProvider` and `QueryClientProvider` wrapper components. + +## Prompt: replaces + +Nothing — React is already the UI framework in the workspace. + +## Prompt: migration-cost-out + +Impossible: React is the foundational UI library for this workspace. + +## Prompt: alternatives-considered + +React is workspace-locked as the UI framework. No alternative evaluated. diff --git a/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-superjson.md.hbs b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-superjson.md.hbs new file mode 100644 index 0000000..6babca0 --- /dev/null +++ b/turbo/generators/templates/core-package/trpc/docs/library-decisions/2026-05-14-superjson.md.hbs @@ -0,0 +1,68 @@ +--- +package: superjson +version: "^2.2.1" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: null +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view superjson license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types in its distribution. + +## Filter: maintenance + +Active. Currently maintained by the tRPC team after the original author transferred stewardship. + +## Filter: boundary-fit + +Core package. superjson is used exclusively in `core-trpc` as the tRPC transformer; no feature package imports it directly. + +## Filter: shadow-check + +No competing JSON-extended serializer in the workspace. No shadow. + +## Filter: eu-residency + +Pure serialization library; no network calls or vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-trpc` uses superjson as the tRPC transformer to support `Date`, `Map`, `Set`, and other non-JSON-native types over the wire. + +## Prompt: replaces + +Nothing — this is the initial tRPC scaffold. + +## Prompt: migration-cost-out + +Mechanical: swap transformer in the tRPC init; update client config. No data-storage coupling. + +## Prompt: alternatives-considered + +1. **devalue** — no stable npm release at adoption time. +2. **Plain JSON** — loses Date/Map/Set round-trip support required by the workspace's domain models. +superjson is the tRPC-canonical transformer for extended types. diff --git a/turbo/generators/templates/core-package/ui/docs/library-decisions/2026-05-14-clsx.md.hbs b/turbo/generators/templates/core-package/ui/docs/library-decisions/2026-05-14-clsx.md.hbs new file mode 100644 index 0000000..c6925c3 --- /dev/null +++ b/turbo/generators/templates/core-package/ui/docs/library-decisions/2026-05-14-clsx.md.hbs @@ -0,0 +1,68 @@ +--- +package: clsx +version: "^2.1.1" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: null +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view clsx license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types in its distribution. + +## Filter: maintenance + +Active. Maintained by Luke Edwards; stable, minimal API. + +## Filter: boundary-fit + +Core UI package. `clsx` is a utility for constructing `className` strings; appropriate for `core-ui`. No boundary rule violation. + +## Filter: shadow-check + +No competing className utility in the workspace. No shadow. + +## Filter: eu-residency + +Pure compute; no network calls or vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-ui` uses `clsx` in the `cn()` utility (combined with `tailwind-merge`) for conditional class composition. + +## Prompt: replaces + +Nothing — this is the initial UI scaffold. + +## Prompt: migration-cost-out + +Mechanical: replace `clsx()` calls with template literals or equivalent. Minimal API surface. + +## Prompt: alternatives-considered + +1. **classnames** — the older predecessor; `clsx` is smaller and faster. +2. **Template literals** — verbose; no conditional logic support. +`clsx` is the de-facto standard lightweight className utility. diff --git a/turbo/generators/templates/core-package/ui/docs/library-decisions/2026-05-14-react.md.hbs b/turbo/generators/templates/core-package/ui/docs/library-decisions/2026-05-14-react.md.hbs new file mode 100644 index 0000000..9911012 --- /dev/null +++ b/turbo/generators/templates/core-package/ui/docs/library-decisions/2026-05-14-react.md.hbs @@ -0,0 +1,66 @@ +--- +package: react +version: "^19.0.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: null +filter-results: + license: MIT + types: "@types/react" + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view react license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +TypeScript types via `@types/react` (community-maintained but canonical; ships in sync with each React major). + +## Filter: maintenance + +Active. Maintained by Meta; v19 is the current stable major. + +## Filter: boundary-fit + +Core UI package. React is required for the component library; appropriate for `core-ui`. No boundary rule violation. + +## Filter: shadow-check + +React is already workspace-present in app packages. Same major version; no shadow. + +## Filter: eu-residency + +Client-side rendering library; no vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-ui` renders all atomic-design components (Button, Input, Label, FormField) as React components. + +## Prompt: replaces + +Nothing — React is already the UI framework in the workspace. + +## Prompt: migration-cost-out + +Impossible: React is the foundational UI library for this workspace. + +## Prompt: alternatives-considered + +React is workspace-locked as the UI framework. No alternative evaluated. diff --git a/turbo/generators/templates/core-package/ui/docs/library-decisions/2026-05-14-tailwind-merge.md.hbs b/turbo/generators/templates/core-package/ui/docs/library-decisions/2026-05-14-tailwind-merge.md.hbs new file mode 100644 index 0000000..24ce4f0 --- /dev/null +++ b/turbo/generators/templates/core-package/ui/docs/library-decisions/2026-05-14-tailwind-merge.md.hbs @@ -0,0 +1,68 @@ +--- +package: tailwind-merge +version: "^3.0.0" +tier: core +decision: approved +date: 2026-05-14 +deciders: [scaffolded] +adr: null +filter-results: + license: MIT + types: native + maintenance: active + boundary-fit: pass + shadow-check: pass + eu-residency: n/a + cve-scan: clean + named-consumer: pass +verification-commands: + - pnpm audit --audit-level=moderate + - npm view tailwind-merge license +accepted-cves: [] +--- + +## Filter: license + +MIT — on the workspace allowlist. + +## Filter: types + +Ships first-party TypeScript types in its distribution. + +## Filter: maintenance + +Active. Maintained by dcastil; v3 is the current stable major. + +## Filter: boundary-fit + +Core UI package. `tailwind-merge` deduplicates conflicting Tailwind classes; appropriate for `core-ui`. No boundary rule violation. + +## Filter: shadow-check + +No competing Tailwind class-merging utility in the workspace. No shadow. + +## Filter: eu-residency + +Pure compute; no network calls or vendor data transmission. n/a. + +## Filter: cve-scan + +No advisories at adoption time. + +## Filter: named-consumer + +`core-ui` uses `tailwind-merge` in the `cn()` utility (combined with `clsx`) to resolve conflicting Tailwind class names at runtime. + +## Prompt: replaces + +Nothing — this is the initial UI scaffold. + +## Prompt: migration-cost-out + +Mechanical: replace `twMerge()` calls in the `cn()` utility; update any call sites. Narrow API surface. + +## Prompt: alternatives-considered + +1. **Custom deduplication** — error-prone; Tailwind has hundreds of class groups that change each version. +2. **tw-join** — does not merge conflicts; only concatenates. +`tailwind-merge` is the de-facto standard for conflict-free Tailwind class composition. diff --git a/turbo/generators/verify-doc-shas.test.ts b/turbo/generators/verify-doc-shas.test.ts new file mode 100644 index 0000000..a78c974 --- /dev/null +++ b/turbo/generators/verify-doc-shas.test.ts @@ -0,0 +1,123 @@ +import { describe, it, expect } from "vitest"; +import { createHash } from "node:crypto"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +// Resolve repo root relative to this file (turbo/generators/) +const repoRoot = join(import.meta.dirname, "..", ".."); + +function sha256(filePath: string): string { + const content = readFileSync(filePath, "utf8"); + const normalized = content.replace(/\r\n/g, "\n").replace(/\n*$/, "\n"); + return createHash("sha256").update(normalized).digest("hex"); +} + +const tplBase = join( + repoRoot, + "turbo", + "generators", + "templates", + "core-package", +); + +describe("doc template SHA256 checksums", () => { + const cases: Array<{ label: string; rel: string; expected: string }> = [ + { + label: "events/zod", + rel: "events/docs/library-decisions/2026-05-14-zod.md.hbs", + expected: + "6b159ae8890a51a0ff1cc94a8cb1ee70bc1343a87fc2e6e4d0b197a6c5801ff7", + }, + { + label: "realtime/socket.io", + rel: "realtime/docs/library-decisions/2026-05-14-socket.io.md.hbs", + expected: + "7fd94c82037bdb92a90329355123950132050ca6b7b828292359438f8c9a562f", + }, + { + label: "realtime/zod", + rel: "realtime/docs/library-decisions/2026-05-14-zod.md.hbs", + expected: + "412f92181566e983259982cefc0c7c3b632f289d566971badc855f7a5a03f4a5", + }, + { + label: "audit/@trpc/server", + rel: "audit/docs/library-decisions/2026-05-14-@trpc/server.md.hbs", + expected: + "40217b9dd1938083adaf3ccf5faa586447b82c5df60c30f662809f3cd668e4d6", + }, + { + label: "audit/zod", + rel: "audit/docs/library-decisions/2026-05-14-zod.md.hbs", + expected: + "8f18b4c90031dd453c278f549b8d3dbc20083aacef2e6a422233a893afa29bd9", + }, + { + label: "trpc/@tanstack/react-query", + rel: "trpc/docs/library-decisions/2026-05-14-@tanstack/react-query.md.hbs", + expected: + "9e3fe29d5e6f9a9b81fa70a01f966f485e8a7c1a841a1654e117e3171e07aa6c", + }, + { + label: "trpc/@trpc/client", + rel: "trpc/docs/library-decisions/2026-05-14-@trpc/client.md.hbs", + expected: + "0c871657850954ee7fb0141070c36f8aa0c79932c510773d292aaf8e65ceb871", + }, + { + label: "trpc/@trpc/react-query", + rel: "trpc/docs/library-decisions/2026-05-14-@trpc/react-query.md.hbs", + expected: + "fc02764ca7727d6353ccdc7230f90c5054f6976304ef6c81d7864a917815f002", + }, + { + label: "trpc/@trpc/server", + rel: "trpc/docs/library-decisions/2026-05-14-@trpc/server.md.hbs", + expected: + "094ae3505561e84090002a8646967a0ae9dcf4ce121bbbc240da06af2cc078c8", + }, + { + label: "trpc/@trpc/tanstack-react-query", + rel: "trpc/docs/library-decisions/2026-05-14-@trpc/tanstack-react-query.md.hbs", + expected: + "f139e506a7b09bd0154818cb9616c7a80295d191a89b7fbd7e3fd2eb7912e8f2", + }, + { + label: "trpc/react", + rel: "trpc/docs/library-decisions/2026-05-14-react.md.hbs", + expected: + "dbd8727184632f403404a34f7d26be572461bbbf8923cbbf747438ae4feb1529", + }, + { + label: "trpc/superjson", + rel: "trpc/docs/library-decisions/2026-05-14-superjson.md.hbs", + expected: + "b6404b77aa0e222fd6100a7b29e2572f75103a01c7d905d112d90a31e8443eed", + }, + { + label: "ui/clsx", + rel: "ui/docs/library-decisions/2026-05-14-clsx.md.hbs", + expected: + "3507768e0ee216dac6983b35b1ac7a6326a85f35a659863d266a340655602429", + }, + { + label: "ui/react", + rel: "ui/docs/library-decisions/2026-05-14-react.md.hbs", + expected: + "9d6178b1ec0b0ddba60b83ef8c5d65791f6890092e18d1813f7a2db1e8bb1e94", + }, + { + label: "ui/tailwind-merge", + rel: "ui/docs/library-decisions/2026-05-14-tailwind-merge.md.hbs", + expected: + "e8e2474dfc62f95f0671c659c04055371dec7dc9a2f2d26fc318eccaa7d59504", + }, + ]; + + for (const { label, rel, expected } of cases) { + it(`${label} matches snapshot`, () => { + const actual = sha256(join(tplBase, rel)); + expect(actual).toBe(expected); + }); + } +});