/** * Overlay resolver — applies editorial overlays to a source projection, * walking the variant fallback chain. * * Variant axes are `locale`, `audience`, `market`. The resolver walks an * 8-step fallback (most-specific → least-specific) and applies the matching * overlay value using the merge rule from the field policy. * * Pure logic: no DB access, no IO. Callers fetch overlays by entity in one * query, pass them in here as `OverlayLookup`, and get back the resolved * field value tree. * * See `docs/architecture/catalog-architecture.md` §5.2.1 for the fallback * chain and §7.1 for the split rule that keeps merging predictable. */ import type { FieldPolicy, FieldPolicyRegistry, Visibility } from "../contract.js"; import { OVERLAY_DEFAULT_SCOPE } from "./schema.js"; /** A single overlay row reduced to what the resolver needs. */ export interface ResolverOverlay { field_path: string; locale: string; audience: Visibility | typeof OVERLAY_DEFAULT_SCOPE; market: string; value: unknown; } /** * The variant-scoped query the caller is asking the resolver to satisfy. * Returned values reflect overlays applicable to this exact tuple, with * fallbacks down through `default` sentinels. */ export interface ResolverScope { locale: string; audience: Visibility; market: string; /** The actor making the request — used for visibility filtering. */ actor: Visibility; } /** * Set of overlay rows fetched for a single entity. The resolver indexes * them internally by `(field_path, locale, audience, market)` for the * fallback walk. */ export type OverlayLookup = ReadonlyArray; /** * Resolved-view emitter. Given a per-entity source projection and the * applicable overlays, returns the per-(field_path → value) map after * applying the variant fallback chain, the merge rule, and the visibility * filter for the requesting actor. */ export interface ResolvedView { /** Resolved values keyed by field path. */ values: Map; /** * Fields the resolver intentionally omitted because they are not visible * to the requesting actor. Useful for debug / "preview as audience X" * views; consumers should not display these. */ hidden: Set; /** * Per-field provenance: which variant slice satisfied the lookup. `null` * means the source projection's value was used (no overlay applied). */ provenance: Map; } export interface ResolvedFieldProvenance { locale: string; audience: Visibility | typeof OVERLAY_DEFAULT_SCOPE; market: string; } /** * The 8-step variant fallback chain, ordered from most-specific to * least-specific. The resolver walks this list and stops at the first * overlay it finds for the requested field path. */ export declare function variantFallbackChain(scope: ResolverScope): Array<{ locale: string; audience: string; market: string; }>; /** * Applies the field policy's merge rule to combine a source value with an * overlay value. Returns the resolved value. * * Throws if `merge: "source-only"` is configured but an overlay was passed — * this means the overlay-write path validation failed and a forbidden * override slipped through; refuse to honor it at read time. */ export declare function applyMerge(policy: FieldPolicy, sourceValue: unknown, overlayValue: unknown): unknown; /** * Resolves a source projection plus a set of overlays into a final view for * the requesting `(locale, audience, market)` scope, filtered by the actor's * visibility. * * The source projection is keyed by field path; only fields present in the * registry are considered. Fields whose policy hides them from the actor's * audience are placed in `hidden`, not `values`. */ export declare function resolveOverlay(registry: FieldPolicyRegistry, sourceProjection: ReadonlyMap, overlays: OverlayLookup, scope: ResolverScope): ResolvedView; /** Visibility check: is the field visible to the requesting actor? */ export declare function isVisibleTo(policy: FieldPolicy, actor: Visibility): boolean; //# sourceMappingURL=resolver.d.ts.map