Initial commit

This commit is contained in:
fraqtal
2026-07-12 08:15:46 +00:00
commit ee0fec0691
1397 changed files with 127242 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
# AGENTS.md — core-api
**Tag:** core-composition
**Composition-only package** that aggregates feature tRPC routers into a single root `appRouter`. It does not define procedures; instead, it imports them from feature packages.
## Responsibilities
- **Compose tRPC appRouter** — merges feature routers into `t.router({ ... })`
- **Type export** — exports `AppRouter` type for frontend type safety
- **No procedure definitions** — all routers owned by their respective features (`@repo/auth`, `@repo/blog`, etc.)
- **No business logic** — purely structural assembly
## Allowed imports
- **`@repo/<feature>/api`** subpath exports only (to get tRPC routers)
- e.g., `import { authRouter } from "@repo/auth/api"`
- e.g., `import { blogRouter } from "@repo/blog/api"`
- e.g., `import { navigationRouter } from "@repo/navigation/api"`
- `@repo/core-shared/trpc/init` — for `t.router()` builder
## Must NOT import
- Any feature's root package or other subpaths (e.g., NOT `@repo/blog/di`, NOT `@repo/blog/entities`)
- Any app package
- `@repo/core-cms`, `@repo/core-trpc`, `@repo/core-ui`
## Public exports
From `package.json`:
- `.``appRouter` and `AppRouter` type
Example usage:
```typescript
import { appRouter, type AppRouter } from "@repo/core-api";
```
## Test conventions
- No unit tests (composition layer)
- Verify at app boot: `pnpm dev --filter @repo/web-next` succeeds and tRPC client fetches data
- Run router health check: `pnpm typecheck` confirms `AppRouter` type is valid
## Structure
```
src/
root.ts # t.router({ ... }) aggregating all feature routers
index.ts # re-exports appRouter + type
```

View File

@@ -0,0 +1,3 @@
import baseConfig from "@repo/core-eslint/base";
export default baseConfig;

View File

@@ -0,0 +1,34 @@
{
"name": "@repo/core-api",
"private": true,
"version": "0.0.0",
"type": "module",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"build": "tsc --noEmit",
"lint": "eslint .",
"typecheck": "tsc --noEmit",
"test": "vitest run --passWithNoTests"
},
"dependencies": {
"@repo/auth": "workspace:*",
"@repo/blog": "workspace:*",
"@repo/core-consent": "workspace:*",
"@repo/core-dsr": "workspace:*",
"@repo/core-shared": "workspace:*",
"@repo/marketing-pages": "workspace:*",
"@repo/media": "workspace:*",
"@repo/navigation": "workspace:*",
"@trpc/server": "^11.0.0"
},
"devDependencies": {
"@repo/core-eslint": "workspace:*",
"@repo/core-testing": "workspace:*",
"@repo/core-typescript": "workspace:*",
"@types/node": "^22.0.0",
"@vitest/coverage-v8": "^3.0.0",
"vitest": "^3.0.0"
}
}

View File

@@ -0,0 +1 @@
export { appRouter, type AppRouter } from "./root";

View File

@@ -0,0 +1,21 @@
import { router } from "@repo/core-shared/trpc/init";
import { authRouter } from "@repo/auth/api";
import { blogRouter } from "@repo/blog/api";
import { marketingPagesRouter } from "@repo/marketing-pages/api";
import { navigationRouter } from "@repo/navigation/api";
import { mediaRouter } from "@repo/media/api";
import { dsrRouter } from "@repo/core-dsr";
import { consentRouter } from "@repo/core-consent";
export const appRouter = router({
auth: authRouter,
blog: blogRouter,
marketingPages: marketingPagesRouter,
navigation: navigationRouter,
media: mediaRouter,
// gen:routers — optional-core routers composed below
dsr: dsrRouter,
consent: consentRouter,
});
export type AppRouter = typeof appRouter;

View File

