Adds `withAnalytics(analytics, factory)` to packages/core-analytics — mirrors the `withAudit` pattern: thin forwarding closure that attaches the `__analyzed` brand via `attachBrand` from `@repo/core-shared/conformance` without mutating the original factory. Exports `Analyzed<F>` type and `withAnalytics` from the `@repo/core-analytics` root barrel. Adds `with-analytics.test.ts` asserting brand is present after wrapping, absent on the original fn, output passes through unchanged, and errors propagate. Adds `@repo/core-shared` as a production dependency. Also fixes `scripts/library-decisions/check.mjs` to exempt workspace-protocol entries (`workspace:*`) from the library trace requirement — internal monorepo packages are not third-party libraries and were incorrectly gated. Adds a regression test in `check.test.mjs` covering the exemption. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
37 lines
1.8 KiB
TypeScript
37 lines
1.8 KiB
TypeScript
import type { IAnalytics } from "./analytics.interface";
|
|
import { attachBrand } from "@repo/core-shared/conformance";
|
|
|
|
/**
|
|
* Phantom-type brand attached at wrap time by `withAnalytics`. The conformance
|
|
* system uses this as the type-level seam for use cases that declare
|
|
* `analyticsEvents: [...]` in their manifest — without `__analyzed`, the
|
|
* binding is not assignable to `ProductionUseCase<I, O, M>` when M demands it.
|
|
* At runtime the brand is a non-enumerable property attached by `attachBrand`
|
|
* from `@repo/core-shared/conformance`, so the boot-time assertion can verify
|
|
* the binding went through the analytics-aware path.
|
|
*/
|
|
export type Analyzed<F> = F & { readonly __analyzed: true };
|
|
|
|
/**
|
|
* Use-case wrapper applied at DI bind time. The wrapper is a thin closure
|
|
* that forwards to `fn` unchanged and carries the `__analyzed` brand. The
|
|
* forward closure (instead of returning `fn` directly) keeps the brand on
|
|
* a fresh function so the caller's original `fn` is not mutated — important
|
|
* when the same factory output is used elsewhere unwrapped (dev-seed paths,
|
|
* tests).
|
|
*/
|
|
export function withAnalytics<Args extends unknown[], R>(
|
|
// TODO: wire automated event recording from manifest declarations.
|
|
// `analyticsEvents[]` declarations. For now, the wrapper exists to:
|
|
// (1) require callers to pass the analytics instance at bind time (dep is available)
|
|
// (2) attach the `__analyzed` brand so the boot-time assertion can verify
|
|
// use cases were bound through the analytics-aware path.
|
|
analytics: IAnalytics,
|
|
fn: (...args: Args) => Promise<R>,
|
|
): Analyzed<(...args: Args) => Promise<R>> {
|
|
void analytics;
|
|
const wrapped: (...args: Args) => Promise<R> = (...args) => fn(...args);
|
|
attachBrand(wrapped, "__analyzed");
|
|
return wrapped as Analyzed<(...args: Args) => Promise<R>>;
|
|
}
|