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",
|
"path": "AGENTS.md",
|
||||||
"sha256": "65b3fe18cf85c2fb23fe88b5d7bda102316eccee7539c0d326947eb1c0b9a645"
|
"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",
|
"path": "eslint.config.js",
|
||||||
"sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745"
|
"sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745"
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
"path": "AGENTS.md",
|
"path": "AGENTS.md",
|
||||||
"sha256": "8f476285d06563b0d0a3b76ab31bec49311cdc81dffd81f71f4893126270d056"
|
"sha256": "8f476285d06563b0d0a3b76ab31bec49311cdc81dffd81f71f4893126270d056"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "docs/library-decisions/2026-05-14-zod.md",
|
||||||
|
"sha256": "6b159ae8890a51a0ff1cc94a8cb1ee70bc1343a87fc2e6e4d0b197a6c5801ff7"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "eslint.config.js",
|
"path": "eslint.config.js",
|
||||||
"sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745"
|
"sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745"
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
"path": "AGENTS.md",
|
"path": "AGENTS.md",
|
||||||
"sha256": "a9b9527c327a4164ee4ffca81727118f3876711c1cf3f0b8f32542d08531ccc3"
|
"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",
|
"path": "eslint.config.js",
|
||||||
"sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745"
|
"sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745"
|
||||||
|
|||||||
@@ -3,6 +3,34 @@
|
|||||||
"path": "AGENTS.md",
|
"path": "AGENTS.md",
|
||||||
"sha256": "b1b9c2c1be6925f37576841d88929916286dc530a2ad979e675d038235aab6ad"
|
"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",
|
"path": "eslint.config.js",
|
||||||
"sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745"
|
"sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745"
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
"path": "AGENTS.md",
|
"path": "AGENTS.md",
|
||||||
"sha256": "626329a8a409b4428307ba0014fb8be179a5db7d1fc2a43b017941ab44476188"
|
"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",
|
"path": "eslint.config.js",
|
||||||
"sha256": "64a85c158e386417d855793d256b9c59be38635ce2febcb5a6a15b17f4143745"
|
"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.";
|
return "Guard passed — packages/core-realtime does not exist yet.";
|
||||||
},
|
},
|
||||||
...emitTemplateTree("core-package/realtime", "packages/core-realtime"),
|
...emitTemplateTree("core-package/realtime", "packages/core-realtime"),
|
||||||
|
...emitTemplateTree(
|
||||||
|
"core-package/realtime/docs/library-decisions",
|
||||||
|
"docs/library-decisions",
|
||||||
|
{ force: true },
|
||||||
|
),
|
||||||
...emitTemplateTree(
|
...emitTemplateTree(
|
||||||
"core-package/realtime-eslint-rules",
|
"core-package/realtime-eslint-rules",
|
||||||
"packages/core-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.";
|
return "Guard passed — packages/core-events does not exist yet.";
|
||||||
},
|
},
|
||||||
...emitTemplateTree("core-package/events", "packages/core-events"),
|
...emitTemplateTree("core-package/events", "packages/core-events"),
|
||||||
|
...emitTemplateTree(
|
||||||
|
"core-package/events/docs/library-decisions",
|
||||||
|
"docs/library-decisions",
|
||||||
|
{ force: true },
|
||||||
|
),
|
||||||
() => {
|
() => {
|
||||||
addToTranspilePackages(
|
addToTranspilePackages(
|
||||||
"apps/web-next/next.config.mjs",
|
"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.";
|
return "Guard passed — packages/core-trpc does not exist yet.";
|
||||||
},
|
},
|
||||||
...emitTemplateTree("core-package/trpc", "packages/core-trpc"),
|
...emitTemplateTree("core-package/trpc", "packages/core-trpc"),
|
||||||
|
...emitTemplateTree(
|
||||||
|
"core-package/trpc/docs/library-decisions",
|
||||||
|
"docs/library-decisions",
|
||||||
|
{ force: true },
|
||||||
|
),
|
||||||
() => {
|
() => {
|
||||||
addToTranspilePackages(
|
addToTranspilePackages(
|
||||||
"apps/web-next/next.config.mjs",
|
"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.";
|
return "Guard passed — packages/core-ui does not exist yet.";
|
||||||
},
|
},
|
||||||
...emitTemplateTree("core-package/ui", "packages/core-ui"),
|
...emitTemplateTree("core-package/ui", "packages/core-ui"),
|
||||||
|
...emitTemplateTree(
|
||||||
|
"core-package/ui/docs/library-decisions",
|
||||||
|
"docs/library-decisions",
|
||||||
|
{ force: true },
|
||||||
|
),
|
||||||
() => {
|
() => {
|
||||||
addToTranspilePackages(
|
addToTranspilePackages(
|
||||||
"apps/web-next/next.config.mjs",
|
"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.";
|
return "Guard passed — packages/core-audit does not exist yet.";
|
||||||
},
|
},
|
||||||
...emitTemplateTree("core-package/audit", "packages/core-audit"),
|
...emitTemplateTree("core-package/audit", "packages/core-audit"),
|
||||||
|
...emitTemplateTree(
|
||||||
|
"core-package/audit/docs/library-decisions",
|
||||||
|
"docs/library-decisions",
|
||||||
|
{ force: true },
|
||||||
|
),
|
||||||
() => {
|
() => {
|
||||||
addToTranspilePackages(
|
addToTranspilePackages(
|
||||||
"apps/web-next/next.config.mjs",
|
"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 { join, relative } from "node:path";
|
||||||
import type { PlopTypes } from "@turbo/gen";
|
import type { PlopTypes } from "@turbo/gen";
|
||||||
|
|
||||||
@@ -61,8 +67,7 @@ export function splicePluginRulesAt(
|
|||||||
}
|
}
|
||||||
const after = source.slice(idx + anchor.length);
|
const after = source.slice(idx + anchor.length);
|
||||||
if (after.trimStart().startsWith(block.trim())) return; // idempotent
|
if (after.trimStart().startsWith(block.trim())) return; // idempotent
|
||||||
const updated =
|
const updated = source.slice(0, idx + anchor.length) + "\n" + block + after;
|
||||||
source.slice(0, idx + anchor.length) + "\n" + block + after;
|
|
||||||
writeFileSync(filePath, updated);
|
writeFileSync(filePath, updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +102,10 @@ export function addBoundariesEntry(
|
|||||||
}
|
}
|
||||||
const modeFragment = opts.mode ? `, mode: "${opts.mode}"` : "";
|
const modeFragment = opts.mode ? `, mode: "${opts.mode}"` : "";
|
||||||
const newEntry = ` { type: "core", pattern: "${packagePath}"${modeFragment} },\n`;
|
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);
|
writeFileSync(baseJsPath, updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,11 +114,13 @@ export function addBoundariesEntry(
|
|||||||
* file, returns a plop `add` action that emits the file (without .hbs
|
* file, returns a plop `add` action that emits the file (without .hbs
|
||||||
* extension) at <destPrefix>/<relative-path>. The actions are sorted so
|
* extension) at <destPrefix>/<relative-path>. The actions are sorted so
|
||||||
* directory creation is deterministic.
|
* directory creation is deterministic.
|
||||||
|
*
|
||||||
|
* Set `opts.force` to overwrite existing files (idempotent re-runs).
|
||||||
*/
|
*/
|
||||||
export function emitTemplateTree(
|
export function emitTemplateTree(
|
||||||
srcPrefix: string,
|
srcPrefix: string,
|
||||||
destPrefix: string,
|
destPrefix: string,
|
||||||
opts: { templatesRoot?: string } = {},
|
opts: { templatesRoot?: string; force?: boolean } = {},
|
||||||
): PlopTypes.AddActionConfig[] {
|
): PlopTypes.AddActionConfig[] {
|
||||||
// The templates directory is resolved in priority order:
|
// The templates directory is resolved in priority order:
|
||||||
// 1. opts.templatesRoot — test injection (temp directory)
|
// 1. opts.templatesRoot — test injection (temp directory)
|
||||||
@@ -120,13 +130,18 @@ export function emitTemplateTree(
|
|||||||
if (opts.templatesRoot) {
|
if (opts.templatesRoot) {
|
||||||
root = opts.templatesRoot;
|
root = opts.templatesRoot;
|
||||||
} else {
|
} else {
|
||||||
const fromRepoRoot = join(process.cwd(), "turbo", "generators", "templates");
|
const fromRepoRoot = join(
|
||||||
|
process.cwd(),
|
||||||
|
"turbo",
|
||||||
|
"generators",
|
||||||
|
"templates",
|
||||||
|
);
|
||||||
const fromGeneratorsDir = join(process.cwd(), "templates");
|
const fromGeneratorsDir = join(process.cwd(), "templates");
|
||||||
root = existsSync(fromRepoRoot) ? fromRepoRoot : fromGeneratorsDir;
|
root = existsSync(fromRepoRoot) ? fromRepoRoot : fromGeneratorsDir;
|
||||||
}
|
}
|
||||||
const srcRoot = join(root, srcPrefix);
|
const srcRoot = join(root, srcPrefix);
|
||||||
const out: PlopTypes.AddActionConfig[] = [];
|
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 ?? ""));
|
out.sort((a, b) => (a.path ?? "").localeCompare(b.path ?? ""));
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -137,11 +152,12 @@ function walkHbs(
|
|||||||
srcPrefix: string,
|
srcPrefix: string,
|
||||||
destPrefix: string,
|
destPrefix: string,
|
||||||
out: PlopTypes.AddActionConfig[],
|
out: PlopTypes.AddActionConfig[],
|
||||||
|
force: boolean,
|
||||||
): void {
|
): void {
|
||||||
for (const name of readdirSync(dir)) {
|
for (const name of readdirSync(dir)) {
|
||||||
const full = join(dir, name);
|
const full = join(dir, name);
|
||||||
if (statSync(full).isDirectory()) {
|
if (statSync(full).isDirectory()) {
|
||||||
walkHbs(topRoot, full, srcPrefix, destPrefix, out);
|
walkHbs(topRoot, full, srcPrefix, destPrefix, out, force);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!name.endsWith(".hbs")) continue;
|
if (!name.endsWith(".hbs")) continue;
|
||||||
@@ -149,8 +165,12 @@ function walkHbs(
|
|||||||
out.push({
|
out.push({
|
||||||
type: "add",
|
type: "add",
|
||||||
path: join(destPrefix, rel).replace(/\\/g, "/"),
|
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);
|
} 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