feat(core-consent): extend generator with consent template + fix withCapture brand propagation
- Add consent to CORE_PACKAGE_GENERATORS in turbo/generators/config.ts so pnpm turbo gen core-package consent is a valid command (not hand-rollable) - Create turbo/generators/templates/core-package/consent/ mirroring the analytics template shape (AGENTS.md, package.json, tsconfig, turbo, vitest, eslint, src/index.ts scaffolds) - Regenerate packages/core-consent/ from the new template (replaces the previous hand-rolled attempt that violated the generator-first rule) - Add __consentChecked to withCapture PROPAGATED_BRANDS so the brand bubbles through the full withSpan→withCapture wrapper chain to the outermost binding that assertFeatureConformance reads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# @repo/core-consent
|
||||
|
||||
Optional core package providing a vendor-neutral consent management interface. Scaffold via `pnpm turbo gen core-package consent` (once the generator supports it).
|
||||
Optional core package providing a vendor-neutral consent management interface. Scaffold via `pnpm turbo gen core-package consent`.
|
||||
|
||||
## Structure
|
||||
|
||||
@@ -21,7 +21,7 @@ src/
|
||||
- `withdraw(category)` — record consent withdrawal for a category
|
||||
- `getCategories()` — list all known consent states
|
||||
|
||||
The interface is vendor-neutral: no storage implementation is bundled here. Concrete implementations (e.g. a Payload-backed store) are wired at DI bind time in `bind-production` (Story 04).
|
||||
The interface is vendor-neutral: no storage implementation is bundled here. Concrete implementations (e.g. a Payload-backed store) are wired at DI bind time in `bind-production`.
|
||||
|
||||
`withConsent` wraps a use-case factory at bind time, attaches the `__consentChecked` brand, and is the innermost wrapper in the composition chain:
|
||||
`withSpan → withCapture → withAudit → withAnalytics → withConsent → factory(deps)`
|
||||
|
||||
@@ -5,19 +5,16 @@ import { attachBrand } from "@repo/core-shared/conformance";
|
||||
export type { ConsentChecked };
|
||||
|
||||
/**
|
||||
* Use-case wrapper applied at DI bind time. The wrapper is a thin closure
|
||||
* that forwards to `fn` unchanged and carries the `__consentChecked` brand.
|
||||
* Use-case wrapper applied at DI bind time. Attaches the `__consentChecked`
|
||||
* brand so the boot-time assertion can verify consent-gated use cases were
|
||||
* bound through the consent-aware path.
|
||||
*
|
||||
* The forward closure keeps the brand on a fresh function so the original
|
||||
* `fn` reference is not mutated — important when the same factory output is
|
||||
* used elsewhere unwrapped (dev-seed paths, tests).
|
||||
*
|
||||
* Composition order (innermost to outermost):
|
||||
* withSpan → withCapture → withAudit → withAnalytics → withConsent → factory(deps)
|
||||
*
|
||||
* The wrapper exists to:
|
||||
* (1) require callers to pass the consent instance at bind time (dep is available)
|
||||
* (2) attach the `__consentChecked` brand so the boot-time assertion can verify
|
||||
* consent-gated use cases were bound through the consent-aware path.
|
||||
*/
|
||||
export function withConsent<Args extends unknown[], R>(
|
||||
consent: IConsent,
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import path from "node:path";
|
||||
import { defineConfig, mergeConfig } from "vitest/config";
|
||||
import { mergeConfig } from "vitest/config";
|
||||
import { nodeVitestConfig } from "@repo/core-typescript/vitest.base.node";
|
||||
|
||||
export default mergeConfig(
|
||||
nodeVitestConfig,
|
||||
defineConfig({
|
||||
test: {
|
||||
include: ["src/**/*.test.ts"],
|
||||
},
|
||||
resolve: {
|
||||
alias: { "@": path.resolve(__dirname, "./src") },
|
||||
},
|
||||
}),
|
||||
);
|
||||
export default mergeConfig(nodeVitestConfig, {
|
||||
resolve: {
|
||||
alias: { "@": path.resolve(__dirname, "./src") },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -32,6 +32,7 @@ export function withCapture<Args extends unknown[], R>(
|
||||
"__instrumented",
|
||||
"__audited",
|
||||
"__analyzed",
|
||||
"__consentChecked",
|
||||
] as const;
|
||||
|
||||
const wrapped: (...args: Args) => Promise<R> = async (...args) => {
|
||||
|
||||
Reference in New Issue
Block a user