/** * Declared-context canonicalization — the TS arm of `ctxhash`. * * One algorithm, two implementations: this file and * `mosaic-edge-serve/internal/ctxhash`. The shared vectors in * `parity/fixtures/ctxhash/vectors.json` are the contract; a change here * that is not mirrored in Go fails both parity suites. * * The vocabulary is CLOSED on purpose: it is simultaneously the cardinality * fence (the cache-key space is the product of small enums) and the injection * defense (attacker-reachable strings never key a shared cache entry). * Unknown keys are dropped at both ends, by contract. An unknown value for a * known enum is dropped from the hash — never a new bucket, never a * generation. * * The client's hash is a cache hint. The server re-canonicalizes from raw * dims and its own hash is the identity for generation, billing, and * admission — nobody gets to pre-poison a bucket. */ /** Platform intent vocabulary. */ export declare const CONTEXT_INTENTS: readonly ["signup", "signin", "retain", "upsell", "reassure", "explain", "browse", "convert"]; /** Dayparts — the timecontext classification, lowercased for the hash. */ export declare const CONTEXT_DAYPARTS: readonly ["earlymorning", "morning", "latemorning", "midday", "afternoon", "evening", "lateevening", "latenight"]; export declare const CONTEXT_DAY_TYPES: readonly ["weekday", "weekend"]; export declare const CONTEXT_WEATHER: readonly ["clear", "cloudy", "rainy", "snowy"]; export declare const CONTEXT_TEMP_BANDS: readonly ["cold", "mild", "warm", "hot"]; export declare const CONTEXT_DEVICES: readonly ["mobile", "tablet", "desktop", "signage"]; /** Existing platform surface-type vocabulary (mosaic/lib/surface-types.ts). */ export declare const CONTEXT_SURFACE_TYPES: readonly ["vending", "retail", "transit", "stadium", "mall", "outdoor_billboard", "indoor_display", "point_of_sale", "lobby", "speaker", "kiosk", "ar", "web", "other", "grocery", "convenience", "restaurant", "cafe", "bar"]; export declare const MAX_CUSTOM_DIMS = 4; export declare const MAX_NOTES_LENGTH = 280; /** Canonical dim map: short keys, normalized lowercase values. */ export type CanonicalDims = Record; /** Developer-declared placement, the public shape (long field names). */ export interface DeclaredContext { /** Declared page class, `[a-z0-9-]{1,32}`. Never a URL or path. */ page?: string; /** Platform intent enum. */ intent?: string; /** Placement descriptor, never a user identifier. */ audience?: string; /** * Free text, ≤280 chars, describing the placement — never the visitor. * Hashed in full: a distinct notes value is a distinct context. */ notes?: string; /** Org-declared custom dims: `{ tier: "pro" }` becomes dim `x.tier`. */ custom?: Record; /** Literal custom dims: `"x.tier": "pro"` — same dim as `custom.tier`, * which wins on collision. Accepted because the server extractor accepts * them; dropping them client-side desyncs ctxhash from the signed hash. */ [key: `x.${string}`]: string | undefined; } /** * Normalizes the free-text `notes` dim. Returns undefined when the field is * empty after scrubbing or matches a rejected shape (emails, phone numbers, * long token-like runs) — the field is dropped, never partially kept. */ export declare function normalizeNotes(raw: string): string | undefined; export interface NormalizeResult { dims: CanonicalDims; /** Keys (or `key=value` pairs) removed during normalization. Handed back so * a caller can surface them; the SDK itself never warns. */ dropped: string[]; } /** * Normalizes a raw dim map into canonical dims: unknown keys dropped, enum * values validated, tokens pattern-checked, custom dims capped at * MAX_CUSTOM_DIMS (bytewise-first wins, deterministically), notes scrubbed. */ export declare function normalizeDims(raw: Record): NormalizeResult; /** * A context object as the wire carries it: the entries of `custom` become * `x.` keys (lowercased; they win over literal `x.*` keys) and * `custom` itself is removed. Every other key is kept as is. The server * reads custom dimensions only as `x.*` keys, so the browser's streams and * the server read both send context through this one function. */ export declare function flattenCustomContext>(context: T): Record; /** Maps the public DeclaredContext shape onto short-key dims. */ export declare function declaredToDims(declared: DeclaredContext): Record; /** * Canonical form: drop absent/unknown keys → lowercase → sort keys bytewise → * join "k:v|k:v". Already-normalized dims only; run normalizeDims first. */ export declare function canonicalContextString(dims: CanonicalDims): string; /** sha256(canonical) → hex, truncated to 16 chars (64 bits). */ export declare function computeContextHash(dims: CanonicalDims): Promise; /** Length-budget floors per kind — ceilings on the ceiling (spec §2.6). They * cap the case where a developer's fallback is a single word. */ export declare const FRAGMENT_KIND_FLOORS: Record; /** The mint validator's caps (mosaic/app/api/embed/v1/fragments/mint/route.ts * — both counted in UTF-16 code units, the same unit `.length` reads below). * Exported so warnings and docs cite the number the server actually * enforces. These MAY move again; the budget boundary below may not. */ export declare const MAX_FRAGMENT_FIELDS = 24; export declare const MAX_FALLBACK_CHARS = 2000; /** * Piecewise: identity at or below `QUANTIZE_ABOVE`, tapered and quantized * above it. * * Below the frozen boundary this is exactly the historical function — * ceil(max(len × 1.3, floor)) — so every schema hash computed by any shipped * SDK is stable across releases. Above it, two properties for large fields: * * - The multiplier tapers (1.05·len + 125, which equals 1.3·len exactly at * len = 500, so the curve is continuous): ±30% of a paragraph is a line, * ±30% of 5,000 chars is ~250px of layout shift. * - The result quantizes to steps of 64. The raw budget is in the schema * hash, so un-quantized, EVERY character edit re-keys the cache and costs * a fresh generation; stepped, a typo stays on the same copy while a new * sentence is honestly new. * * Server twin: mosaic/services/fragments/fragmentService.ts computeFieldBudget * — adopted in the fragments-0.3 release that lifted the caps, byte-identical, * held to the shared vectors (including `quantized_vectors`). The server * recomputes and never trusts the client's number. */ export declare function computeFieldBudget(fallback: string, kind: string): number; /** * Schema hash: keys + kinds + budgets, never the fallback prose (prose in the * GET URL would make the CDN object private and delete the cost story). MUST * stay byte-identical to mosaic/services/fragments/fragmentService.ts * computeSchemaHash; the shared vectors in * parity/fixtures/ctxhash/schema-vectors.json are the contract. */ export declare function computeSchemaHash(fields: readonly { key: string; kind: string; budget: number; }[]): Promise; /** * Convenience: declared shape → normalized dims → hash. Declared dims ONLY — * no environment parameter, by design: env signals must never enter the * fragment identity (fragments.ts doctrine), or the shared CDN cache keys on * device/daypart combinations and bills a fresh generation for each. A * signature that ACCEPTED an env bag was a standing invitation to defeat * that fence. */ export declare function hashDeclaredContext(declared: DeclaredContext): Promise<{ dims: CanonicalDims; hash: string; dropped: string[]; }>; //# sourceMappingURL=context.d.ts.map