feat(core-consent): scaffold package with types, IConsent, withConsent brand wrapper
- Add packages/core-consent with ConsentCategory, ConsentState, UserConsentState types and IConsent interface - Add withConsent wrapper attaching __consentChecked brand at bind time; unit tests assert brand attachment and factory passthrough - Add ConsentChecked<F> type to core-shared/conformance/brands.ts and isConsentChecked helper to brand-runtime.ts - Extend FeatureManifest with requiresConsent?: readonly string[] field - Extend assertFeatureConformance to require __consentChecked brand when requiresConsent.length > 0; synthetic fixture tests cover pass/fail cases - Propagate __consentChecked in withSpan PROPAGATED_BRANDS so the outermost binding carries the brand Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
30
packages/core-consent/src/with-consent.ts
Normal file
30
packages/core-consent/src/with-consent.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { IConsent } from "./consent.interface";
|
||||
import type { ConsentChecked } from "@repo/core-shared/conformance";
|
||||
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.
|
||||
* 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,
|
||||
fn: (...args: Args) => Promise<R>,
|
||||
): ConsentChecked<(...args: Args) => Promise<R>> {
|
||||
void consent;
|
||||
const wrapped: (...args: Args) => Promise<R> = (...args) => fn(...args);
|
||||
attachBrand(wrapped, "__consentChecked");
|
||||
return wrapped as ConsentChecked<(...args: Args) => Promise<R>>;
|
||||
}
|
||||
Reference in New Issue
Block a user