feat(tooling): pre-ship approved library traces in optional core generator templates
Each of the five optional core package generators (events, realtime, audit, trpc, ui) now copies pre-written decision: approved trace files into docs/library-decisions/ at scaffold time, covering every direct runtime dependency of that core package. This prevents a pre-commit gate failure the first time a developer runs pnpm turbo gen core-package <name> — the generator is the policy-compliant path, so the traces land by construction. - Added docs/library-decisions/*.md.hbs trace files under each of the five core-package template directories (15 files total) - Updated generator config to emit traces into workspace docs/library-decisions/ via a second emitTemplateTree call per core package - Updated all five __snapshots__/core-package/*.snapshot.json to include the new trace file entries - Added verify-doc-shas.test.ts to pin SHA256 hashes of all 15 trace templates so snapshot and file content cannot drift independently ADR refs: events→ADR-015, realtime→ADR-016, audit→ADR-018; trpc and ui cite closest ADR or null where no specific ADR exists. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 <destPrefix>/<relative-path>. 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
123
turbo/generators/verify-doc-shas.test.ts
Normal file
123
turbo/generators/verify-doc-shas.test.ts
Normal file
@@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user