feat(core-dsr): scaffold package + GDPR DSR interfaces and types

- Add pnpm turbo gen core-package dsr generator template and register
  dsr in CORE_PACKAGE_GENERATORS / choices list
- Run generator to produce packages/core-dsr/ shell
- Define IDataExport (Art. 15/20), IDataDelete (Art. 17),
  IDataRectify (Art. 16), IProcessingRestriction (Art. 18) interfaces
- Add UserDataBundle and DeletionCertificate types in dsr-types.ts
- Ship core-dsr/contexts/user-data.jsonld schema.org JSON-LD @context
- Wire @repo/core-dsr into transpilePackages (web-next)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 19:32:03 +00:00
parent 86d949294f
commit e378c950a9
24 changed files with 490 additions and 9 deletions

View File

@@ -797,11 +797,26 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js";
},
printConsentNextSteps,
],
dsr: () => [
() => {
assertOptionalPackageNotPresent("core-dsr");
return "Guard passed — packages/core-dsr does not exist yet.";
},
...emitTemplateTree("core-package/dsr", "packages/core-dsr"),
() => {
addToTranspilePackages(
"apps/web-next/next.config.mjs",
"@repo/core-dsr",
);
return "Added @repo/core-dsr to transpilePackages.";
},
printDsrNextSteps,
],
};
plop.setGenerator("core-package", {
description:
"Scaffold an optional core package (realtime, events, trpc, ui, audit, analytics, consent)",
"Scaffold an optional core package (realtime, events, trpc, ui, audit, analytics, consent, dsr)",
prompts: [
{
type: "list",
@@ -811,6 +826,7 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js";
"analytics",
"audit",
"consent",
"dsr",
"events",
"realtime",
"trpc",
@@ -1564,6 +1580,25 @@ function printConsentNextSteps(): string {
].join("\n");
}
function printDsrNextSteps(): string {
return [
"─────────────────────────────────────────────────────────────",
"@repo/core-dsr scaffolded into packages/core-dsr/.",
"",
"Next steps:",
"",
" 1. pnpm install # link the new workspace package",
"",
" 2. Implement DSR interfaces and types in packages/core-dsr/src/",
" (see story 06-core-dsr)",
"",
" 3. Add @repo/core-dsr to feature package.json files that need DSR",
"",
" 4. pnpm typecheck && pnpm lint && pnpm test",
"─────────────────────────────────────────────────────────────",
].join("\n");
}
function coreUiComponentActions(a: {
tier: "atom" | "molecule" | "organism";
name: string;

View File

@@ -0,0 +1,32 @@
# @repo/core-dsr
Optional core package providing GDPR Data Subject Rights (DSR) interfaces and implementations. Scaffold via `pnpm turbo gen core-package dsr`.
## Structure
```
src/
data-export.interface.ts # IDataExport — exportSubjectData
data-delete.interface.ts # IDataDelete — deleteSubjectData
data-rectify.interface.ts # IDataRectify — updateSubjectField
processing-restriction.interface.ts # IProcessingRestriction — setRestriction, isRestricted
dsr-types.ts # UserDataBundle, DeletionCertificate, DSR value types
contexts/
user-data.jsonld # schema.org JSON-LD @context
index.ts # Barrel export
```
## Design
Four interfaces map directly to GDPR Articles 1518 + 20:
- `IDataExport` (Art. 15/20) — export a subject's data as `UserDataBundle`
- `IDataDelete` (Art. 17) — soft-delete or cascade-hard-delete subject data; returns `DeletionCertificate`
- `IDataRectify` (Art. 16) — update a specific field for a subject
- `IProcessingRestriction` (Art. 18) — toggle and read the processing restriction flag
Implementations walk `custom.pii`-tagged fields and `custom.subject`-linked collections. Row semantics:
- `kind: "self" | "owner"` → directly owned by the subject (export full; delete hard or soft)
- `kind: "reference"` → references the subject from another entity (export redacted; redact link on delete)
See `docs/architecture/agent-first-workflow-and-conformance.md` for the DI conventions.

View File

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

View File

@@ -0,0 +1,27 @@
{
"name": "@repo/core-dsr",
"version": "0.0.1",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"build": "tsc --noEmit",
"lint": "eslint .",
"typecheck": "tsc --noEmit",
"test": "vitest run --passWithNoTests"
},
"dependencies": {
"@repo/core-shared": "workspace:*",
"zod": "^3.24.0"
},
"devDependencies": {
"@repo/core-eslint": "workspace:*",
"@repo/core-testing": "workspace:*",
"@repo/core-typescript": "workspace:*",
"@vitest/coverage-v8": "^3.0.0",
"typescript": "^5.8.0",
"vitest": "^3.0.0"
}
}

View File

@@ -0,0 +1,2 @@
// placeholder — populated by story 06-core-dsr
export {};

View File

@@ -0,0 +1,12 @@
{
"extends": "@repo/core-typescript/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["**/*.ts"],
"exclude": ["node_modules", "dist"]
}

View File

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

View File

@@ -0,0 +1,9 @@
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") },
},
});