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` 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 & { 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( // The wrapper attaches the brand and ensures the analytics dependency is // available at bind time. Actual `analytics.track()` calls live in the // use case body — only the use case knows which properties to extract // from its input/output for the analytics event. analytics: IAnalytics, fn: (...args: Args) => Promise, ): Analyzed<(...args: Args) => Promise> { void analytics; const wrapped: (...args: Args) => Promise = (...args) => fn(...args); attachBrand(wrapped, "__analyzed"); return wrapped as Analyzed<(...args: Args) => Promise>; }