/** * The SINGLE content-href authority for every embeddable surface — the * `ChatRuntime.composeContentUrl` seam. Page views (onboarding catalog/detail, * product releases) AND chat surfaces (entity cards, source chips, search * results) all resolve a content link through this one function, so a given * content type lands in the SAME place no matter where it's rendered. * * ## Why this exists * * Before unification there were two link builders: `composeContentUrl` (pages) * and `resolveSourceRowCTA` (chat), and only the former honored the embedder's * in-app routing config — so the same `product_release` soft-navigated in-app on * the releases page but opened OUT to the hub as a chat card. Now `resolveSource- * RowCTA` delegates its href to this seam, so one config (`hostedTypes` / * `overrides`) governs internal-vs-external for pages and chat alike. * * `makeComposeContentUrl` builds the embedder default from a small config: the * set of types THIS host serves in-app (→ relative href, soft-navigates) vs. * everything else (→ the canonical hub URL, opens out). The hub wires its own * composer (`composeContentUrlFromPlatforms` — cross-platform topology) to the * same seam. Pure + server-safe (no React, no browser APIs). * * composeContentUrl: makeComposeContentUrl({ * hostedTypes: new Set(['onboarding_guide', 'product_release']), * contentOrigin: VITE_HUB_ORIGIN, * }) */ /** * Type → in-app route suffix. The public-hostable subset of the hub's * `PUBLIC_URL_PATHS` (`lib/utils/content-url-builder.ts`); the hub keeps * its own copy, this is the embedder default and must stay in sync. (The * cross-repo boundary makes a shared import impossible; the lib test pins this * constant against a literal copy of itself, so it does NOT detect hub-side * drift — values match today and are kept aligned by hand.) */ export declare const DEFAULT_CONTENT_SUFFIXES: Record; /** Input to the unified content-href seam. ONE object covers both callers: * page views pass `type` + `identifier` (the slug); chat rows pass `type` + * `identifier` (the primary-key id) + `externalUrl` (the canonical hub URL, * from which the slug is recovered for in-app routing). */ export interface ComposeContentUrlInput { /** The content's CANONICAL documentType (e.g. `'product_release'`, * `'blog_post'`). * * Rail-vocab aliases (`'blog_post_existing'`) must be canonicalized by the * caller via `canonicalContentRefType` BEFORE they reach the seam: a host * writes its `hostedTypes` / `suffixes` / `overrides` against the canonical * vocabulary, so an alias silently misses every one of them and falls * through to the default `//` branch — which is how * the same blog post reached `/blog/` from one path and * `/blog_post_existing/` from another. */ type: string; /** Content identifier. Page views pass the slug; chat rows pass the * primary-key id (the slug is recovered from `externalUrl` when hosted). */ identifier: string; /** Preferred path segment for HOSTED types when there is no `externalUrl` * to recover the slug from — the Mingo/NATS transport ships bare * `[card://type:id]` markers with no ref metadata, so a fetch-mode card * knows the row's slug only AFTER it loads the row. `identifier` must stay * the primary key (that is what `overrides` deep-link on), hence a separate * field rather than overloading it. Ignored for non-hosted types. */ slug?: string | null; /** Hydrated platform junction from the list APIs. `hostedTypes` membership * still decides in-app vs out; this decides WHICH origin an out-link points * at — an OpenMSP-owned row resolves to openmsp.ai, not to the embedder's * `contentOrigin`. Read only when `targetPlatform` is absent. Accepts the * three junction shapes the DALs produce (see `primaryPlatformOf`). */ platforms?: Array<{ name?: string; }>; /** The canonical hub URL when the caller already has it (chat entity rows * carry it from the RAG mapper). Hosted types relativize it to an in-app * path; non-hosted types use it verbatim (authoritative). Absent for pages. */ externalUrl?: string | null; /** Platform that owns `externalUrl` (chat rows). Passed through on the * non-hosted branch. */ targetPlatform?: string | null; } export interface ContentHrefOptions { /** Types THIS host serves in-app → relative href (soft-nav). Everything * else resolves to the row's `externalUrl` / `contentOrigin` (opens out). */ hostedTypes: ReadonlySet; /** Fallback origin for non-hosted types with no `externalUrl` AND no * resolvable owning platform (e.g. `https://openframe.app`). When the row * DOES name its platform, that platform's canonical origin wins — otherwise * cross-platform content (an OpenMSP blog post) would be linked to the * wrong site. */ contentOrigin: string; /** Per-type route suffix. Defaults to {@link DEFAULT_CONTENT_SUFFIXES}. */ suffixes?: Record; /** Per-type full override — wins over the suffix logic. Receives the same * `identifier` the seam was called with. */ overrides?: Record { href: string; targetPlatform: string | null; }>; } /** The unified `composeContentUrl` seam shape on `ChatRuntime`. ALWAYS returns * a tuple (never null) — the seam type is non-nullable and callers read * `.href` unconditionally. */ export type ComposeContentUrl = (input: ComposeContentUrlInput) => { href: string; targetPlatform: string | null; }; /** * Build the embedder's `composeContentUrl` for the unified seam. * * Resolution order (the merged rule used by BOTH page views and chat cards): * 1. `overrides[type]` — explicit per-type href. * 2. `hostedTypes.has(type)` → relative `//` (in-app, soft-nav). * Chat rows carry the hub URL not the slug, so the slug is recovered from * `externalUrl`; page views pass the slug as `identifier`. * 3. `externalUrl` present → use it verbatim (RAG-authoritative hub URL; the * chat non-hosted case). * 4. else → `${contentOrigin}//` (page-view non-hosted). * * In-vs-out is decided by `hostedTypes` membership — NOT platform equality, * since an embedder has a free-form `source`. The `platforms` arg is part of the * seam signature but unused here. */ export declare function makeComposeContentUrl(opts: ContentHrefOptions): ComposeContentUrl; /** * Default href shape when `runtime.composeContentUrl` is NOT wired * (single-platform embedders without cross-platform topology). Shared by * every catalog/detail view that composes a content link (onboarding guides, * product releases, …) so the no-composer fallback has ONE source — both * views pass their `basePath`-derived shape through here. */ export declare function buildDefaultHref(basePath: string, slug: string): { href: string; targetPlatform: string | null; }; /** * Resolve a content link via the host's `composeContentUrl` when wired, else the * same-origin `buildDefaultHref` fallback — the exact ternary every catalog/detail page * VIEW repeated. Centralizes the `composeContentUrl` input shape + the fallback in one * place so a future input-shape change lands once, not per view. */ export declare function resolveContentHref(composeContentUrl: ComposeContentUrl | undefined, args: { type: string; slug: string; basePath: string; platforms?: ComposeContentUrlInput['platforms']; }): { href: string; targetPlatform: string | null; }; //# sourceMappingURL=content-href.d.ts.map