@@ -0,0 +1,394 @@
import { describe, it, expect, beforeEach } from "vitest";
import { router } from "@repo/core-shared/trpc/init";
import { appRouter } from "./root";
import { createDsrRouter } from "@repo/core-dsr";
import type { DsrBinding } from "@repo/core-dsr";
import type { DsrTrpcUser } from "@repo/core-dsr";
import { consentRouter } from "@repo/core-consent";
import type { ConsentFactory } from "@repo/core-consent";
import {
RecordingDataExport,
RecordingDataDelete,
RecordingDataRectify,
RecordingProcessingRestriction,
RecordingConsent,
} from "@repo/core-testing/instrumentation";
// ---------------------------------------------------------------------------
// Test helpers
// ---------------------------------------------------------------------------
function makeDsrBinding() {
return {
dataExport: new RecordingDataExport(),
dataDelete: new RecordingDataDelete(),
dataRectify: new RecordingDataRectify(),
processingRestriction: new RecordingProcessingRestriction(),
};
}
type DsrTestBinding = ReturnType<typeof makeDsrBinding>;
function makeIntegrationRouter(dsrBinding: DsrTestBinding) {
return router({
dsr: createDsrRouter(dsrBinding as unknown as DsrBinding),
consent: consentRouter,
});
}
type IntegrationRouter = ReturnType<typeof makeIntegrationRouter>;
function makeCaller(
testRouter: IntegrationRouter,
userId: string,
consentFactory: ConsentFactory,
roles: string[] = ["user"],
) {
return testRouter.createCaller({
user: { id: userId, roles } as DsrTrpcUser,
userId,
consentFactory,
} as Record<string, unknown>);
}
function makeUnauthCaller(
testRouter: IntegrationRouter,
consentFactory: ConsentFactory,
) {
return testRouter.createCaller({
consentFactory,
} as Record<string, unknown>);
}
// ---------------------------------------------------------------------------
// Structure tests — appRouter composition
// ---------------------------------------------------------------------------
describe("appRouter composition", () => {
it("exposes auth, blog, marketingPages, navigation, media routers", () => {
const procedures = appRouter._def.procedures;
const keys = Object.keys(procedures);
expect(keys.some((k) => k.startsWith("auth."))).toBe(true);
expect(keys.some((k) => k.startsWith("blog."))).toBe(true);
expect(keys.some((k) => k.startsWith("marketingPages."))).toBe(true);
expect(keys.some((k) => k.startsWith("navigation."))).toBe(true);
expect(keys.some((k) => k.startsWith("media."))).toBe(true);
});
it("blog router has expected procedures", () => {
const procedures = appRouter._def.procedures;
expect(procedures).toHaveProperty("blog.articleBySlug");
expect(procedures).toHaveProperty("blog.listArticles");
});
it("exposes dsr and consent routers", () => {
const keys = Object.keys(appRouter._def.procedures);
expect(keys.some((k) => k.startsWith("dsr."))).toBe(true);
expect(keys.some((k) => k.startsWith("consent."))).toBe(true);
});
it("dsr router exposes all four procedures", () => {
const procedures = appRouter._def.procedures;
expect(procedures).toHaveProperty("dsr.export");
expect(procedures).toHaveProperty("dsr.delete");
expect(procedures).toHaveProperty("dsr.rectify");
expect(procedures).toHaveProperty("dsr.restrict");
});
it("consent router exposes all four procedures", () => {
const procedures = appRouter._def.procedures;
expect(procedures).toHaveProperty("consent.grant");
expect(procedures).toHaveProperty("consent.withdraw");
expect(procedures).toHaveProperty("consent.isGranted");
expect(procedures).toHaveProperty("consent.getCategories");
});
});
// ---------------------------------------------------------------------------
// Integration tests — dsr procedures
// ---------------------------------------------------------------------------
describe("dsr.export — integration", () => {
let binding: DsrTestBinding;
let consent: RecordingConsent;
let caller: ReturnType<typeof makeCaller>;
beforeEach(() => {
binding = makeDsrBinding();
consent = new RecordingConsent();
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
caller = makeCaller(testRouter, "alice", factory);
});
it("resolves with UserDataBundle body for authenticated user", async () => {
const result = await caller.dsr.export({
subjectId: "alice",
format: "json",
});
expect(result.subjectId).toBe("alice");
expect(result.format).toBe("json");
expect(binding.dataExport.calls).toHaveLength(1);
});
it("resolves with json-ld format", async () => {
const result = await caller.dsr.export({
subjectId: "alice",
format: "json-ld",
});
expect(result.format).toBe("json-ld");
});
it("throws UNAUTHORIZED when unauthenticated", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.dsr.export({ subjectId: "alice", format: "json" }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("dsr.delete — integration", () => {
let binding: DsrTestBinding;
let consent: RecordingConsent;
beforeEach(() => {
binding = makeDsrBinding();
consent = new RecordingConsent();
});
it("resolves with DeletionCertificate for soft mode (authenticated user)", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const caller = makeCaller(testRouter, "alice", factory);
const result = await caller.dsr.delete({
subjectId: "alice",
mode: "soft",
});
expect(result.subjectId).toBe("alice");
expect(result.mode).toBe("soft");
expect(binding.dataDelete.calls).toHaveLength(1);
});
it("resolves with DeletionCertificate for cascade-hard mode (admin)", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const adminCaller = makeCaller(testRouter, "admin", factory, ["admin"]);
const result = await adminCaller.dsr.delete({
subjectId: "alice",
mode: "cascade-hard",
});
expect(result.mode).toBe("cascade-hard");
});
it("throws FORBIDDEN for cascade-hard when user lacks admin role", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const caller = makeCaller(testRouter, "alice", factory, ["user"]);
await expect(
caller.dsr.delete({ subjectId: "alice", mode: "cascade-hard" }),
).rejects.toMatchObject({ code: "FORBIDDEN" });
});
it("throws UNAUTHORIZED when unauthenticated", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.dsr.delete({ subjectId: "alice", mode: "soft" }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("dsr.rectify — integration", () => {
let binding: DsrTestBinding;
let consent: RecordingConsent;
let caller: ReturnType<typeof makeCaller>;
beforeEach(() => {
binding = makeDsrBinding();
consent = new RecordingConsent();
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
caller = makeCaller(testRouter, "alice", factory);
});
it("resolves with { ok: true } for authenticated user", async () => {
const result = await caller.dsr.rectify({
subjectId: "alice",
collection: "users",
field: "name",
value: "Alice Updated",
});
expect(result).toEqual({ ok: true });
expect(binding.dataRectify.calls[0]).toMatchObject({
subjectId: "alice",
collection: "users",
field: "name",
});
});
it("throws UNAUTHORIZED when unauthenticated", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.dsr.rectify({
subjectId: "alice",
collection: "users",
field: "name",
value: "x",
}),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("dsr.restrict — integration", () => {
let binding: DsrTestBinding;
let consent: RecordingConsent;
let caller: ReturnType<typeof makeCaller>;
beforeEach(() => {
binding = makeDsrBinding();
consent = new RecordingConsent();
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
caller = makeCaller(testRouter, "alice", factory);
});
it("resolves with { ok: true } when granting restriction", async () => {
const result = await caller.dsr.restrict({
subjectId: "alice",
granted: true,
});
expect(result).toEqual({ ok: true });
expect(binding.processingRestriction.sets[0]).toMatchObject({
subjectId: "alice",
granted: true,
});
});
it("resolves with { ok: true } when lifting restriction", async () => {
const result = await caller.dsr.restrict({
subjectId: "alice",
granted: false,
});
expect(result).toEqual({ ok: true });
expect(binding.processingRestriction.sets[0]?.granted).toBe(false);
});
it("throws UNAUTHORIZED when unauthenticated", async () => {
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.dsr.restrict({ subjectId: "alice", granted: true }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
// ---------------------------------------------------------------------------
// Integration tests — consent procedures
// ---------------------------------------------------------------------------
describe("consent — integration", () => {
let consent: RecordingConsent;
let caller: ReturnType<typeof makeCaller>;
beforeEach(() => {
const binding = makeDsrBinding();
consent = new RecordingConsent();
const factory: ConsentFactory = async () => consent;
const testRouter = makeIntegrationRouter(binding);
caller = makeCaller(testRouter, "user-1", factory);
});
describe("consent.grant", () => {
it("resolves with { success: true } and records the grant", async () => {
const result = await caller.consent.grant({ category: "analytics" });
expect(result).toEqual({ success: true });
expect(consent.grants).toHaveLength(1);
expect(consent.grants[0]!.category).toBe("analytics");
});
it("throws UNAUTHORIZED when userId is absent", async () => {
const c = new RecordingConsent();
const factory: ConsentFactory = async () => c;
const testRouter = makeIntegrationRouter(makeDsrBinding());
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.consent.grant({ category: "analytics" }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("consent.withdraw", () => {
it("resolves with { success: true } and records the withdrawal", async () => {
await caller.consent.grant({ category: "marketing" });
const result = await caller.consent.withdraw({ category: "marketing" });
expect(result).toEqual({ success: true });
expect(consent.withdrawals).toHaveLength(1);
expect(consent.withdrawals[0]).toBe("marketing");
});
it("throws UNAUTHORIZED when userId is absent", async () => {
const c = new RecordingConsent();
const factory: ConsentFactory = async () => c;
const testRouter = makeIntegrationRouter(makeDsrBinding());
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.consent.withdraw({ category: "analytics" }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("consent.isGranted", () => {
it("resolves with { granted: false } before any grant", async () => {
const result = await caller.consent.isGranted({ category: "analytics" });
expect(result).toEqual({ granted: false });
});
it("resolves with { granted: true } after grant", async () => {
await caller.consent.grant({ category: "analytics" });
const result = await caller.consent.isGranted({ category: "analytics" });
expect(result).toEqual({ granted: true });
});
it("throws UNAUTHORIZED when userId is absent", async () => {
const c = new RecordingConsent();
const factory: ConsentFactory = async () => c;
const testRouter = makeIntegrationRouter(makeDsrBinding());
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.consent.isGranted({ category: "analytics" }),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
describe("consent.getCategories", () => {
it("resolves with { categories: [] } initially", async () => {
const result = await caller.consent.getCategories({});
expect(result).toEqual({ categories: [] });
});
it("resolves with all granted categories", async () => {
await caller.consent.grant({ category: "necessary" });
await caller.consent.grant({ category: "analytics" });
const { categories } = await caller.consent.getCategories({});
expect(categories).toHaveLength(2);
const names = categories.map((c) => c.category).sort();
expect(names).toEqual(["analytics", "necessary"]);
});
it("throws UNAUTHORIZED when userId is absent", async () => {
const c = new RecordingConsent();
const factory: ConsentFactory = async () => c;
const testRouter = makeIntegrationRouter(makeDsrBinding());
const unauthCaller = makeUnauthCaller(testRouter, factory);
await expect(
unauthCaller.consent.getCategories({}),
).rejects.toMatchObject({ code: "UNAUTHORIZED" });
});
});
});

View File

@@ -0,0 +1,15 @@
{
"extends": "@repo/core-typescript/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": ".",
"declaration": false,
"declarationMap": false,
"types": ["vitest/globals"],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -0,0 +1,4 @@
{
"extends": ["//"],
"tags": ["core-composition"]
}

View File

@@ -0,0 +1,7 @@
import path from "node:path";
import { mergeConfig } from "vitest/config";
import { nodeVitestConfig } from "@repo/core-typescript/vitest.base.node";
export default mergeConfig(nodeVitestConfig, {
resolve: { alias: { "@": path.resolve(__dirname, "./src") } },
});