@duffcloudservices/cms-core 0.8.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{browser-Ds57q3s6.d.ts → browser-BIslXFmC.d.ts} +19 -15
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +5 -72
- package/dist/browser.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -72
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -390,7 +390,9 @@ interface MetaTag {
|
|
|
390
390
|
declare function buildMetaTags(seo: ResolvedSeo): MetaTag[];
|
|
391
391
|
|
|
392
392
|
/**
|
|
393
|
-
* Runtime content fetching for premium tier customers
|
|
393
|
+
* Runtime content fetching for premium tier customers.
|
|
394
|
+
*
|
|
395
|
+
* ⚠️ BOTH HELPERS ARE INERT. See the Q-297 block below before touching them.
|
|
394
396
|
*/
|
|
395
397
|
|
|
396
398
|
/**
|
|
@@ -406,26 +408,28 @@ interface FetchOptions {
|
|
|
406
408
|
}
|
|
407
409
|
/**
|
|
408
410
|
* Fetch runtime content from the DCS API.
|
|
409
|
-
* This is a premium tier feature - returns 403 for non-premium sites.
|
|
410
411
|
*
|
|
411
|
-
* @
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
* @
|
|
412
|
+
* @deprecated INERT — always resolves to `null` without issuing a request. The
|
|
413
|
+
* endpoint it was written against has never existed in any server mode; Q-297
|
|
414
|
+
* chose deletion over implementation. Use build-time content (`loadContentYaml`
|
|
415
|
+
* / `resolveTextKey`). Scheduled for removal in the next cms-core major.
|
|
416
|
+
*
|
|
417
|
+
* @param siteSlug - Ignored. Retained only for source compatibility.
|
|
418
|
+
* @param options - Ignored. Retained only for source compatibility.
|
|
419
|
+
* @returns Always `null` — the build-time-content fallback.
|
|
417
420
|
*/
|
|
418
421
|
declare function fetchRuntimeContent(siteSlug: string, options?: FetchOptions): Promise<ContentConfiguration | null>;
|
|
419
422
|
/**
|
|
420
423
|
* Fetch runtime SEO configuration from the DCS API.
|
|
421
|
-
* This is a premium tier feature - returns 403 for non-premium sites.
|
|
422
424
|
*
|
|
423
|
-
* @
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
* @
|
|
425
|
+
* @deprecated INERT — always resolves to `null` without issuing a request. The
|
|
426
|
+
* endpoint it was written against has never existed in any server mode; Q-297
|
|
427
|
+
* chose deletion over implementation. Use build-time SEO (`loadSeoYaml` /
|
|
428
|
+
* `resolveSeoForPage`). Scheduled for removal in the next cms-core major.
|
|
429
|
+
*
|
|
430
|
+
* @param siteSlug - Ignored. Retained only for source compatibility.
|
|
431
|
+
* @param options - Ignored. Retained only for source compatibility.
|
|
432
|
+
* @returns Always `null` — the build-time-SEO fallback.
|
|
429
433
|
*/
|
|
430
434
|
declare function fetchRuntimeSeo(siteSlug: string, options?: FetchOptions): Promise<SeoConfiguration | null>;
|
|
431
435
|
|
package/dist/browser.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { C as ContentConfiguration, a as ContentConfigurationV1, D as DcsPasskeyOptions, x as DcsPasskeyResult, F as FetchOptions, G as GlobalSeoConfig, M as MetaTag, k as PageEntry, b as PageSeoConfig, P as PagesConfiguration, R as ResolvedSeo, i as SeoAlternateConfig, c as SeoAuthorConfig, S as SeoConfiguration, e as SeoImagesConfig, f as SeoOpenGraphConfig, h as SeoSchemaConfig, d as SeoSocialConfig, g as SeoTwitterConfig, j as SeoVerificationConfig, t as authenticateDcsSitePasskey, p as buildMetaTags, q as fetchRuntimeContent, s as fetchRuntimeSeo, n as getGlobalContent, m as getPageContent, u as isDcsPasskeyImmediateUIAvailable, v as isDcsPasskeySupported, w as registerDcsSitePasskey, o as resolveSeoForPage, r as resolveTextKey } from './browser-
|
|
1
|
+
export { C as ContentConfiguration, a as ContentConfigurationV1, D as DcsPasskeyOptions, x as DcsPasskeyResult, F as FetchOptions, G as GlobalSeoConfig, M as MetaTag, k as PageEntry, b as PageSeoConfig, P as PagesConfiguration, R as ResolvedSeo, i as SeoAlternateConfig, c as SeoAuthorConfig, S as SeoConfiguration, e as SeoImagesConfig, f as SeoOpenGraphConfig, h as SeoSchemaConfig, d as SeoSocialConfig, g as SeoTwitterConfig, j as SeoVerificationConfig, t as authenticateDcsSitePasskey, p as buildMetaTags, q as fetchRuntimeContent, s as fetchRuntimeSeo, n as getGlobalContent, m as getPageContent, u as isDcsPasskeyImmediateUIAvailable, v as isDcsPasskeySupported, w as registerDcsSitePasskey, o as resolveSeoForPage, r as resolveTextKey } from './browser-BIslXFmC.js';
|
package/dist/browser.js
CHANGED
|
@@ -132,81 +132,14 @@ function buildMetaTags(seo) {
|
|
|
132
132
|
buildTwitterTags(tags, seo);
|
|
133
133
|
return tags;
|
|
134
134
|
}
|
|
135
|
-
|
|
136
|
-
// src/fetch.ts
|
|
137
|
-
var DEFAULT_API_URL = "https://api.duffcloudservices.com";
|
|
138
|
-
var DEFAULT_TIMEOUT = 5e3;
|
|
135
|
+
var PHANTOM_ENDPOINT_NOTICE = "[DCS] Runtime hydration is inert (Q-297): the DCS API has never served a runtime content/SEO endpoint, so this call is a no-op and the site falls back to build-time content. Remove the call \u2014 the helper is deleted in the next cms-core major.";
|
|
139
136
|
async function fetchRuntimeContent(siteSlug, options = {}) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
143
|
-
try {
|
|
144
|
-
const response = await fetch(
|
|
145
|
-
`${apiBaseUrl}/api/v1/content/runtime`,
|
|
146
|
-
{
|
|
147
|
-
method: "GET",
|
|
148
|
-
headers: {
|
|
149
|
-
"Content-Type": "application/json",
|
|
150
|
-
...options.headers
|
|
151
|
-
},
|
|
152
|
-
signal: controller.signal
|
|
153
|
-
}
|
|
154
|
-
);
|
|
155
|
-
if (!response.ok) {
|
|
156
|
-
if (response.status === 403) {
|
|
157
|
-
console.warn(
|
|
158
|
-
"[DCS] Runtime content requires premium tier. Using build-time content."
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
return null;
|
|
162
|
-
}
|
|
163
|
-
return await response.json();
|
|
164
|
-
} catch (error) {
|
|
165
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
166
|
-
console.warn("[DCS] Runtime content fetch timed out");
|
|
167
|
-
} else {
|
|
168
|
-
console.warn("[DCS] Runtime content fetch failed:", error);
|
|
169
|
-
}
|
|
170
|
-
return null;
|
|
171
|
-
} finally {
|
|
172
|
-
clearTimeout(timeoutId);
|
|
173
|
-
}
|
|
137
|
+
console.warn(PHANTOM_ENDPOINT_NOTICE);
|
|
138
|
+
return null;
|
|
174
139
|
}
|
|
175
140
|
async function fetchRuntimeSeo(siteSlug, options = {}) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
179
|
-
try {
|
|
180
|
-
const response = await fetch(
|
|
181
|
-
`${apiBaseUrl}/api/v1/seo/runtime`,
|
|
182
|
-
{
|
|
183
|
-
method: "GET",
|
|
184
|
-
headers: {
|
|
185
|
-
"Content-Type": "application/json",
|
|
186
|
-
...options.headers
|
|
187
|
-
},
|
|
188
|
-
signal: controller.signal
|
|
189
|
-
}
|
|
190
|
-
);
|
|
191
|
-
if (!response.ok) {
|
|
192
|
-
if (response.status === 403) {
|
|
193
|
-
console.warn(
|
|
194
|
-
"[DCS] Runtime SEO requires premium tier. Using build-time SEO."
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
return await response.json();
|
|
200
|
-
} catch (error) {
|
|
201
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
202
|
-
console.warn("[DCS] Runtime SEO fetch timed out");
|
|
203
|
-
} else {
|
|
204
|
-
console.warn("[DCS] Runtime SEO fetch failed:", error);
|
|
205
|
-
}
|
|
206
|
-
return null;
|
|
207
|
-
} finally {
|
|
208
|
-
clearTimeout(timeoutId);
|
|
209
|
-
}
|
|
141
|
+
console.warn(PHANTOM_ENDPOINT_NOTICE);
|
|
142
|
+
return null;
|
|
210
143
|
}
|
|
211
144
|
|
|
212
145
|
// src/platform-fetch.ts
|
package/dist/browser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/content.ts","../src/seo.ts","../src/fetch.ts","../src/platform-fetch.ts","../src/passkeys.ts"],"names":[],"mappings":"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","file":"browser.js","sourcesContent":["/**\n * Content resolution utilities\n */\n\nimport type { ContentConfiguration } from './types'\n\n/**\n * Resolve a text key for a specific page.\n * Checks page-specific content first, then falls back to global content.\n *\n * @param content - The content configuration\n * @param page - The page slug\n * @param key - The text key to resolve\n * @returns The resolved text value, or undefined if not found\n */\nexport function resolveTextKey(\n content: ContentConfiguration,\n page: string,\n key: string\n): string | undefined {\n // Check page-specific content first\n const pageContent = content.pages?.[page]\n if (pageContent && key in pageContent) {\n return pageContent[key]\n }\n\n // Fall back to global content\n if (content.global && key in content.global) {\n return content.global[key]\n }\n\n return undefined\n}\n\n/**\n * Get all content for a specific page, merging global and page-specific.\n *\n * @param content - The content configuration\n * @param page - The page slug\n * @returns Merged content object (global values overridden by page values)\n */\nexport function getPageContent(\n content: ContentConfiguration,\n page: string\n): Record<string, string> {\n const global = content.global ?? {}\n const pageContent = content.pages?.[page] ?? {}\n return { ...global, ...pageContent }\n}\n\n/**\n * Get only the global content.\n *\n * @param content - The content configuration\n * @returns Global content object\n */\nexport function getGlobalContent(\n content: ContentConfiguration\n): Record<string, string> {\n return content.global ?? {}\n}\n","/**\n * SEO resolution utilities\n */\n\nimport type {\n SeoConfiguration,\n ResolvedSeo,\n SeoOpenGraphConfig,\n SeoTwitterConfig,\n} from './types'\n\n/**\n * Resolve SEO configuration for a specific page.\n * Merges global defaults with page-specific overrides.\n *\n * @param seo - The SEO configuration\n * @param page - The page slug\n * @returns Resolved SEO object with all values filled in\n */\nexport function resolveSeoForPage(\n seo: SeoConfiguration,\n page: string\n): ResolvedSeo {\n const global = seo.global ?? {}\n const pageSeo = seo.pages?.[page] ?? {}\n\n // Resolve title with template\n let title = pageSeo.title ?? global.defaultTitle ?? ''\n if (title && global.titleTemplate && !pageSeo.noTitleTemplate) {\n title = global.titleTemplate.replace('%s', title)\n }\n\n const description = pageSeo.description ?? global.defaultDescription ?? ''\n\n // Resolve Open Graph\n const ogImage =\n pageSeo.openGraph?.image ?? global.images?.ogDefault ?? undefined\n const openGraph = resolveOpenGraph(pageSeo.openGraph, ogImage, title, description)\n\n // Resolve Twitter Card\n const twitterImage =\n pageSeo.twitter?.image ?? global.images?.twitterDefault ?? ogImage\n const twitter = resolveTwitterCard(\n pageSeo.twitter,\n twitterImage,\n title,\n description,\n global.social?.twitter\n )\n\n return {\n title,\n description,\n image: ogImage,\n siteName: global.siteName,\n siteUrl: global.siteUrl,\n locale: global.locale,\n canonical: pageSeo.canonical,\n robots: pageSeo.robots ?? global.robots,\n noIndex: pageSeo.robots?.includes('noindex'),\n openGraph,\n twitter,\n schemas: pageSeo.schemas ?? global.schemas,\n alternates: pageSeo.alternates,\n }\n}\n\n/**\n * Resolve Open Graph configuration\n */\nfunction resolveOpenGraph(\n pageOg: SeoOpenGraphConfig | undefined,\n ogImage: string | undefined,\n title: string,\n description: string\n): SeoOpenGraphConfig | undefined {\n if (pageOg) {\n return {\n ...pageOg,\n title: pageOg.title ?? title,\n description: pageOg.description ?? description,\n image: ogImage,\n }\n }\n\n if (ogImage) {\n return {\n title,\n description,\n image: ogImage,\n type: 'website',\n }\n }\n\n return undefined\n}\n\n/**\n * Resolve Twitter Card configuration\n */\nfunction resolveTwitterCard(\n pageTwitter: SeoTwitterConfig | undefined,\n twitterImage: string | undefined,\n title: string,\n description: string,\n globalTwitterHandle: string | undefined\n): SeoTwitterConfig | undefined {\n if (pageTwitter) {\n return {\n card: pageTwitter.card ?? 'summary_large_image',\n site: pageTwitter.site ?? globalTwitterHandle,\n ...pageTwitter,\n title: pageTwitter.title ?? title,\n description: pageTwitter.description ?? description,\n image: twitterImage,\n }\n }\n\n if (globalTwitterHandle || twitterImage) {\n return {\n card: 'summary_large_image',\n site: globalTwitterHandle,\n title,\n description,\n image: twitterImage,\n }\n }\n\n return undefined\n}\n\n/**\n * Meta tag representation for framework-agnostic usage\n */\nexport interface MetaTag {\n name?: string\n property?: string\n content: string\n}\n\n/**\n * Helper to add a meta tag if content exists\n */\nfunction addTag(\n tags: MetaTag[],\n content: string | undefined,\n attr: { name?: string; property?: string }\n): void {\n if (content) {\n tags.push({ ...attr, content })\n }\n}\n\n/**\n * Build Open Graph meta tags\n */\nfunction buildOpenGraphTags(\n tags: MetaTag[],\n seo: ResolvedSeo\n): void {\n const og = seo.openGraph\n if (!og) return\n\n addTag(tags, og.title, { property: 'og:title' })\n addTag(tags, og.description, { property: 'og:description' })\n addTag(tags, og.image, { property: 'og:image' })\n addTag(tags, og.imageAlt, { property: 'og:image:alt' })\n addTag(tags, og.imageWidth?.toString(), { property: 'og:image:width' })\n addTag(tags, og.imageHeight?.toString(), { property: 'og:image:height' })\n addTag(tags, og.type, { property: 'og:type' })\n addTag(tags, og.url, { property: 'og:url' })\n addTag(tags, seo.siteName, { property: 'og:site_name' })\n addTag(tags, seo.locale, { property: 'og:locale' })\n}\n\n/**\n * Build Twitter Card meta tags\n */\nfunction buildTwitterTags(\n tags: MetaTag[],\n seo: ResolvedSeo\n): void {\n const tw = seo.twitter\n if (!tw) return\n\n addTag(tags, tw.card, { name: 'twitter:card' })\n addTag(tags, tw.site ? `@${tw.site}` : undefined, { name: 'twitter:site' })\n addTag(tags, tw.creator ? `@${tw.creator}` : undefined, { name: 'twitter:creator' })\n addTag(tags, tw.title, { name: 'twitter:title' })\n addTag(tags, tw.description, { name: 'twitter:description' })\n addTag(tags, tw.image, { name: 'twitter:image' })\n addTag(tags, tw.imageAlt, { name: 'twitter:image:alt' })\n}\n\n/**\n * Build an array of meta tags from resolved SEO.\n * Useful for frameworks that need to manually set meta tags.\n *\n * @param seo - Resolved SEO object\n * @returns Array of meta tag objects\n */\nexport function buildMetaTags(seo: ResolvedSeo): MetaTag[] {\n const tags: MetaTag[] = []\n\n // Basic meta\n addTag(tags, seo.description, { name: 'description' })\n addTag(tags, seo.robots, { name: 'robots' })\n\n // Open Graph\n buildOpenGraphTags(tags, seo)\n\n // Twitter Card\n buildTwitterTags(tags, seo)\n\n return tags\n}\n","/**\n * Runtime content fetching for premium tier customers\n */\n\nimport type { ContentConfiguration, SeoConfiguration } from './types'\n\n/**\n * Options for runtime fetch operations\n */\nexport interface FetchOptions {\n /** Base URL for the DCS API */\n apiBaseUrl?: string\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number\n /** Custom headers to include */\n headers?: Record<string, string>\n}\n\nconst DEFAULT_API_URL = 'https://api.duffcloudservices.com'\nconst DEFAULT_TIMEOUT = 5000\n\n/**\n * Fetch runtime content from the DCS API.\n * This is a premium tier feature - returns 403 for non-premium sites.\n *\n * @param siteSlug - The site's slug identifier.\n * @deprecated The site is now resolved server-side from the request Host or the\n * dedicated Container App's `DCS_SITE_SLUG`. This argument is retained for source\n * compatibility but is ignored for routing (no longer placed in the URL path).\n * @param options - Fetch options\n * @returns Content configuration or null if fetch fails\n */\nexport async function fetchRuntimeContent(\n siteSlug: string,\n options: FetchOptions = {}\n): Promise<ContentConfiguration | null> {\n // `siteSlug` intentionally unused: the site is resolved server-side (Host / DCS_SITE_SLUG).\n void siteSlug\n const { apiBaseUrl = DEFAULT_API_URL, timeout = DEFAULT_TIMEOUT } = options\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const response = await fetch(\n `${apiBaseUrl}/api/v1/content/runtime`,\n {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n signal: controller.signal,\n }\n )\n\n if (!response.ok) {\n if (response.status === 403) {\n console.warn(\n '[DCS] Runtime content requires premium tier. Using build-time content.'\n )\n }\n return null\n }\n\n return (await response.json()) as ContentConfiguration\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n console.warn('[DCS] Runtime content fetch timed out')\n } else {\n console.warn('[DCS] Runtime content fetch failed:', error)\n }\n return null\n } finally {\n clearTimeout(timeoutId)\n }\n}\n\n/**\n * Fetch runtime SEO configuration from the DCS API.\n * This is a premium tier feature - returns 403 for non-premium sites.\n *\n * @param siteSlug - The site's slug identifier.\n * @deprecated The site is now resolved server-side from the request Host or the\n * dedicated Container App's `DCS_SITE_SLUG`. This argument is retained for source\n * compatibility but is ignored for routing (no longer placed in the URL path).\n * @param options - Fetch options\n * @returns SEO configuration or null if fetch fails\n */\nexport async function fetchRuntimeSeo(\n siteSlug: string,\n options: FetchOptions = {}\n): Promise<SeoConfiguration | null> {\n // `siteSlug` intentionally unused: the site is resolved server-side (Host / DCS_SITE_SLUG).\n void siteSlug\n const { apiBaseUrl = DEFAULT_API_URL, timeout = DEFAULT_TIMEOUT } = options\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const response = await fetch(\n `${apiBaseUrl}/api/v1/seo/runtime`,\n {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n signal: controller.signal,\n }\n )\n\n if (!response.ok) {\n if (response.status === 403) {\n console.warn(\n '[DCS] Runtime SEO requires premium tier. Using build-time SEO.'\n )\n }\n return null\n }\n\n return (await response.json()) as SeoConfiguration\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n console.warn('[DCS] Runtime SEO fetch timed out')\n } else {\n console.warn('[DCS] Runtime SEO fetch failed:', error)\n }\n return null\n } finally {\n clearTimeout(timeoutId)\n }\n}\n","/**\n * platformFetch — a `fetch` for DCS platform API calls that CANNOT swallow HTML.\n *\n * WHY THIS EXISTS (C-298 layer 2; .docs/analysis/fleet-api-reachability-2026-07-26.md §5)\n * ---------------------------------------------------------------------------------------\n * A customer site reaches the platform API only if two independent things agree:\n * (a) the base URL the bundle resolved at build time, and\n * (b) a `customDomains` array on one Front Door route.\n * Nothing checks the agreement, and when they disagree the failure mode is\n * **HTTP 200 `text/html`** — a relative `/api/v1/*` call falls through Front Door's\n * catch-all to the static site's `index.html`. Every status-only check reads that as\n * healthy.\n *\n * The consequence is not a visible error, it is SILENCE. KEPT (a paying customer) ran for\n * ~2 weeks with 24 dead endpoints — the whole revenue rail, membership, push, and all NINE\n * login routes — because each consumer swallowed the HTML:\n * * `useRevenueSiteConfig` `await response.json()` threw on `<!DOCTYPE html>`, the\n * composable caught it, and `subscriptionTermsEnabled` silently kept its `false`\n * default. That is also why C-141 N-7 could never be verified.\n * * `fetchSiteVisitorSession` treats an unparseable body as \"signed out\", which is the\n * EXPECTED state on most page loads — so a dead auth rail is indistinguishable from a\n * visitor who simply is not logged in.\n *\n * One assertion converts a silent feature outage into a diagnosable, named error.\n *\n * THE CONTRACT\n * ------------\n * `platformFetch` is a drop-in for `fetch` that returns the SAME `Response`, so every\n * existing `if (!response.ok)` branch keeps working byte-for-byte. It throws\n * {@link PlatformFetchError} in exactly ONE situation: the response carried a body that is\n * not JSON. Specifically:\n *\n * | response | platformFetch |\n * |--------------------------------------------|----------------------------------------|\n * | 2xx `application/json` | passes through |\n * | non-2xx `application/json` (a REAL API error, incl. 404/401/403/500) | passes through UNTOUCHED — callers keep their own error contract |\n * | `application/problem+json`, `text/json` | passes through (any `*json*` type) |\n * | no content-type, body sniffs as `{`/`[` | passes through (tolerates thin origins) |\n * | 204 / 205 / 304, or a genuinely empty body | passes through (no body to assert) |\n * | `text/html` (the C-261 class), `text/plain`, anything else non-empty | **THROWS** `PlatformFetchError` |\n *\n * That table is the whole design: **being loud is scoped to the misroute class, and\n * genuine API errors are never reclassified.** A 404 that answers with JSON is the API\n * working correctly and must never be reported as a reachability failure — the API said\n * \"not found\" and it was heard.\n *\n * Network-layer failures (DNS, TLS, offline, abort) reject with the platform's own\n * `TypeError`/`AbortError` exactly as `fetch` does. They are NOT wrapped: existing\n * `catch`/timeout handling around every call site depends on those shapes, and a network\n * error was never the silent class — it already throws.\n *\n * VOCABULARY SHARED WITH THE PROBE\n * --------------------------------\n * `bodyClass` here uses the same words as `cli/probe-agent-config/` (`json` | `spa-html` |\n * `html` | `empty` | `other`), so the client-side error and the per-site reachability\n * contract (C-298 layer 4) describe the same failure with the same term.\n *\n * @example\n * ```ts\n * import { platformFetch, PlatformFetchError } from '@duffcloudservices/cms-core'\n *\n * try {\n * const res = await platformFetch(`${base}/api/v1/revenue/config`)\n * if (!res.ok) return null // unchanged: a real API error\n * return await res.json()\n * } catch (e) {\n * if (e instanceof PlatformFetchError) {\n * // LOUD: names the URL, the content-type, and the body prefix.\n * console.error(e.message)\n * }\n * return null\n * }\n * ```\n */\n\n/** How a response body classifies. Same vocabulary as `cli/probe-agent-config/`. */\nexport type PlatformBodyClass = 'json' | 'spa-html' | 'html' | 'empty' | 'other'\n\n/**\n * Why a platform call failed the JSON assertion.\n *\n * - `non-json-body` — the response carried a non-JSON body. On `bodyClass: 'spa-html'`\n * this is the C-261 Front-Door-catch-all class almost by definition.\n * - `unparseable-json` — the content-type claimed JSON but the body would not parse\n * (only produced by {@link readPlatformJson}).\n */\nexport type PlatformFetchErrorKind = 'non-json-body' | 'unparseable-json'\n\n/** How much of the offending body the error message quotes. */\nconst BODY_PREFIX_CHARS = 180\n\n/** Statuses that are defined to carry no body — nothing to assert. */\nconst BODYLESS_STATUSES = new Set([204, 205, 304])\n\n/**\n * A platform API call whose response was not JSON.\n *\n * Carries everything needed to diagnose the misroute without re-running anything: the\n * URL that was called, the status, the received content-type, the `bodyClass`, and a\n * short body prefix. `instanceof` works across bundlers (the prototype is re-pinned for\n * transpiled `extends Error`).\n */\nexport class PlatformFetchError extends Error {\n /** Stable discriminator; survives minification, unlike a class-name check. */\n readonly isPlatformFetchError = true as const\n readonly kind: PlatformFetchErrorKind\n /** The URL that was requested (as passed in). */\n readonly url: string\n /** HTTP method, upper-cased. */\n readonly method: string\n /** HTTP status of the response that failed the assertion. */\n readonly status: number\n /** The `content-type` header as received (`''` when absent). */\n readonly contentType: string\n /** Classification of the received body. */\n readonly bodyClass: PlatformBodyClass\n /** First {@link BODY_PREFIX_CHARS} characters of the body, whitespace-collapsed. */\n readonly bodyPrefix: string\n\n constructor(init: {\n kind: PlatformFetchErrorKind\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n }) {\n super(buildMessage(init))\n this.name = 'PlatformFetchError'\n this.kind = init.kind\n this.url = init.url\n this.method = init.method\n this.status = init.status\n this.contentType = init.contentType\n this.bodyClass = init.bodyClass\n this.bodyPrefix = init.bodyPrefix\n // Transpiled `extends Error` loses the prototype chain on some targets.\n Object.setPrototypeOf(this, PlatformFetchError.prototype)\n }\n}\n\n/**\n * Narrow an unknown error to a {@link PlatformFetchError} without relying on `instanceof`\n * (safe across duplicated module instances / bundler boundaries).\n */\nexport function isPlatformFetchError(error: unknown): error is PlatformFetchError {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as { isPlatformFetchError?: unknown }).isPlatformFetchError === true\n )\n}\n\nfunction buildMessage(init: {\n kind: PlatformFetchErrorKind\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n}): string {\n const ct = init.contentType ? `\"${init.contentType}\"` : '(no content-type header)'\n const head =\n init.kind === 'unparseable-json'\n ? `[DCS] platformFetch: ${init.method} ${init.url} claimed ${ct} but the body did not parse as JSON`\n : `[DCS] platformFetch: ${init.method} ${init.url} answered with ${ct} where application/json was required`\n const why =\n init.bodyClass === 'spa-html' || init.bodyClass === 'html'\n ? ' — an HTML body on a platform API path means the request never reached the API: it fell through to a static site shell (Front Door catch-all -> index.html). Check that this host routes /api/v1/* to the platform API, or call the absolute public API host instead. See C-261 / C-298.'\n : ''\n return (\n `${head} (status ${init.status}, bodyClass=${init.bodyClass}).${why}` +\n (init.bodyPrefix ? ` Body starts: ${JSON.stringify(init.bodyPrefix)}` : '')\n )\n}\n\n/** `true` for `application/json`, `text/json`, `application/problem+json`, … */\nexport function isJsonContentType(contentType: string | null | undefined): boolean {\n return /json/i.test(contentType ?? '')\n}\n\n/**\n * Classify a body the way `cli/probe-agent-config/` does, so a client-side error and a\n * server-side reachability probe describe the same failure with the same word.\n */\nexport function classifyBodyClass(\n contentType: string | null | undefined,\n body: string | null | undefined,\n): PlatformBodyClass {\n const raw = body ?? ''\n if (raw.trim() === '') return 'empty'\n const trimmed = raw.trimStart()\n if (isJsonContentType(contentType) || trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return 'json'\n }\n if (/html/i.test(contentType ?? '') || trimmed.startsWith('<')) {\n // An SPA shell served in place of an API response is the trap this exists for.\n return /<div id=\"app\"|<div id=\"root\"|type=\"module\"/i.test(raw) ? 'spa-html' : 'html'\n }\n return 'other'\n}\n\n/** A violation observed by {@link platformFetch}. */\nexport interface PlatformFetchViolation {\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n kind: PlatformFetchErrorKind\n}\n\ntype ViolationReporter = (violation: PlatformFetchViolation) => void\n\nlet reporter: ViolationReporter | null = null\n\n/**\n * Register a side-channel for violations — e.g. an Application Insights\n * `trackEvent({ name: 'PlatformApiNonJsonResponse', ... })`. Optional by design:\n * `cms-core` must stay dependency-free, and the console error + thrown error are the\n * primary signal. Pass `null` to clear.\n */\nexport function setPlatformFetchReporter(fn: ViolationReporter | null): void {\n reporter = fn\n}\n\nexport interface PlatformFetchOptions {\n /**\n * Suppress the `console.error`. The error is still thrown — this only silences the\n * console leg, for consumers that log the thrown error themselves.\n */\n silent?: boolean\n}\n\n/** Read a clone's body without consuming the caller's `Response`. Best-effort. */\nasync function peekBody(response: Response): Promise<string | null> {\n const cloneable = typeof (response as { clone?: unknown }).clone === 'function'\n try {\n const source = cloneable ? response.clone() : response\n if (typeof source.text !== 'function') return null\n return await source.text()\n } catch {\n return null\n }\n}\n\nfunction collapse(body: string): string {\n const flat = body.replace(/\\s+/g, ' ').trim()\n return flat.length > BODY_PREFIX_CHARS ? `${flat.slice(0, BODY_PREFIX_CHARS)}…` : flat\n}\n\nfunction raise(\n violation: PlatformFetchViolation,\n options: PlatformFetchOptions | undefined,\n): PlatformFetchError {\n const error = new PlatformFetchError(violation)\n if (!options?.silent) console.error(error.message)\n try {\n reporter?.(violation)\n } catch {\n // A broken reporter must never mask the real failure.\n }\n return error\n}\n\n/**\n * `fetch` for platform API calls, with a content-type assertion that makes an HTML body\n * LOUD instead of swallowed. Returns the untouched `Response` (body unread) on success.\n *\n * @throws {PlatformFetchError} when the response carried a non-JSON body. Never for a\n * non-2xx JSON response — see the contract table at the top of this file.\n */\nexport async function platformFetch(\n input: string | URL | Request,\n init?: RequestInit,\n options?: PlatformFetchOptions,\n): Promise<Response> {\n const response = await fetch(input as RequestInfo, init)\n\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : ((input as Request).url ?? String(input))\n const method = String(init?.method ?? (input as Request)?.method ?? 'GET').toUpperCase()\n\n // Nothing to assert: no body is defined for these, and a HEAD never carries one.\n if (BODYLESS_STATUSES.has(response.status) || method === 'HEAD') return response\n\n const contentType = response.headers?.get?.('content-type') ?? ''\n\n // FAST PATH — the overwhelming majority. Never touches the body, so the caller's\n // `response.json()` / streaming behaviour is completely unaffected.\n if (isJsonContentType(contentType)) return response\n\n // Slow path only: we have to look at the body to tell \"HTML shell\" (loud) from\n // \"empty body\" / \"JSON without a content-type header\" (both tolerated).\n const body = await peekBody(response)\n if (body === null) {\n // Body not inspectable (no clone(), already consumed). Judge on the header alone:\n // an explicit non-JSON content-type is still the misroute class; a MISSING header\n // with no way to sniff is given the benefit of the doubt rather than invented.\n if (!contentType) return response\n throw raise(\n {\n kind: 'non-json-body',\n url,\n method,\n status: response.status,\n contentType,\n bodyClass: classifyBodyClass(contentType, ''),\n bodyPrefix: '',\n },\n options,\n )\n }\n\n const bodyClass = classifyBodyClass(contentType, body)\n // `json` covers a `{`/`[` body from an origin that forgot the header; `empty` covers a\n // 200 with no body, which is not the swallow class and whose handling belongs to the\n // caller (it would already fail its own `.json()` loudly).\n if (bodyClass === 'json' || bodyClass === 'empty') return response\n\n throw raise(\n {\n kind: 'non-json-body',\n url,\n method,\n status: response.status,\n contentType,\n bodyClass,\n bodyPrefix: collapse(body),\n },\n options,\n )\n}\n\n/**\n * Parse a `Response` body as JSON, turning a parse failure into the same named error\n * rather than a bare `SyntaxError: Unexpected token '<'`. Use after {@link platformFetch}\n * when you want the parse step named too.\n *\n * @throws {PlatformFetchError} `kind: 'unparseable-json'`\n */\nexport async function readPlatformJson<T>(\n response: Response,\n options?: PlatformFetchOptions,\n): Promise<T> {\n const contentType = response.headers?.get?.('content-type') ?? ''\n // Clone BEFORE parsing: once `.json()` has consumed the body, `clone()` throws and the\n // offending bytes are gone — which is exactly the evidence the error needs to quote.\n let spare: Response | null = null\n try {\n spare = typeof (response as { clone?: unknown }).clone === 'function' ? response.clone() : null\n } catch {\n spare = null\n }\n try {\n return (await response.json()) as T\n } catch {\n const body = spare ? await peekBody(spare) : null\n throw raise(\n {\n kind: 'unparseable-json',\n url: response.url ?? '',\n method: 'GET',\n status: response.status,\n contentType,\n // Classify from the BODY alone: reaching here means the content-type header\n // claimed JSON and was wrong, so it has no vote. A `text/html` shell served\n // under an `application/json` header is still the C-261 misroute class.\n bodyClass: classifyBodyClass(null, body),\n bodyPrefix: body ? collapse(body) : '',\n },\n options,\n )\n }\n}\n","import { platformFetch, readPlatformJson } from './platform-fetch'\n\nexport type DcsPasskeyOptions = {\n apiBaseUrl?: string\n returnTo?: string\n immediate?: boolean\n}\n\nexport type DcsPasskeyResult = {\n success: boolean\n redirectTo?: string\n visitor?: {\n email: string\n name: string\n picture?: string\n }\n}\n\ntype CeremonyOptions = {\n state: string\n publicKey: Record<string, unknown>\n}\n\ntype CredentialRequestWithImmediateUI = CredentialRequestOptions & {\n uiMode?: 'immediate'\n}\n\ntype ClientCapabilities = {\n immediateGet?: boolean\n}\n\ntype PublicKeyCredentialConstructorWithCapabilities = typeof PublicKeyCredential & {\n getClientCapabilities?: () => Promise<ClientCapabilities>\n}\n\nconst getPublicKeyCredentialConstructor = (): PublicKeyCredentialConstructorWithCapabilities | undefined => {\n if (typeof window === 'undefined' || typeof window.PublicKeyCredential === 'undefined') {\n return undefined\n }\n return window.PublicKeyCredential as PublicKeyCredentialConstructorWithCapabilities\n}\n\nconst apiBase = (value?: string) => (value ?? '/api/v1').replace(/\\/$/u, '')\n\nconst base64UrlToBuffer = (value: string): ArrayBuffer => {\n const base64 = value.replace(/-/g, '+').replace(/_/g, '/')\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=')\n const raw = window.atob(padded)\n const output = new Uint8Array(raw.length)\n for (let index = 0; index < raw.length; index += 1) {\n output[index] = raw.charCodeAt(index)\n }\n return output.buffer\n}\n\nconst bufferToBase64Url = (value: ArrayBuffer | null): string | null => {\n if (!value) {\n return null\n }\n const bytes = new Uint8Array(value)\n let binary = ''\n bytes.forEach(byte => {\n binary += String.fromCharCode(byte)\n })\n return window.btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/u, '')\n}\n\nconst decodeDescriptors = (items: unknown): PublicKeyCredentialDescriptor[] | undefined => {\n if (!Array.isArray(items)) {\n return undefined\n }\n return items.map(item => {\n const descriptor = item as { id?: unknown; type?: PublicKeyCredentialType; transports?: AuthenticatorTransport[] }\n return {\n ...descriptor,\n id: typeof descriptor.id === 'string' ? base64UrlToBuffer(descriptor.id) : descriptor.id,\n type: descriptor.type ?? 'public-key',\n } as PublicKeyCredentialDescriptor\n })\n}\n\nconst decodeCreationOptions = (publicKey: Record<string, unknown>): PublicKeyCredentialCreationOptions => {\n const user = publicKey.user as { id?: unknown } | undefined\n return {\n ...publicKey,\n challenge: base64UrlToBuffer(String(publicKey.challenge)),\n user: {\n ...(user ?? {}),\n id: typeof user?.id === 'string' ? base64UrlToBuffer(user.id) : user?.id,\n } as PublicKeyCredentialUserEntity,\n excludeCredentials: decodeDescriptors(publicKey.excludeCredentials),\n } as PublicKeyCredentialCreationOptions\n}\n\nconst decodeRequestOptions = (publicKey: Record<string, unknown>): PublicKeyCredentialRequestOptions => ({\n ...publicKey,\n challenge: base64UrlToBuffer(String(publicKey.challenge)),\n allowCredentials: decodeDescriptors(publicKey.allowCredentials),\n}) as PublicKeyCredentialRequestOptions\n\nconst serializeCredential = (credential: PublicKeyCredential): Record<string, unknown> => {\n const response = credential.response\n const base = {\n id: credential.id,\n type: credential.type,\n rawId: bufferToBase64Url(credential.rawId),\n authenticatorAttachment: credential.authenticatorAttachment,\n clientExtensionResults: credential.getClientExtensionResults(),\n }\n\n if (response instanceof AuthenticatorAttestationResponse) {\n return {\n ...base,\n response: {\n clientDataJSON: bufferToBase64Url(response.clientDataJSON),\n attestationObject: bufferToBase64Url(response.attestationObject),\n transports: response.getTransports?.() ?? [],\n },\n }\n }\n\n const assertion = response as AuthenticatorAssertionResponse\n return {\n ...base,\n response: {\n clientDataJSON: bufferToBase64Url(assertion.clientDataJSON),\n authenticatorData: bufferToBase64Url(assertion.authenticatorData),\n signature: bufferToBase64Url(assertion.signature),\n userHandle: bufferToBase64Url(assertion.userHandle),\n },\n }\n}\n\n// C-298 layer 2 adoption. `apiBase()` defaults to the RELATIVE `/api/v1`, so on a host\n// whose Front Door config has no `/api/v1/*` route these ceremonies get the SPA shell at\n// HTTP 200 — `response.ok` is true and `response.json()` throws a bare\n// \"Unexpected token '<'\". Passkey login was one of the nine dead KEPT site-auth routes.\n// platformFetch names the URL, the content-type and the body instead; a genuine non-2xx\n// JSON error still lands in the existing `!response.ok` branch untouched.\nconst postJSON = async <T>(url: string, body?: unknown): Promise<T> => {\n const response = await platformFetch(url, {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n })\n if (!response.ok) {\n throw new Error(`Passkey request failed (${response.status})`)\n }\n return await readPlatformJson<T>(response)\n}\n\nexport const isDcsPasskeySupported = (): boolean =>\n getPublicKeyCredentialConstructor() !== undefined &&\n typeof navigator !== 'undefined' &&\n typeof navigator.credentials?.get === 'function' &&\n typeof navigator.credentials?.create === 'function'\n\nexport const isDcsPasskeyImmediateUIAvailable = async (): Promise<boolean> => {\n const publicKeyCredential = getPublicKeyCredentialConstructor()\n if (!isDcsPasskeySupported() || typeof publicKeyCredential?.getClientCapabilities !== 'function') {\n return false\n }\n try {\n const capabilities = await publicKeyCredential.getClientCapabilities()\n return capabilities.immediateGet === true\n } catch {\n return false\n }\n}\n\nexport const registerDcsSitePasskey = async (options?: DcsPasskeyOptions): Promise<string | null> => {\n if (!isDcsPasskeySupported()) {\n throw new Error('Passkeys are not available in this browser.')\n }\n const base = apiBase(options?.apiBaseUrl)\n const ceremony = await postJSON<CeremonyOptions>(`${base}/site-auth/passkeys/register/options`)\n const credential = await navigator.credentials.create({\n publicKey: decodeCreationOptions(ceremony.publicKey),\n })\n if (!(credential instanceof PublicKeyCredential)) {\n throw new Error('Passkey registration was cancelled.')\n }\n const result = await postJSON<{ success: boolean; credentialId?: string }>(\n `${base}/site-auth/passkeys/register/verify`,\n {\n state: ceremony.state,\n credential: serializeCredential(credential),\n },\n )\n if (!result.success) {\n throw new Error('Passkey registration failed.')\n }\n return result.credentialId ?? null\n}\n\nexport const authenticateDcsSitePasskey = async (options?: DcsPasskeyOptions): Promise<DcsPasskeyResult> => {\n if (!isDcsPasskeySupported()) {\n throw new Error('Passkeys are not available in this browser.')\n }\n const base = apiBase(options?.apiBaseUrl)\n const ceremony = await postJSON<CeremonyOptions>(`${base}/site-auth/passkeys/authenticate/options`)\n const request: CredentialRequestWithImmediateUI = {\n publicKey: decodeRequestOptions(ceremony.publicKey),\n }\n if (options?.immediate) {\n request.uiMode = 'immediate'\n }\n const credential = await navigator.credentials.get(request)\n if (!(credential instanceof PublicKeyCredential)) {\n throw new Error('Passkey sign-in was cancelled.')\n }\n return await postJSON<DcsPasskeyResult>(`${base}/site-auth/passkeys/authenticate/verify`, {\n state: ceremony.state,\n credential: serializeCredential(credential),\n returnTo: options?.returnTo ?? '/',\n })\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/content.ts","../src/seo.ts","../src/fetch.ts","../src/platform-fetch.ts","../src/passkeys.ts"],"names":[],"mappings":"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","file":"browser.js","sourcesContent":["/**\n * Content resolution utilities\n */\n\nimport type { ContentConfiguration } from './types'\n\n/**\n * Resolve a text key for a specific page.\n * Checks page-specific content first, then falls back to global content.\n *\n * @param content - The content configuration\n * @param page - The page slug\n * @param key - The text key to resolve\n * @returns The resolved text value, or undefined if not found\n */\nexport function resolveTextKey(\n content: ContentConfiguration,\n page: string,\n key: string\n): string | undefined {\n // Check page-specific content first\n const pageContent = content.pages?.[page]\n if (pageContent && key in pageContent) {\n return pageContent[key]\n }\n\n // Fall back to global content\n if (content.global && key in content.global) {\n return content.global[key]\n }\n\n return undefined\n}\n\n/**\n * Get all content for a specific page, merging global and page-specific.\n *\n * @param content - The content configuration\n * @param page - The page slug\n * @returns Merged content object (global values overridden by page values)\n */\nexport function getPageContent(\n content: ContentConfiguration,\n page: string\n): Record<string, string> {\n const global = content.global ?? {}\n const pageContent = content.pages?.[page] ?? {}\n return { ...global, ...pageContent }\n}\n\n/**\n * Get only the global content.\n *\n * @param content - The content configuration\n * @returns Global content object\n */\nexport function getGlobalContent(\n content: ContentConfiguration\n): Record<string, string> {\n return content.global ?? {}\n}\n","/**\n * SEO resolution utilities\n */\n\nimport type {\n SeoConfiguration,\n ResolvedSeo,\n SeoOpenGraphConfig,\n SeoTwitterConfig,\n} from './types'\n\n/**\n * Resolve SEO configuration for a specific page.\n * Merges global defaults with page-specific overrides.\n *\n * @param seo - The SEO configuration\n * @param page - The page slug\n * @returns Resolved SEO object with all values filled in\n */\nexport function resolveSeoForPage(\n seo: SeoConfiguration,\n page: string\n): ResolvedSeo {\n const global = seo.global ?? {}\n const pageSeo = seo.pages?.[page] ?? {}\n\n // Resolve title with template\n let title = pageSeo.title ?? global.defaultTitle ?? ''\n if (title && global.titleTemplate && !pageSeo.noTitleTemplate) {\n title = global.titleTemplate.replace('%s', title)\n }\n\n const description = pageSeo.description ?? global.defaultDescription ?? ''\n\n // Resolve Open Graph\n const ogImage =\n pageSeo.openGraph?.image ?? global.images?.ogDefault ?? undefined\n const openGraph = resolveOpenGraph(pageSeo.openGraph, ogImage, title, description)\n\n // Resolve Twitter Card\n const twitterImage =\n pageSeo.twitter?.image ?? global.images?.twitterDefault ?? ogImage\n const twitter = resolveTwitterCard(\n pageSeo.twitter,\n twitterImage,\n title,\n description,\n global.social?.twitter\n )\n\n return {\n title,\n description,\n image: ogImage,\n siteName: global.siteName,\n siteUrl: global.siteUrl,\n locale: global.locale,\n canonical: pageSeo.canonical,\n robots: pageSeo.robots ?? global.robots,\n noIndex: pageSeo.robots?.includes('noindex'),\n openGraph,\n twitter,\n schemas: pageSeo.schemas ?? global.schemas,\n alternates: pageSeo.alternates,\n }\n}\n\n/**\n * Resolve Open Graph configuration\n */\nfunction resolveOpenGraph(\n pageOg: SeoOpenGraphConfig | undefined,\n ogImage: string | undefined,\n title: string,\n description: string\n): SeoOpenGraphConfig | undefined {\n if (pageOg) {\n return {\n ...pageOg,\n title: pageOg.title ?? title,\n description: pageOg.description ?? description,\n image: ogImage,\n }\n }\n\n if (ogImage) {\n return {\n title,\n description,\n image: ogImage,\n type: 'website',\n }\n }\n\n return undefined\n}\n\n/**\n * Resolve Twitter Card configuration\n */\nfunction resolveTwitterCard(\n pageTwitter: SeoTwitterConfig | undefined,\n twitterImage: string | undefined,\n title: string,\n description: string,\n globalTwitterHandle: string | undefined\n): SeoTwitterConfig | undefined {\n if (pageTwitter) {\n return {\n card: pageTwitter.card ?? 'summary_large_image',\n site: pageTwitter.site ?? globalTwitterHandle,\n ...pageTwitter,\n title: pageTwitter.title ?? title,\n description: pageTwitter.description ?? description,\n image: twitterImage,\n }\n }\n\n if (globalTwitterHandle || twitterImage) {\n return {\n card: 'summary_large_image',\n site: globalTwitterHandle,\n title,\n description,\n image: twitterImage,\n }\n }\n\n return undefined\n}\n\n/**\n * Meta tag representation for framework-agnostic usage\n */\nexport interface MetaTag {\n name?: string\n property?: string\n content: string\n}\n\n/**\n * Helper to add a meta tag if content exists\n */\nfunction addTag(\n tags: MetaTag[],\n content: string | undefined,\n attr: { name?: string; property?: string }\n): void {\n if (content) {\n tags.push({ ...attr, content })\n }\n}\n\n/**\n * Build Open Graph meta tags\n */\nfunction buildOpenGraphTags(\n tags: MetaTag[],\n seo: ResolvedSeo\n): void {\n const og = seo.openGraph\n if (!og) return\n\n addTag(tags, og.title, { property: 'og:title' })\n addTag(tags, og.description, { property: 'og:description' })\n addTag(tags, og.image, { property: 'og:image' })\n addTag(tags, og.imageAlt, { property: 'og:image:alt' })\n addTag(tags, og.imageWidth?.toString(), { property: 'og:image:width' })\n addTag(tags, og.imageHeight?.toString(), { property: 'og:image:height' })\n addTag(tags, og.type, { property: 'og:type' })\n addTag(tags, og.url, { property: 'og:url' })\n addTag(tags, seo.siteName, { property: 'og:site_name' })\n addTag(tags, seo.locale, { property: 'og:locale' })\n}\n\n/**\n * Build Twitter Card meta tags\n */\nfunction buildTwitterTags(\n tags: MetaTag[],\n seo: ResolvedSeo\n): void {\n const tw = seo.twitter\n if (!tw) return\n\n addTag(tags, tw.card, { name: 'twitter:card' })\n addTag(tags, tw.site ? `@${tw.site}` : undefined, { name: 'twitter:site' })\n addTag(tags, tw.creator ? `@${tw.creator}` : undefined, { name: 'twitter:creator' })\n addTag(tags, tw.title, { name: 'twitter:title' })\n addTag(tags, tw.description, { name: 'twitter:description' })\n addTag(tags, tw.image, { name: 'twitter:image' })\n addTag(tags, tw.imageAlt, { name: 'twitter:image:alt' })\n}\n\n/**\n * Build an array of meta tags from resolved SEO.\n * Useful for frameworks that need to manually set meta tags.\n *\n * @param seo - Resolved SEO object\n * @returns Array of meta tag objects\n */\nexport function buildMetaTags(seo: ResolvedSeo): MetaTag[] {\n const tags: MetaTag[] = []\n\n // Basic meta\n addTag(tags, seo.description, { name: 'description' })\n addTag(tags, seo.robots, { name: 'robots' })\n\n // Open Graph\n buildOpenGraphTags(tags, seo)\n\n // Twitter Card\n buildTwitterTags(tags, seo)\n\n return tags\n}\n","/**\n * Runtime content fetching for premium tier customers.\n *\n * ⚠️ BOTH HELPERS ARE INERT. See the Q-297 block below before touching them.\n */\n\nimport type { ContentConfiguration, SeoConfiguration } from './types'\n\n/**\n * Options for runtime fetch operations\n */\nexport interface FetchOptions {\n /** Base URL for the DCS API */\n apiBaseUrl?: string\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number\n /** Custom headers to include */\n headers?: Record<string, string>\n}\n\nconst DEFAULT_API_URL = 'https://api.duffcloudservices.com'\nconst DEFAULT_TIMEOUT = 5000\n\n/*\n * ─── Q-297 · the runtime hydration endpoints never existed ────────────────────\n *\n * These two helpers were written against `GET /api/v1/content/runtime` and\n * `GET /api/v1/seo/runtime`. NO DCS server mode has EVER served either path.\n * A `-S` pickaxe over every `*.go` file on every branch returns zero commits for\n * either string; the identical pickaxe for `agent/config` returns seven, so the\n * instrument works. The client drifted ahead of a contract that was designed and\n * never implemented — it was born broken in 6812eed2d (2026-01-07), which called\n * an equally phantom `/portal/sites/{slug}/content/runtime`, and ddfcff9a2\n * (2026-05-30) merely re-pointed one phantom path at another while making the\n * fleet-wide slug-free correction.\n *\n * The cost was a silent-failure class: a 404 fell through `!response.ok` and\n * returned `null` with no warning, so runtime content and SEO hydration died\n * quietly on customer sites while the calls piled up in the 404 numerator\n * (234 in 7 days on api.duffcloudservices.com alone, UA `node`).\n *\n * Q-297 (answered 2026-07-12) put the choice to the owner:\n * A. Delete the phantom callers in the next cms-core major. ← CHOSEN\n * B. Implement the endpoints server-side if premium runtime\n * hydration is on the revenue roadmap. rejected\n *\n * Option B is off the table, so there is no endpoint to re-point at: the real\n * content API is per-resource (`/api/v1/content/pages`, `/api/v1/content/pages/\n * {pageSlug}`, `/api/v1/pages/{pageSlug}/text`) and serves nothing shaped like a\n * whole ContentConfiguration or SeoConfiguration document.\n *\n * Option A's deletion is a breaking change to a published package's exported\n * surface (cms-react's useTextContent and cms-angular's ContentService both\n * import `fetchRuntimeContent`), so it is gated on the cms-core major that has\n * not been scheduled. Until then these helpers are INERT: they issue no request\n * and return `null` immediately.\n *\n * That is not a behaviour change. `null` is what production has always returned\n * here — the 404 guaranteed it — and every consumer already branches `if (data)`\n * onto build-time content. Removing the request changes no observable outcome;\n * it only stops the doomed call and converts the silence into a warning.\n *\n * DO NOT \"restore\" the fetch without a server route to point it at. Adding one is\n * option B, which was declined. `fetch.test.ts` pins all of this.\n * ─────────────────────────────────────────────────────────────────────────────\n */\n\nconst PHANTOM_ENDPOINT_NOTICE =\n '[DCS] Runtime hydration is inert (Q-297): the DCS API has never served a ' +\n 'runtime content/SEO endpoint, so this call is a no-op and the site falls ' +\n 'back to build-time content. Remove the call — the helper is deleted in the ' +\n 'next cms-core major.'\n\n/**\n * Fetch runtime content from the DCS API.\n *\n * @deprecated INERT — always resolves to `null` without issuing a request. The\n * endpoint it was written against has never existed in any server mode; Q-297\n * chose deletion over implementation. Use build-time content (`loadContentYaml`\n * / `resolveTextKey`). Scheduled for removal in the next cms-core major.\n *\n * @param siteSlug - Ignored. Retained only for source compatibility.\n * @param options - Ignored. Retained only for source compatibility.\n * @returns Always `null` — the build-time-content fallback.\n */\nexport async function fetchRuntimeContent(\n siteSlug: string,\n options: FetchOptions = {}\n): Promise<ContentConfiguration | null> {\n // Both arguments are intentionally unused: there is no endpoint to address.\n void siteSlug\n void options\n void DEFAULT_API_URL\n void DEFAULT_TIMEOUT\n\n console.warn(PHANTOM_ENDPOINT_NOTICE)\n return null\n}\n\n/**\n * Fetch runtime SEO configuration from the DCS API.\n *\n * @deprecated INERT — always resolves to `null` without issuing a request. The\n * endpoint it was written against has never existed in any server mode; Q-297\n * chose deletion over implementation. Use build-time SEO (`loadSeoYaml` /\n * `resolveSeoForPage`). Scheduled for removal in the next cms-core major.\n *\n * @param siteSlug - Ignored. Retained only for source compatibility.\n * @param options - Ignored. Retained only for source compatibility.\n * @returns Always `null` — the build-time-SEO fallback.\n */\nexport async function fetchRuntimeSeo(\n siteSlug: string,\n options: FetchOptions = {}\n): Promise<SeoConfiguration | null> {\n void siteSlug\n void options\n\n console.warn(PHANTOM_ENDPOINT_NOTICE)\n return null\n}\n","/**\n * platformFetch — a `fetch` for DCS platform API calls that CANNOT swallow HTML.\n *\n * WHY THIS EXISTS (C-298 layer 2; .docs/analysis/fleet-api-reachability-2026-07-26.md §5)\n * ---------------------------------------------------------------------------------------\n * A customer site reaches the platform API only if two independent things agree:\n * (a) the base URL the bundle resolved at build time, and\n * (b) a `customDomains` array on one Front Door route.\n * Nothing checks the agreement, and when they disagree the failure mode is\n * **HTTP 200 `text/html`** — a relative `/api/v1/*` call falls through Front Door's\n * catch-all to the static site's `index.html`. Every status-only check reads that as\n * healthy.\n *\n * The consequence is not a visible error, it is SILENCE. KEPT (a paying customer) ran for\n * ~2 weeks with 24 dead endpoints — the whole revenue rail, membership, push, and all NINE\n * login routes — because each consumer swallowed the HTML:\n * * `useRevenueSiteConfig` `await response.json()` threw on `<!DOCTYPE html>`, the\n * composable caught it, and `subscriptionTermsEnabled` silently kept its `false`\n * default. That is also why C-141 N-7 could never be verified.\n * * `fetchSiteVisitorSession` treats an unparseable body as \"signed out\", which is the\n * EXPECTED state on most page loads — so a dead auth rail is indistinguishable from a\n * visitor who simply is not logged in.\n *\n * One assertion converts a silent feature outage into a diagnosable, named error.\n *\n * THE CONTRACT\n * ------------\n * `platformFetch` is a drop-in for `fetch` that returns the SAME `Response`, so every\n * existing `if (!response.ok)` branch keeps working byte-for-byte. It throws\n * {@link PlatformFetchError} in exactly ONE situation: the response carried a body that is\n * not JSON. Specifically:\n *\n * | response | platformFetch |\n * |--------------------------------------------|----------------------------------------|\n * | 2xx `application/json` | passes through |\n * | non-2xx `application/json` (a REAL API error, incl. 404/401/403/500) | passes through UNTOUCHED — callers keep their own error contract |\n * | `application/problem+json`, `text/json` | passes through (any `*json*` type) |\n * | no content-type, body sniffs as `{`/`[` | passes through (tolerates thin origins) |\n * | 204 / 205 / 304, or a genuinely empty body | passes through (no body to assert) |\n * | `text/html` (the C-261 class), `text/plain`, anything else non-empty | **THROWS** `PlatformFetchError` |\n *\n * That table is the whole design: **being loud is scoped to the misroute class, and\n * genuine API errors are never reclassified.** A 404 that answers with JSON is the API\n * working correctly and must never be reported as a reachability failure — the API said\n * \"not found\" and it was heard.\n *\n * Network-layer failures (DNS, TLS, offline, abort) reject with the platform's own\n * `TypeError`/`AbortError` exactly as `fetch` does. They are NOT wrapped: existing\n * `catch`/timeout handling around every call site depends on those shapes, and a network\n * error was never the silent class — it already throws.\n *\n * VOCABULARY SHARED WITH THE PROBE\n * --------------------------------\n * `bodyClass` here uses the same words as `cli/probe-agent-config/` (`json` | `spa-html` |\n * `html` | `empty` | `other`), so the client-side error and the per-site reachability\n * contract (C-298 layer 4) describe the same failure with the same term.\n *\n * @example\n * ```ts\n * import { platformFetch, PlatformFetchError } from '@duffcloudservices/cms-core'\n *\n * try {\n * const res = await platformFetch(`${base}/api/v1/revenue/config`)\n * if (!res.ok) return null // unchanged: a real API error\n * return await res.json()\n * } catch (e) {\n * if (e instanceof PlatformFetchError) {\n * // LOUD: names the URL, the content-type, and the body prefix.\n * console.error(e.message)\n * }\n * return null\n * }\n * ```\n */\n\n/** How a response body classifies. Same vocabulary as `cli/probe-agent-config/`. */\nexport type PlatformBodyClass = 'json' | 'spa-html' | 'html' | 'empty' | 'other'\n\n/**\n * Why a platform call failed the JSON assertion.\n *\n * - `non-json-body` — the response carried a non-JSON body. On `bodyClass: 'spa-html'`\n * this is the C-261 Front-Door-catch-all class almost by definition.\n * - `unparseable-json` — the content-type claimed JSON but the body would not parse\n * (only produced by {@link readPlatformJson}).\n */\nexport type PlatformFetchErrorKind = 'non-json-body' | 'unparseable-json'\n\n/** How much of the offending body the error message quotes. */\nconst BODY_PREFIX_CHARS = 180\n\n/** Statuses that are defined to carry no body — nothing to assert. */\nconst BODYLESS_STATUSES = new Set([204, 205, 304])\n\n/**\n * A platform API call whose response was not JSON.\n *\n * Carries everything needed to diagnose the misroute without re-running anything: the\n * URL that was called, the status, the received content-type, the `bodyClass`, and a\n * short body prefix. `instanceof` works across bundlers (the prototype is re-pinned for\n * transpiled `extends Error`).\n */\nexport class PlatformFetchError extends Error {\n /** Stable discriminator; survives minification, unlike a class-name check. */\n readonly isPlatformFetchError = true as const\n readonly kind: PlatformFetchErrorKind\n /** The URL that was requested (as passed in). */\n readonly url: string\n /** HTTP method, upper-cased. */\n readonly method: string\n /** HTTP status of the response that failed the assertion. */\n readonly status: number\n /** The `content-type` header as received (`''` when absent). */\n readonly contentType: string\n /** Classification of the received body. */\n readonly bodyClass: PlatformBodyClass\n /** First {@link BODY_PREFIX_CHARS} characters of the body, whitespace-collapsed. */\n readonly bodyPrefix: string\n\n constructor(init: {\n kind: PlatformFetchErrorKind\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n }) {\n super(buildMessage(init))\n this.name = 'PlatformFetchError'\n this.kind = init.kind\n this.url = init.url\n this.method = init.method\n this.status = init.status\n this.contentType = init.contentType\n this.bodyClass = init.bodyClass\n this.bodyPrefix = init.bodyPrefix\n // Transpiled `extends Error` loses the prototype chain on some targets.\n Object.setPrototypeOf(this, PlatformFetchError.prototype)\n }\n}\n\n/**\n * Narrow an unknown error to a {@link PlatformFetchError} without relying on `instanceof`\n * (safe across duplicated module instances / bundler boundaries).\n */\nexport function isPlatformFetchError(error: unknown): error is PlatformFetchError {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as { isPlatformFetchError?: unknown }).isPlatformFetchError === true\n )\n}\n\nfunction buildMessage(init: {\n kind: PlatformFetchErrorKind\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n}): string {\n const ct = init.contentType ? `\"${init.contentType}\"` : '(no content-type header)'\n const head =\n init.kind === 'unparseable-json'\n ? `[DCS] platformFetch: ${init.method} ${init.url} claimed ${ct} but the body did not parse as JSON`\n : `[DCS] platformFetch: ${init.method} ${init.url} answered with ${ct} where application/json was required`\n const why =\n init.bodyClass === 'spa-html' || init.bodyClass === 'html'\n ? ' — an HTML body on a platform API path means the request never reached the API: it fell through to a static site shell (Front Door catch-all -> index.html). Check that this host routes /api/v1/* to the platform API, or call the absolute public API host instead. See C-261 / C-298.'\n : ''\n return (\n `${head} (status ${init.status}, bodyClass=${init.bodyClass}).${why}` +\n (init.bodyPrefix ? ` Body starts: ${JSON.stringify(init.bodyPrefix)}` : '')\n )\n}\n\n/** `true` for `application/json`, `text/json`, `application/problem+json`, … */\nexport function isJsonContentType(contentType: string | null | undefined): boolean {\n return /json/i.test(contentType ?? '')\n}\n\n/**\n * Classify a body the way `cli/probe-agent-config/` does, so a client-side error and a\n * server-side reachability probe describe the same failure with the same word.\n */\nexport function classifyBodyClass(\n contentType: string | null | undefined,\n body: string | null | undefined,\n): PlatformBodyClass {\n const raw = body ?? ''\n if (raw.trim() === '') return 'empty'\n const trimmed = raw.trimStart()\n if (isJsonContentType(contentType) || trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return 'json'\n }\n if (/html/i.test(contentType ?? '') || trimmed.startsWith('<')) {\n // An SPA shell served in place of an API response is the trap this exists for.\n return /<div id=\"app\"|<div id=\"root\"|type=\"module\"/i.test(raw) ? 'spa-html' : 'html'\n }\n return 'other'\n}\n\n/** A violation observed by {@link platformFetch}. */\nexport interface PlatformFetchViolation {\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n kind: PlatformFetchErrorKind\n}\n\ntype ViolationReporter = (violation: PlatformFetchViolation) => void\n\nlet reporter: ViolationReporter | null = null\n\n/**\n * Register a side-channel for violations — e.g. an Application Insights\n * `trackEvent({ name: 'PlatformApiNonJsonResponse', ... })`. Optional by design:\n * `cms-core` must stay dependency-free, and the console error + thrown error are the\n * primary signal. Pass `null` to clear.\n */\nexport function setPlatformFetchReporter(fn: ViolationReporter | null): void {\n reporter = fn\n}\n\nexport interface PlatformFetchOptions {\n /**\n * Suppress the `console.error`. The error is still thrown — this only silences the\n * console leg, for consumers that log the thrown error themselves.\n */\n silent?: boolean\n}\n\n/** Read a clone's body without consuming the caller's `Response`. Best-effort. */\nasync function peekBody(response: Response): Promise<string | null> {\n const cloneable = typeof (response as { clone?: unknown }).clone === 'function'\n try {\n const source = cloneable ? response.clone() : response\n if (typeof source.text !== 'function') return null\n return await source.text()\n } catch {\n return null\n }\n}\n\nfunction collapse(body: string): string {\n const flat = body.replace(/\\s+/g, ' ').trim()\n return flat.length > BODY_PREFIX_CHARS ? `${flat.slice(0, BODY_PREFIX_CHARS)}…` : flat\n}\n\nfunction raise(\n violation: PlatformFetchViolation,\n options: PlatformFetchOptions | undefined,\n): PlatformFetchError {\n const error = new PlatformFetchError(violation)\n if (!options?.silent) console.error(error.message)\n try {\n reporter?.(violation)\n } catch {\n // A broken reporter must never mask the real failure.\n }\n return error\n}\n\n/**\n * `fetch` for platform API calls, with a content-type assertion that makes an HTML body\n * LOUD instead of swallowed. Returns the untouched `Response` (body unread) on success.\n *\n * @throws {PlatformFetchError} when the response carried a non-JSON body. Never for a\n * non-2xx JSON response — see the contract table at the top of this file.\n */\nexport async function platformFetch(\n input: string | URL | Request,\n init?: RequestInit,\n options?: PlatformFetchOptions,\n): Promise<Response> {\n const response = await fetch(input as RequestInfo, init)\n\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : ((input as Request).url ?? String(input))\n const method = String(init?.method ?? (input as Request)?.method ?? 'GET').toUpperCase()\n\n // Nothing to assert: no body is defined for these, and a HEAD never carries one.\n if (BODYLESS_STATUSES.has(response.status) || method === 'HEAD') return response\n\n const contentType = response.headers?.get?.('content-type') ?? ''\n\n // FAST PATH — the overwhelming majority. Never touches the body, so the caller's\n // `response.json()` / streaming behaviour is completely unaffected.\n if (isJsonContentType(contentType)) return response\n\n // Slow path only: we have to look at the body to tell \"HTML shell\" (loud) from\n // \"empty body\" / \"JSON without a content-type header\" (both tolerated).\n const body = await peekBody(response)\n if (body === null) {\n // Body not inspectable (no clone(), already consumed). Judge on the header alone:\n // an explicit non-JSON content-type is still the misroute class; a MISSING header\n // with no way to sniff is given the benefit of the doubt rather than invented.\n if (!contentType) return response\n throw raise(\n {\n kind: 'non-json-body',\n url,\n method,\n status: response.status,\n contentType,\n bodyClass: classifyBodyClass(contentType, ''),\n bodyPrefix: '',\n },\n options,\n )\n }\n\n const bodyClass = classifyBodyClass(contentType, body)\n // `json` covers a `{`/`[` body from an origin that forgot the header; `empty` covers a\n // 200 with no body, which is not the swallow class and whose handling belongs to the\n // caller (it would already fail its own `.json()` loudly).\n if (bodyClass === 'json' || bodyClass === 'empty') return response\n\n throw raise(\n {\n kind: 'non-json-body',\n url,\n method,\n status: response.status,\n contentType,\n bodyClass,\n bodyPrefix: collapse(body),\n },\n options,\n )\n}\n\n/**\n * Parse a `Response` body as JSON, turning a parse failure into the same named error\n * rather than a bare `SyntaxError: Unexpected token '<'`. Use after {@link platformFetch}\n * when you want the parse step named too.\n *\n * @throws {PlatformFetchError} `kind: 'unparseable-json'`\n */\nexport async function readPlatformJson<T>(\n response: Response,\n options?: PlatformFetchOptions,\n): Promise<T> {\n const contentType = response.headers?.get?.('content-type') ?? ''\n // Clone BEFORE parsing: once `.json()` has consumed the body, `clone()` throws and the\n // offending bytes are gone — which is exactly the evidence the error needs to quote.\n let spare: Response | null = null\n try {\n spare = typeof (response as { clone?: unknown }).clone === 'function' ? response.clone() : null\n } catch {\n spare = null\n }\n try {\n return (await response.json()) as T\n } catch {\n const body = spare ? await peekBody(spare) : null\n throw raise(\n {\n kind: 'unparseable-json',\n url: response.url ?? '',\n method: 'GET',\n status: response.status,\n contentType,\n // Classify from the BODY alone: reaching here means the content-type header\n // claimed JSON and was wrong, so it has no vote. A `text/html` shell served\n // under an `application/json` header is still the C-261 misroute class.\n bodyClass: classifyBodyClass(null, body),\n bodyPrefix: body ? collapse(body) : '',\n },\n options,\n )\n }\n}\n","import { platformFetch, readPlatformJson } from './platform-fetch'\n\nexport type DcsPasskeyOptions = {\n apiBaseUrl?: string\n returnTo?: string\n immediate?: boolean\n}\n\nexport type DcsPasskeyResult = {\n success: boolean\n redirectTo?: string\n visitor?: {\n email: string\n name: string\n picture?: string\n }\n}\n\ntype CeremonyOptions = {\n state: string\n publicKey: Record<string, unknown>\n}\n\ntype CredentialRequestWithImmediateUI = CredentialRequestOptions & {\n uiMode?: 'immediate'\n}\n\ntype ClientCapabilities = {\n immediateGet?: boolean\n}\n\ntype PublicKeyCredentialConstructorWithCapabilities = typeof PublicKeyCredential & {\n getClientCapabilities?: () => Promise<ClientCapabilities>\n}\n\nconst getPublicKeyCredentialConstructor = (): PublicKeyCredentialConstructorWithCapabilities | undefined => {\n if (typeof window === 'undefined' || typeof window.PublicKeyCredential === 'undefined') {\n return undefined\n }\n return window.PublicKeyCredential as PublicKeyCredentialConstructorWithCapabilities\n}\n\nconst apiBase = (value?: string) => (value ?? '/api/v1').replace(/\\/$/u, '')\n\nconst base64UrlToBuffer = (value: string): ArrayBuffer => {\n const base64 = value.replace(/-/g, '+').replace(/_/g, '/')\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=')\n const raw = window.atob(padded)\n const output = new Uint8Array(raw.length)\n for (let index = 0; index < raw.length; index += 1) {\n output[index] = raw.charCodeAt(index)\n }\n return output.buffer\n}\n\nconst bufferToBase64Url = (value: ArrayBuffer | null): string | null => {\n if (!value) {\n return null\n }\n const bytes = new Uint8Array(value)\n let binary = ''\n bytes.forEach(byte => {\n binary += String.fromCharCode(byte)\n })\n return window.btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/u, '')\n}\n\nconst decodeDescriptors = (items: unknown): PublicKeyCredentialDescriptor[] | undefined => {\n if (!Array.isArray(items)) {\n return undefined\n }\n return items.map(item => {\n const descriptor = item as { id?: unknown; type?: PublicKeyCredentialType; transports?: AuthenticatorTransport[] }\n return {\n ...descriptor,\n id: typeof descriptor.id === 'string' ? base64UrlToBuffer(descriptor.id) : descriptor.id,\n type: descriptor.type ?? 'public-key',\n } as PublicKeyCredentialDescriptor\n })\n}\n\nconst decodeCreationOptions = (publicKey: Record<string, unknown>): PublicKeyCredentialCreationOptions => {\n const user = publicKey.user as { id?: unknown } | undefined\n return {\n ...publicKey,\n challenge: base64UrlToBuffer(String(publicKey.challenge)),\n user: {\n ...(user ?? {}),\n id: typeof user?.id === 'string' ? base64UrlToBuffer(user.id) : user?.id,\n } as PublicKeyCredentialUserEntity,\n excludeCredentials: decodeDescriptors(publicKey.excludeCredentials),\n } as PublicKeyCredentialCreationOptions\n}\n\nconst decodeRequestOptions = (publicKey: Record<string, unknown>): PublicKeyCredentialRequestOptions => ({\n ...publicKey,\n challenge: base64UrlToBuffer(String(publicKey.challenge)),\n allowCredentials: decodeDescriptors(publicKey.allowCredentials),\n}) as PublicKeyCredentialRequestOptions\n\nconst serializeCredential = (credential: PublicKeyCredential): Record<string, unknown> => {\n const response = credential.response\n const base = {\n id: credential.id,\n type: credential.type,\n rawId: bufferToBase64Url(credential.rawId),\n authenticatorAttachment: credential.authenticatorAttachment,\n clientExtensionResults: credential.getClientExtensionResults(),\n }\n\n if (response instanceof AuthenticatorAttestationResponse) {\n return {\n ...base,\n response: {\n clientDataJSON: bufferToBase64Url(response.clientDataJSON),\n attestationObject: bufferToBase64Url(response.attestationObject),\n transports: response.getTransports?.() ?? [],\n },\n }\n }\n\n const assertion = response as AuthenticatorAssertionResponse\n return {\n ...base,\n response: {\n clientDataJSON: bufferToBase64Url(assertion.clientDataJSON),\n authenticatorData: bufferToBase64Url(assertion.authenticatorData),\n signature: bufferToBase64Url(assertion.signature),\n userHandle: bufferToBase64Url(assertion.userHandle),\n },\n }\n}\n\n// C-298 layer 2 adoption. `apiBase()` defaults to the RELATIVE `/api/v1`, so on a host\n// whose Front Door config has no `/api/v1/*` route these ceremonies get the SPA shell at\n// HTTP 200 — `response.ok` is true and `response.json()` throws a bare\n// \"Unexpected token '<'\". Passkey login was one of the nine dead KEPT site-auth routes.\n// platformFetch names the URL, the content-type and the body instead; a genuine non-2xx\n// JSON error still lands in the existing `!response.ok` branch untouched.\nconst postJSON = async <T>(url: string, body?: unknown): Promise<T> => {\n const response = await platformFetch(url, {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n })\n if (!response.ok) {\n throw new Error(`Passkey request failed (${response.status})`)\n }\n return await readPlatformJson<T>(response)\n}\n\nexport const isDcsPasskeySupported = (): boolean =>\n getPublicKeyCredentialConstructor() !== undefined &&\n typeof navigator !== 'undefined' &&\n typeof navigator.credentials?.get === 'function' &&\n typeof navigator.credentials?.create === 'function'\n\nexport const isDcsPasskeyImmediateUIAvailable = async (): Promise<boolean> => {\n const publicKeyCredential = getPublicKeyCredentialConstructor()\n if (!isDcsPasskeySupported() || typeof publicKeyCredential?.getClientCapabilities !== 'function') {\n return false\n }\n try {\n const capabilities = await publicKeyCredential.getClientCapabilities()\n return capabilities.immediateGet === true\n } catch {\n return false\n }\n}\n\nexport const registerDcsSitePasskey = async (options?: DcsPasskeyOptions): Promise<string | null> => {\n if (!isDcsPasskeySupported()) {\n throw new Error('Passkeys are not available in this browser.')\n }\n const base = apiBase(options?.apiBaseUrl)\n const ceremony = await postJSON<CeremonyOptions>(`${base}/site-auth/passkeys/register/options`)\n const credential = await navigator.credentials.create({\n publicKey: decodeCreationOptions(ceremony.publicKey),\n })\n if (!(credential instanceof PublicKeyCredential)) {\n throw new Error('Passkey registration was cancelled.')\n }\n const result = await postJSON<{ success: boolean; credentialId?: string }>(\n `${base}/site-auth/passkeys/register/verify`,\n {\n state: ceremony.state,\n credential: serializeCredential(credential),\n },\n )\n if (!result.success) {\n throw new Error('Passkey registration failed.')\n }\n return result.credentialId ?? null\n}\n\nexport const authenticateDcsSitePasskey = async (options?: DcsPasskeyOptions): Promise<DcsPasskeyResult> => {\n if (!isDcsPasskeySupported()) {\n throw new Error('Passkeys are not available in this browser.')\n }\n const base = apiBase(options?.apiBaseUrl)\n const ceremony = await postJSON<CeremonyOptions>(`${base}/site-auth/passkeys/authenticate/options`)\n const request: CredentialRequestWithImmediateUI = {\n publicKey: decodeRequestOptions(ceremony.publicKey),\n }\n if (options?.immediate) {\n request.uiMode = 'immediate'\n }\n const credential = await navigator.credentials.get(request)\n if (!(credential instanceof PublicKeyCredential)) {\n throw new Error('Passkey sign-in was cancelled.')\n }\n return await postJSON<DcsPasskeyResult>(`${base}/site-auth/passkeys/authenticate/verify`, {\n state: ceremony.state,\n credential: serializeCredential(credential),\n returnTo: options?.returnTo ?? '/',\n })\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as ContentConfiguration, S as SeoConfiguration, P as PagesConfiguration } from './browser-
|
|
2
|
-
export { a as ContentConfigurationV1, D as DcsPasskeyOptions, x as DcsPasskeyResult, F as FetchOptions, G as GlobalSeoConfig, M as MetaTag, k as PageEntry, b as PageSeoConfig, R as ResolvedSeo, i as SeoAlternateConfig, c as SeoAuthorConfig, e as SeoImagesConfig, f as SeoOpenGraphConfig, h as SeoSchemaConfig, d as SeoSocialConfig, g as SeoTwitterConfig, j as SeoVerificationConfig, U as UnifiedConfiguration, l as UnifiedSiteConfig, t as authenticateDcsSitePasskey, p as buildMetaTags, q as fetchRuntimeContent, s as fetchRuntimeSeo, n as getGlobalContent, m as getPageContent, u as isDcsPasskeyImmediateUIAvailable, v as isDcsPasskeySupported, w as registerDcsSitePasskey, o as resolveSeoForPage, r as resolveTextKey } from './browser-
|
|
1
|
+
import { C as ContentConfiguration, S as SeoConfiguration, P as PagesConfiguration } from './browser-BIslXFmC.js';
|
|
2
|
+
export { a as ContentConfigurationV1, D as DcsPasskeyOptions, x as DcsPasskeyResult, F as FetchOptions, G as GlobalSeoConfig, M as MetaTag, k as PageEntry, b as PageSeoConfig, R as ResolvedSeo, i as SeoAlternateConfig, c as SeoAuthorConfig, e as SeoImagesConfig, f as SeoOpenGraphConfig, h as SeoSchemaConfig, d as SeoSocialConfig, g as SeoTwitterConfig, j as SeoVerificationConfig, U as UnifiedConfiguration, l as UnifiedSiteConfig, t as authenticateDcsSitePasskey, p as buildMetaTags, q as fetchRuntimeContent, s as fetchRuntimeSeo, n as getGlobalContent, m as getPageContent, u as isDcsPasskeyImmediateUIAvailable, v as isDcsPasskeySupported, w as registerDcsSitePasskey, o as resolveSeoForPage, r as resolveTextKey } from './browser-BIslXFmC.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* YAML file loaders for DCS configuration files
|
package/dist/index.js
CHANGED
|
@@ -236,81 +236,14 @@ async function loadConfigYaml(projectRoot) {
|
|
|
236
236
|
sources
|
|
237
237
|
};
|
|
238
238
|
}
|
|
239
|
-
|
|
240
|
-
// src/fetch.ts
|
|
241
|
-
var DEFAULT_API_URL = "https://api.duffcloudservices.com";
|
|
242
|
-
var DEFAULT_TIMEOUT = 5e3;
|
|
239
|
+
var PHANTOM_ENDPOINT_NOTICE = "[DCS] Runtime hydration is inert (Q-297): the DCS API has never served a runtime content/SEO endpoint, so this call is a no-op and the site falls back to build-time content. Remove the call \u2014 the helper is deleted in the next cms-core major.";
|
|
243
240
|
async function fetchRuntimeContent(siteSlug, options = {}) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
247
|
-
try {
|
|
248
|
-
const response = await fetch(
|
|
249
|
-
`${apiBaseUrl}/api/v1/content/runtime`,
|
|
250
|
-
{
|
|
251
|
-
method: "GET",
|
|
252
|
-
headers: {
|
|
253
|
-
"Content-Type": "application/json",
|
|
254
|
-
...options.headers
|
|
255
|
-
},
|
|
256
|
-
signal: controller.signal
|
|
257
|
-
}
|
|
258
|
-
);
|
|
259
|
-
if (!response.ok) {
|
|
260
|
-
if (response.status === 403) {
|
|
261
|
-
console.warn(
|
|
262
|
-
"[DCS] Runtime content requires premium tier. Using build-time content."
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
return null;
|
|
266
|
-
}
|
|
267
|
-
return await response.json();
|
|
268
|
-
} catch (error) {
|
|
269
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
270
|
-
console.warn("[DCS] Runtime content fetch timed out");
|
|
271
|
-
} else {
|
|
272
|
-
console.warn("[DCS] Runtime content fetch failed:", error);
|
|
273
|
-
}
|
|
274
|
-
return null;
|
|
275
|
-
} finally {
|
|
276
|
-
clearTimeout(timeoutId);
|
|
277
|
-
}
|
|
241
|
+
console.warn(PHANTOM_ENDPOINT_NOTICE);
|
|
242
|
+
return null;
|
|
278
243
|
}
|
|
279
244
|
async function fetchRuntimeSeo(siteSlug, options = {}) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
283
|
-
try {
|
|
284
|
-
const response = await fetch(
|
|
285
|
-
`${apiBaseUrl}/api/v1/seo/runtime`,
|
|
286
|
-
{
|
|
287
|
-
method: "GET",
|
|
288
|
-
headers: {
|
|
289
|
-
"Content-Type": "application/json",
|
|
290
|
-
...options.headers
|
|
291
|
-
},
|
|
292
|
-
signal: controller.signal
|
|
293
|
-
}
|
|
294
|
-
);
|
|
295
|
-
if (!response.ok) {
|
|
296
|
-
if (response.status === 403) {
|
|
297
|
-
console.warn(
|
|
298
|
-
"[DCS] Runtime SEO requires premium tier. Using build-time SEO."
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
return null;
|
|
302
|
-
}
|
|
303
|
-
return await response.json();
|
|
304
|
-
} catch (error) {
|
|
305
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
306
|
-
console.warn("[DCS] Runtime SEO fetch timed out");
|
|
307
|
-
} else {
|
|
308
|
-
console.warn("[DCS] Runtime SEO fetch failed:", error);
|
|
309
|
-
}
|
|
310
|
-
return null;
|
|
311
|
-
} finally {
|
|
312
|
-
clearTimeout(timeoutId);
|
|
313
|
-
}
|
|
245
|
+
console.warn(PHANTOM_ENDPOINT_NOTICE);
|
|
246
|
+
return null;
|
|
314
247
|
}
|
|
315
248
|
|
|
316
249
|
// src/platform-fetch.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/content.ts","../src/seo.ts","../src/loaders.ts","../src/fetch.ts","../src/platform-fetch.ts","../src/responsive-image.ts","../src/passkeys.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Content resolution utilities\n */\n\nimport type { ContentConfiguration } from './types'\n\n/**\n * Resolve a text key for a specific page.\n * Checks page-specific content first, then falls back to global content.\n *\n * @param content - The content configuration\n * @param page - The page slug\n * @param key - The text key to resolve\n * @returns The resolved text value, or undefined if not found\n */\nexport function resolveTextKey(\n content: ContentConfiguration,\n page: string,\n key: string\n): string | undefined {\n // Check page-specific content first\n const pageContent = content.pages?.[page]\n if (pageContent && key in pageContent) {\n return pageContent[key]\n }\n\n // Fall back to global content\n if (content.global && key in content.global) {\n return content.global[key]\n }\n\n return undefined\n}\n\n/**\n * Get all content for a specific page, merging global and page-specific.\n *\n * @param content - The content configuration\n * @param page - The page slug\n * @returns Merged content object (global values overridden by page values)\n */\nexport function getPageContent(\n content: ContentConfiguration,\n page: string\n): Record<string, string> {\n const global = content.global ?? {}\n const pageContent = content.pages?.[page] ?? {}\n return { ...global, ...pageContent }\n}\n\n/**\n * Get only the global content.\n *\n * @param content - The content configuration\n * @returns Global content object\n */\nexport function getGlobalContent(\n content: ContentConfiguration\n): Record<string, string> {\n return content.global ?? {}\n}\n","/**\n * SEO resolution utilities\n */\n\nimport type {\n SeoConfiguration,\n ResolvedSeo,\n SeoOpenGraphConfig,\n SeoTwitterConfig,\n} from './types'\n\n/**\n * Resolve SEO configuration for a specific page.\n * Merges global defaults with page-specific overrides.\n *\n * @param seo - The SEO configuration\n * @param page - The page slug\n * @returns Resolved SEO object with all values filled in\n */\nexport function resolveSeoForPage(\n seo: SeoConfiguration,\n page: string\n): ResolvedSeo {\n const global = seo.global ?? {}\n const pageSeo = seo.pages?.[page] ?? {}\n\n // Resolve title with template\n let title = pageSeo.title ?? global.defaultTitle ?? ''\n if (title && global.titleTemplate && !pageSeo.noTitleTemplate) {\n title = global.titleTemplate.replace('%s', title)\n }\n\n const description = pageSeo.description ?? global.defaultDescription ?? ''\n\n // Resolve Open Graph\n const ogImage =\n pageSeo.openGraph?.image ?? global.images?.ogDefault ?? undefined\n const openGraph = resolveOpenGraph(pageSeo.openGraph, ogImage, title, description)\n\n // Resolve Twitter Card\n const twitterImage =\n pageSeo.twitter?.image ?? global.images?.twitterDefault ?? ogImage\n const twitter = resolveTwitterCard(\n pageSeo.twitter,\n twitterImage,\n title,\n description,\n global.social?.twitter\n )\n\n return {\n title,\n description,\n image: ogImage,\n siteName: global.siteName,\n siteUrl: global.siteUrl,\n locale: global.locale,\n canonical: pageSeo.canonical,\n robots: pageSeo.robots ?? global.robots,\n noIndex: pageSeo.robots?.includes('noindex'),\n openGraph,\n twitter,\n schemas: pageSeo.schemas ?? global.schemas,\n alternates: pageSeo.alternates,\n }\n}\n\n/**\n * Resolve Open Graph configuration\n */\nfunction resolveOpenGraph(\n pageOg: SeoOpenGraphConfig | undefined,\n ogImage: string | undefined,\n title: string,\n description: string\n): SeoOpenGraphConfig | undefined {\n if (pageOg) {\n return {\n ...pageOg,\n title: pageOg.title ?? title,\n description: pageOg.description ?? description,\n image: ogImage,\n }\n }\n\n if (ogImage) {\n return {\n title,\n description,\n image: ogImage,\n type: 'website',\n }\n }\n\n return undefined\n}\n\n/**\n * Resolve Twitter Card configuration\n */\nfunction resolveTwitterCard(\n pageTwitter: SeoTwitterConfig | undefined,\n twitterImage: string | undefined,\n title: string,\n description: string,\n globalTwitterHandle: string | undefined\n): SeoTwitterConfig | undefined {\n if (pageTwitter) {\n return {\n card: pageTwitter.card ?? 'summary_large_image',\n site: pageTwitter.site ?? globalTwitterHandle,\n ...pageTwitter,\n title: pageTwitter.title ?? title,\n description: pageTwitter.description ?? description,\n image: twitterImage,\n }\n }\n\n if (globalTwitterHandle || twitterImage) {\n return {\n card: 'summary_large_image',\n site: globalTwitterHandle,\n title,\n description,\n image: twitterImage,\n }\n }\n\n return undefined\n}\n\n/**\n * Meta tag representation for framework-agnostic usage\n */\nexport interface MetaTag {\n name?: string\n property?: string\n content: string\n}\n\n/**\n * Helper to add a meta tag if content exists\n */\nfunction addTag(\n tags: MetaTag[],\n content: string | undefined,\n attr: { name?: string; property?: string }\n): void {\n if (content) {\n tags.push({ ...attr, content })\n }\n}\n\n/**\n * Build Open Graph meta tags\n */\nfunction buildOpenGraphTags(\n tags: MetaTag[],\n seo: ResolvedSeo\n): void {\n const og = seo.openGraph\n if (!og) return\n\n addTag(tags, og.title, { property: 'og:title' })\n addTag(tags, og.description, { property: 'og:description' })\n addTag(tags, og.image, { property: 'og:image' })\n addTag(tags, og.imageAlt, { property: 'og:image:alt' })\n addTag(tags, og.imageWidth?.toString(), { property: 'og:image:width' })\n addTag(tags, og.imageHeight?.toString(), { property: 'og:image:height' })\n addTag(tags, og.type, { property: 'og:type' })\n addTag(tags, og.url, { property: 'og:url' })\n addTag(tags, seo.siteName, { property: 'og:site_name' })\n addTag(tags, seo.locale, { property: 'og:locale' })\n}\n\n/**\n * Build Twitter Card meta tags\n */\nfunction buildTwitterTags(\n tags: MetaTag[],\n seo: ResolvedSeo\n): void {\n const tw = seo.twitter\n if (!tw) return\n\n addTag(tags, tw.card, { name: 'twitter:card' })\n addTag(tags, tw.site ? `@${tw.site}` : undefined, { name: 'twitter:site' })\n addTag(tags, tw.creator ? `@${tw.creator}` : undefined, { name: 'twitter:creator' })\n addTag(tags, tw.title, { name: 'twitter:title' })\n addTag(tags, tw.description, { name: 'twitter:description' })\n addTag(tags, tw.image, { name: 'twitter:image' })\n addTag(tags, tw.imageAlt, { name: 'twitter:image:alt' })\n}\n\n/**\n * Build an array of meta tags from resolved SEO.\n * Useful for frameworks that need to manually set meta tags.\n *\n * @param seo - Resolved SEO object\n * @returns Array of meta tag objects\n */\nexport function buildMetaTags(seo: ResolvedSeo): MetaTag[] {\n const tags: MetaTag[] = []\n\n // Basic meta\n addTag(tags, seo.description, { name: 'description' })\n addTag(tags, seo.robots, { name: 'robots' })\n\n // Open Graph\n buildOpenGraphTags(tags, seo)\n\n // Twitter Card\n buildTwitterTags(tags, seo)\n\n return tags\n}\n","/**\n * YAML file loaders for DCS configuration files\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\nimport type {\n ContentConfiguration,\n SeoConfiguration,\n PagesConfiguration,\n UnifiedConfiguration,\n} from './types'\n\n/**\n * Load and parse .dcs/content.yaml\n *\n * @param filePath - Path to content.yaml (absolute or relative to cwd)\n * @returns Parsed content configuration\n * @throws Error if file not found or parse fails\n */\nexport async function loadContentYaml(\n filePath: string\n): Promise<ContentConfiguration> {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath)\n\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\n const content = yaml.load(fileContent) as ContentConfiguration\n\n // Ensure required fields exist\n if (!content.version) {\n content.version = 1\n }\n if (!content.global) {\n content.global = {}\n }\n if (!content.pages) {\n content.pages = {}\n }\n\n return content\n}\n\n/**\n * Load and parse .dcs/seo.yaml\n *\n * @param filePath - Path to seo.yaml (absolute or relative to cwd)\n * @returns Parsed SEO configuration\n * @throws Error if file not found or parse fails\n */\nexport async function loadSeoYaml(filePath: string): Promise<SeoConfiguration> {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath)\n\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\n const seo = yaml.load(fileContent) as SeoConfiguration\n\n // Ensure required fields exist\n if (!seo.version) {\n seo.version = 1\n }\n\n return seo\n}\n\n/**\n * Load and parse .dcs/pages.yaml\n *\n * @param filePath - Path to pages.yaml (absolute or relative to cwd)\n * @returns Parsed pages configuration\n * @throws Error if file not found or parse fails\n */\nexport async function loadPagesYaml(\n filePath: string\n): Promise<PagesConfiguration> {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath)\n\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\n const pages = yaml.load(fileContent) as PagesConfiguration\n\n // Ensure required fields exist\n if (!pages.version) {\n pages.version = 3\n }\n if (!pages.pages) {\n pages.pages = []\n }\n\n return pages\n}\n\n/**\n * Try to find a DCS config file in common locations\n *\n * @param filename - The config file name (e.g., 'content.yaml')\n * @param projectRoot - The project root directory\n * @returns The found path, or undefined if not found\n */\nexport function findDcsConfigFile(\n filename: string,\n projectRoot: string\n): string | undefined {\n const possiblePaths = [\n path.resolve(projectRoot, '.dcs', filename),\n path.resolve(projectRoot, '..', '.dcs', filename), // For VitePress docs folder\n path.resolve(process.cwd(), '.dcs', filename),\n ]\n\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n return testPath\n }\n }\n\n return undefined\n}\n\n// ─── Unified Config Loader ──────────────────────────────────────\n\n/**\n * Result of loading DCS configuration, whether from unified config.yaml\n * or individual split files.\n */\nexport interface DcsConfigResult {\n /** Resolved content configuration */\n content: ContentConfiguration\n /** Resolved SEO configuration */\n seo: SeoConfiguration\n /** Resolved pages configuration */\n pages: PagesConfiguration\n /** Whether the configuration was loaded from a unified config.yaml */\n isUnified: boolean\n /** The source file path(s) that were loaded */\n sources: string[]\n}\n\n/**\n * Load DCS configuration from either a unified `.dcs/config.yaml` (version 2)\n * or fall back to individual split files (content.yaml, seo.yaml, pages.yaml).\n *\n * The unified format is preferred when `.dcs/config.yaml` exists and has `version: 2`.\n * This provides a single source of truth for all site configuration.\n *\n * @param projectRoot - The project root directory containing `.dcs/`\n * @returns Resolved configuration from all sources\n *\n * @example\n * ```typescript\n * const config = await loadConfigYaml('/path/to/site')\n * console.log(config.content.pages.home)\n * console.log(config.seo.global?.siteName)\n * console.log(config.pages.pages)\n * console.log(config.isUnified) // true if loaded from config.yaml\n * ```\n */\nexport async function loadConfigYaml(projectRoot: string): Promise<DcsConfigResult> {\n // Try unified config.yaml first\n const unifiedPath = findDcsConfigFile('config.yaml', projectRoot)\n if (unifiedPath) {\n try {\n const fileContent = fs.readFileSync(unifiedPath, 'utf8')\n const raw = yaml.load(fileContent) as Record<string, unknown>\n\n // Only treat as unified if version === 2\n if (raw && raw.version === 2) {\n const unified = raw as unknown as UnifiedConfiguration\n return {\n content: unified.content ?? { version: 1, global: {}, pages: {} },\n seo: unified.seo ?? { version: 1 },\n pages: {\n version: 3,\n siteSlug: unified.siteSlug ?? unified.site?.slug ?? '',\n pages: unified.pages ?? [],\n },\n isUnified: true,\n sources: [unifiedPath],\n }\n }\n } catch {\n // config.yaml exists but failed to parse — fall through to split files\n }\n }\n\n // Fall back to individual split files\n const sources: string[] = []\n\n let content: ContentConfiguration = { version: 1, global: {}, pages: {} }\n const contentPath = findDcsConfigFile('content.yaml', projectRoot)\n if (contentPath) {\n content = await loadContentYaml(contentPath)\n sources.push(contentPath)\n }\n\n let seo: SeoConfiguration = { version: 1 }\n const seoPath = findDcsConfigFile('seo.yaml', projectRoot)\n if (seoPath) {\n seo = await loadSeoYaml(seoPath)\n sources.push(seoPath)\n }\n\n let pages: PagesConfiguration = { version: 3, siteSlug: '', pages: [] }\n const pagesPath = findDcsConfigFile('pages.yaml', projectRoot)\n if (pagesPath) {\n pages = await loadPagesYaml(pagesPath)\n sources.push(pagesPath)\n }\n\n return {\n content,\n seo,\n pages,\n isUnified: false,\n sources,\n }\n}\n","/**\n * Runtime content fetching for premium tier customers\n */\n\nimport type { ContentConfiguration, SeoConfiguration } from './types'\n\n/**\n * Options for runtime fetch operations\n */\nexport interface FetchOptions {\n /** Base URL for the DCS API */\n apiBaseUrl?: string\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number\n /** Custom headers to include */\n headers?: Record<string, string>\n}\n\nconst DEFAULT_API_URL = 'https://api.duffcloudservices.com'\nconst DEFAULT_TIMEOUT = 5000\n\n/**\n * Fetch runtime content from the DCS API.\n * This is a premium tier feature - returns 403 for non-premium sites.\n *\n * @param siteSlug - The site's slug identifier.\n * @deprecated The site is now resolved server-side from the request Host or the\n * dedicated Container App's `DCS_SITE_SLUG`. This argument is retained for source\n * compatibility but is ignored for routing (no longer placed in the URL path).\n * @param options - Fetch options\n * @returns Content configuration or null if fetch fails\n */\nexport async function fetchRuntimeContent(\n siteSlug: string,\n options: FetchOptions = {}\n): Promise<ContentConfiguration | null> {\n // `siteSlug` intentionally unused: the site is resolved server-side (Host / DCS_SITE_SLUG).\n void siteSlug\n const { apiBaseUrl = DEFAULT_API_URL, timeout = DEFAULT_TIMEOUT } = options\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const response = await fetch(\n `${apiBaseUrl}/api/v1/content/runtime`,\n {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n signal: controller.signal,\n }\n )\n\n if (!response.ok) {\n if (response.status === 403) {\n console.warn(\n '[DCS] Runtime content requires premium tier. Using build-time content.'\n )\n }\n return null\n }\n\n return (await response.json()) as ContentConfiguration\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n console.warn('[DCS] Runtime content fetch timed out')\n } else {\n console.warn('[DCS] Runtime content fetch failed:', error)\n }\n return null\n } finally {\n clearTimeout(timeoutId)\n }\n}\n\n/**\n * Fetch runtime SEO configuration from the DCS API.\n * This is a premium tier feature - returns 403 for non-premium sites.\n *\n * @param siteSlug - The site's slug identifier.\n * @deprecated The site is now resolved server-side from the request Host or the\n * dedicated Container App's `DCS_SITE_SLUG`. This argument is retained for source\n * compatibility but is ignored for routing (no longer placed in the URL path).\n * @param options - Fetch options\n * @returns SEO configuration or null if fetch fails\n */\nexport async function fetchRuntimeSeo(\n siteSlug: string,\n options: FetchOptions = {}\n): Promise<SeoConfiguration | null> {\n // `siteSlug` intentionally unused: the site is resolved server-side (Host / DCS_SITE_SLUG).\n void siteSlug\n const { apiBaseUrl = DEFAULT_API_URL, timeout = DEFAULT_TIMEOUT } = options\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const response = await fetch(\n `${apiBaseUrl}/api/v1/seo/runtime`,\n {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n signal: controller.signal,\n }\n )\n\n if (!response.ok) {\n if (response.status === 403) {\n console.warn(\n '[DCS] Runtime SEO requires premium tier. Using build-time SEO.'\n )\n }\n return null\n }\n\n return (await response.json()) as SeoConfiguration\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n console.warn('[DCS] Runtime SEO fetch timed out')\n } else {\n console.warn('[DCS] Runtime SEO fetch failed:', error)\n }\n return null\n } finally {\n clearTimeout(timeoutId)\n }\n}\n","/**\n * platformFetch — a `fetch` for DCS platform API calls that CANNOT swallow HTML.\n *\n * WHY THIS EXISTS (C-298 layer 2; .docs/analysis/fleet-api-reachability-2026-07-26.md §5)\n * ---------------------------------------------------------------------------------------\n * A customer site reaches the platform API only if two independent things agree:\n * (a) the base URL the bundle resolved at build time, and\n * (b) a `customDomains` array on one Front Door route.\n * Nothing checks the agreement, and when they disagree the failure mode is\n * **HTTP 200 `text/html`** — a relative `/api/v1/*` call falls through Front Door's\n * catch-all to the static site's `index.html`. Every status-only check reads that as\n * healthy.\n *\n * The consequence is not a visible error, it is SILENCE. KEPT (a paying customer) ran for\n * ~2 weeks with 24 dead endpoints — the whole revenue rail, membership, push, and all NINE\n * login routes — because each consumer swallowed the HTML:\n * * `useRevenueSiteConfig` `await response.json()` threw on `<!DOCTYPE html>`, the\n * composable caught it, and `subscriptionTermsEnabled` silently kept its `false`\n * default. That is also why C-141 N-7 could never be verified.\n * * `fetchSiteVisitorSession` treats an unparseable body as \"signed out\", which is the\n * EXPECTED state on most page loads — so a dead auth rail is indistinguishable from a\n * visitor who simply is not logged in.\n *\n * One assertion converts a silent feature outage into a diagnosable, named error.\n *\n * THE CONTRACT\n * ------------\n * `platformFetch` is a drop-in for `fetch` that returns the SAME `Response`, so every\n * existing `if (!response.ok)` branch keeps working byte-for-byte. It throws\n * {@link PlatformFetchError} in exactly ONE situation: the response carried a body that is\n * not JSON. Specifically:\n *\n * | response | platformFetch |\n * |--------------------------------------------|----------------------------------------|\n * | 2xx `application/json` | passes through |\n * | non-2xx `application/json` (a REAL API error, incl. 404/401/403/500) | passes through UNTOUCHED — callers keep their own error contract |\n * | `application/problem+json`, `text/json` | passes through (any `*json*` type) |\n * | no content-type, body sniffs as `{`/`[` | passes through (tolerates thin origins) |\n * | 204 / 205 / 304, or a genuinely empty body | passes through (no body to assert) |\n * | `text/html` (the C-261 class), `text/plain`, anything else non-empty | **THROWS** `PlatformFetchError` |\n *\n * That table is the whole design: **being loud is scoped to the misroute class, and\n * genuine API errors are never reclassified.** A 404 that answers with JSON is the API\n * working correctly and must never be reported as a reachability failure — the API said\n * \"not found\" and it was heard.\n *\n * Network-layer failures (DNS, TLS, offline, abort) reject with the platform's own\n * `TypeError`/`AbortError` exactly as `fetch` does. They are NOT wrapped: existing\n * `catch`/timeout handling around every call site depends on those shapes, and a network\n * error was never the silent class — it already throws.\n *\n * VOCABULARY SHARED WITH THE PROBE\n * --------------------------------\n * `bodyClass` here uses the same words as `cli/probe-agent-config/` (`json` | `spa-html` |\n * `html` | `empty` | `other`), so the client-side error and the per-site reachability\n * contract (C-298 layer 4) describe the same failure with the same term.\n *\n * @example\n * ```ts\n * import { platformFetch, PlatformFetchError } from '@duffcloudservices/cms-core'\n *\n * try {\n * const res = await platformFetch(`${base}/api/v1/revenue/config`)\n * if (!res.ok) return null // unchanged: a real API error\n * return await res.json()\n * } catch (e) {\n * if (e instanceof PlatformFetchError) {\n * // LOUD: names the URL, the content-type, and the body prefix.\n * console.error(e.message)\n * }\n * return null\n * }\n * ```\n */\n\n/** How a response body classifies. Same vocabulary as `cli/probe-agent-config/`. */\nexport type PlatformBodyClass = 'json' | 'spa-html' | 'html' | 'empty' | 'other'\n\n/**\n * Why a platform call failed the JSON assertion.\n *\n * - `non-json-body` — the response carried a non-JSON body. On `bodyClass: 'spa-html'`\n * this is the C-261 Front-Door-catch-all class almost by definition.\n * - `unparseable-json` — the content-type claimed JSON but the body would not parse\n * (only produced by {@link readPlatformJson}).\n */\nexport type PlatformFetchErrorKind = 'non-json-body' | 'unparseable-json'\n\n/** How much of the offending body the error message quotes. */\nconst BODY_PREFIX_CHARS = 180\n\n/** Statuses that are defined to carry no body — nothing to assert. */\nconst BODYLESS_STATUSES = new Set([204, 205, 304])\n\n/**\n * A platform API call whose response was not JSON.\n *\n * Carries everything needed to diagnose the misroute without re-running anything: the\n * URL that was called, the status, the received content-type, the `bodyClass`, and a\n * short body prefix. `instanceof` works across bundlers (the prototype is re-pinned for\n * transpiled `extends Error`).\n */\nexport class PlatformFetchError extends Error {\n /** Stable discriminator; survives minification, unlike a class-name check. */\n readonly isPlatformFetchError = true as const\n readonly kind: PlatformFetchErrorKind\n /** The URL that was requested (as passed in). */\n readonly url: string\n /** HTTP method, upper-cased. */\n readonly method: string\n /** HTTP status of the response that failed the assertion. */\n readonly status: number\n /** The `content-type` header as received (`''` when absent). */\n readonly contentType: string\n /** Classification of the received body. */\n readonly bodyClass: PlatformBodyClass\n /** First {@link BODY_PREFIX_CHARS} characters of the body, whitespace-collapsed. */\n readonly bodyPrefix: string\n\n constructor(init: {\n kind: PlatformFetchErrorKind\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n }) {\n super(buildMessage(init))\n this.name = 'PlatformFetchError'\n this.kind = init.kind\n this.url = init.url\n this.method = init.method\n this.status = init.status\n this.contentType = init.contentType\n this.bodyClass = init.bodyClass\n this.bodyPrefix = init.bodyPrefix\n // Transpiled `extends Error` loses the prototype chain on some targets.\n Object.setPrototypeOf(this, PlatformFetchError.prototype)\n }\n}\n\n/**\n * Narrow an unknown error to a {@link PlatformFetchError} without relying on `instanceof`\n * (safe across duplicated module instances / bundler boundaries).\n */\nexport function isPlatformFetchError(error: unknown): error is PlatformFetchError {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as { isPlatformFetchError?: unknown }).isPlatformFetchError === true\n )\n}\n\nfunction buildMessage(init: {\n kind: PlatformFetchErrorKind\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n}): string {\n const ct = init.contentType ? `\"${init.contentType}\"` : '(no content-type header)'\n const head =\n init.kind === 'unparseable-json'\n ? `[DCS] platformFetch: ${init.method} ${init.url} claimed ${ct} but the body did not parse as JSON`\n : `[DCS] platformFetch: ${init.method} ${init.url} answered with ${ct} where application/json was required`\n const why =\n init.bodyClass === 'spa-html' || init.bodyClass === 'html'\n ? ' — an HTML body on a platform API path means the request never reached the API: it fell through to a static site shell (Front Door catch-all -> index.html). Check that this host routes /api/v1/* to the platform API, or call the absolute public API host instead. See C-261 / C-298.'\n : ''\n return (\n `${head} (status ${init.status}, bodyClass=${init.bodyClass}).${why}` +\n (init.bodyPrefix ? ` Body starts: ${JSON.stringify(init.bodyPrefix)}` : '')\n )\n}\n\n/** `true` for `application/json`, `text/json`, `application/problem+json`, … */\nexport function isJsonContentType(contentType: string | null | undefined): boolean {\n return /json/i.test(contentType ?? '')\n}\n\n/**\n * Classify a body the way `cli/probe-agent-config/` does, so a client-side error and a\n * server-side reachability probe describe the same failure with the same word.\n */\nexport function classifyBodyClass(\n contentType: string | null | undefined,\n body: string | null | undefined,\n): PlatformBodyClass {\n const raw = body ?? ''\n if (raw.trim() === '') return 'empty'\n const trimmed = raw.trimStart()\n if (isJsonContentType(contentType) || trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return 'json'\n }\n if (/html/i.test(contentType ?? '') || trimmed.startsWith('<')) {\n // An SPA shell served in place of an API response is the trap this exists for.\n return /<div id=\"app\"|<div id=\"root\"|type=\"module\"/i.test(raw) ? 'spa-html' : 'html'\n }\n return 'other'\n}\n\n/** A violation observed by {@link platformFetch}. */\nexport interface PlatformFetchViolation {\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n kind: PlatformFetchErrorKind\n}\n\ntype ViolationReporter = (violation: PlatformFetchViolation) => void\n\nlet reporter: ViolationReporter | null = null\n\n/**\n * Register a side-channel for violations — e.g. an Application Insights\n * `trackEvent({ name: 'PlatformApiNonJsonResponse', ... })`. Optional by design:\n * `cms-core` must stay dependency-free, and the console error + thrown error are the\n * primary signal. Pass `null` to clear.\n */\nexport function setPlatformFetchReporter(fn: ViolationReporter | null): void {\n reporter = fn\n}\n\nexport interface PlatformFetchOptions {\n /**\n * Suppress the `console.error`. The error is still thrown — this only silences the\n * console leg, for consumers that log the thrown error themselves.\n */\n silent?: boolean\n}\n\n/** Read a clone's body without consuming the caller's `Response`. Best-effort. */\nasync function peekBody(response: Response): Promise<string | null> {\n const cloneable = typeof (response as { clone?: unknown }).clone === 'function'\n try {\n const source = cloneable ? response.clone() : response\n if (typeof source.text !== 'function') return null\n return await source.text()\n } catch {\n return null\n }\n}\n\nfunction collapse(body: string): string {\n const flat = body.replace(/\\s+/g, ' ').trim()\n return flat.length > BODY_PREFIX_CHARS ? `${flat.slice(0, BODY_PREFIX_CHARS)}…` : flat\n}\n\nfunction raise(\n violation: PlatformFetchViolation,\n options: PlatformFetchOptions | undefined,\n): PlatformFetchError {\n const error = new PlatformFetchError(violation)\n if (!options?.silent) console.error(error.message)\n try {\n reporter?.(violation)\n } catch {\n // A broken reporter must never mask the real failure.\n }\n return error\n}\n\n/**\n * `fetch` for platform API calls, with a content-type assertion that makes an HTML body\n * LOUD instead of swallowed. Returns the untouched `Response` (body unread) on success.\n *\n * @throws {PlatformFetchError} when the response carried a non-JSON body. Never for a\n * non-2xx JSON response — see the contract table at the top of this file.\n */\nexport async function platformFetch(\n input: string | URL | Request,\n init?: RequestInit,\n options?: PlatformFetchOptions,\n): Promise<Response> {\n const response = await fetch(input as RequestInfo, init)\n\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : ((input as Request).url ?? String(input))\n const method = String(init?.method ?? (input as Request)?.method ?? 'GET').toUpperCase()\n\n // Nothing to assert: no body is defined for these, and a HEAD never carries one.\n if (BODYLESS_STATUSES.has(response.status) || method === 'HEAD') return response\n\n const contentType = response.headers?.get?.('content-type') ?? ''\n\n // FAST PATH — the overwhelming majority. Never touches the body, so the caller's\n // `response.json()` / streaming behaviour is completely unaffected.\n if (isJsonContentType(contentType)) return response\n\n // Slow path only: we have to look at the body to tell \"HTML shell\" (loud) from\n // \"empty body\" / \"JSON without a content-type header\" (both tolerated).\n const body = await peekBody(response)\n if (body === null) {\n // Body not inspectable (no clone(), already consumed). Judge on the header alone:\n // an explicit non-JSON content-type is still the misroute class; a MISSING header\n // with no way to sniff is given the benefit of the doubt rather than invented.\n if (!contentType) return response\n throw raise(\n {\n kind: 'non-json-body',\n url,\n method,\n status: response.status,\n contentType,\n bodyClass: classifyBodyClass(contentType, ''),\n bodyPrefix: '',\n },\n options,\n )\n }\n\n const bodyClass = classifyBodyClass(contentType, body)\n // `json` covers a `{`/`[` body from an origin that forgot the header; `empty` covers a\n // 200 with no body, which is not the swallow class and whose handling belongs to the\n // caller (it would already fail its own `.json()` loudly).\n if (bodyClass === 'json' || bodyClass === 'empty') return response\n\n throw raise(\n {\n kind: 'non-json-body',\n url,\n method,\n status: response.status,\n contentType,\n bodyClass,\n bodyPrefix: collapse(body),\n },\n options,\n )\n}\n\n/**\n * Parse a `Response` body as JSON, turning a parse failure into the same named error\n * rather than a bare `SyntaxError: Unexpected token '<'`. Use after {@link platformFetch}\n * when you want the parse step named too.\n *\n * @throws {PlatformFetchError} `kind: 'unparseable-json'`\n */\nexport async function readPlatformJson<T>(\n response: Response,\n options?: PlatformFetchOptions,\n): Promise<T> {\n const contentType = response.headers?.get?.('content-type') ?? ''\n // Clone BEFORE parsing: once `.json()` has consumed the body, `clone()` throws and the\n // offending bytes are gone — which is exactly the evidence the error needs to quote.\n let spare: Response | null = null\n try {\n spare = typeof (response as { clone?: unknown }).clone === 'function' ? response.clone() : null\n } catch {\n spare = null\n }\n try {\n return (await response.json()) as T\n } catch {\n const body = spare ? await peekBody(spare) : null\n throw raise(\n {\n kind: 'unparseable-json',\n url: response.url ?? '',\n method: 'GET',\n status: response.status,\n contentType,\n // Classify from the BODY alone: reaching here means the content-type header\n // claimed JSON and was wrong, so it has no vote. A `text/html` shell served\n // under an `application/json` header is still the C-261 misroute class.\n bodyClass: classifyBodyClass(null, body),\n bodyPrefix: body ? collapse(body) : '',\n },\n options,\n )\n }\n}\n","/**\n * Pure functions for resolving responsive image variants from DCS CDN URLs.\n *\n * The DCS imaging pipeline generates WebP variants during upload:\n * - thumb (150px) — grid thumbnails\n * - sm (640px) — mobile displays\n * - md (1024px) — tablet / blog body\n * - lg (1920px) — desktop / hero sections\n * - og (1200×630) — social sharing (fixed aspect)\n *\n * This module provides framework-agnostic resolution logic that\n * framework packages (@duffcloudservices/cms, cms-react, etc.) wrap\n * in their respective APIs.\n *\n * @module responsive-image\n */\n\n/** Sizing context that controls which variants are included in the srcset. */\nexport type ImageContext = 'hero' | 'card' | 'thumbnail' | 'content' | 'og' | 'lightbox'\n\n/** Options for resolving a responsive image. */\nexport interface ResponsiveImageOptions {\n /** Source URL — original CDN URL or local path. */\n src: string\n /** Alt text for accessibility. */\n alt: string\n /** Sizing context — determines which variants to include. */\n context?: ImageContext\n /** Optional explicit width hints for the browser `sizes` attribute. */\n sizes?: string\n /** Skip variant resolution and return the original URL only. */\n original?: boolean\n /**\n * Intrinsic pixel width of the source image. Serves TWO purposes:\n *\n * 1. **Layout-shift hint.** When BOTH `width` and `height` are known and\n * positive, the resolver emits them as `<img width>`/`<img height>`\n * attributes so the browser reserves layout space and Cumulative Layout\n * Shift (CLS) is eliminated. These are aspect-ratio hints only — CSS\n * (`width:100%; height:auto`) still controls the rendered size. Never\n * fabricate these from a variant's target width: a `height` of 0 means\n * \"unknown\" and MUST be omitted rather than guessed (see the CDN\n * image-map, where older entries carry `height:0`).\n * 2. **Source-fit variant selection (Q-561=A / board C-804).** When `width`\n * alone is known and positive, variants whose target width EXCEEDS it are\n * omitted from the srcset — see {@link selectSourceFitVariants}. Supplying\n * `width` is therefore what makes a narrow-source image stop advertising\n * widths it cannot deliver. Omitting it preserves the pre-C-804 behaviour\n * byte-for-byte.\n */\n width?: number\n /** Intrinsic pixel height of the source image. See `width`. */\n height?: number\n}\n\n/** A single `<source>` entry for a `<picture>` element. */\nexport interface ResponsiveSource {\n srcset: string\n type: string\n sizes?: string\n}\n\n/** Resolved image properties for rendering. */\nexport interface ResponsiveImageResult {\n /** Props spreadable onto an `<img>` element. */\n imgProps: {\n src: string\n alt: string\n loading: 'lazy' | 'eager'\n decoding: 'async' | 'auto'\n /**\n * Intrinsic width — emitted only when both dimensions are known and positive\n * (layout-shift hint). Absent when dimensions are unknown; never fabricated.\n */\n width?: number\n /** Intrinsic height — see `width`. */\n height?: number\n /**\n * `high` for the hero/LCP context so the browser prioritises the fetch.\n * Absent for every other context.\n */\n fetchpriority?: 'high'\n }\n /** WebP `<source>` entries for a `<picture>` element. */\n sources: ResponsiveSource[]\n /** Whether responsive variants were detected. */\n hasVariants: boolean\n}\n\n/**\n * Build the layout-shift + priority hint fragment shared by both the\n * fallback and variant return paths. Dimensions are emitted ONLY when both\n * width and height are known and strictly positive — a missing or zero value\n * means \"unknown\" and is omitted rather than guessed, so we never fabricate a\n * wrong aspect ratio.\n */\nfunction dimensionHints(\n context: ImageContext,\n width?: number,\n height?: number,\n): { width?: number; height?: number; fetchpriority?: 'high' } {\n const hints: { width?: number; height?: number; fetchpriority?: 'high' } = {}\n if (typeof width === 'number' && width > 0 && typeof height === 'number' && height > 0) {\n hints.width = width\n hints.height = height\n }\n if (context === 'hero') {\n hints.fetchpriority = 'high'\n }\n return hints\n}\n\n/**\n * Regex that matches DCS CDN asset URLs and captures the base path, UUID, and extension.\n *\n * Supports both URL formats:\n * - `https://files.duffcloudservices.com/{slug}/assets/{uuid}.ext` (branding container)\n * - `https://files.duffcloudservices.com/content/{slug}/assets/{uuid}.ext` (content container)\n *\n * Group 1: base path including trailing slash (e.g. `https://files.duffcloudservices.com/content/kept/assets/`)\n * Group 2: UUID (e.g. `abc-123`)\n * Group 3: file extension (e.g. `jpg`)\n */\nconst CDN_PATTERN = /^(https?:\\/\\/files\\.[^/]+\\/(?:content\\/)?[^/]+\\/assets\\/(?:[^/]+\\/)*)([a-f0-9-]+)\\.(\\w+)$/\n\n/** Variant definitions mapping suffix → pixel width. */\nconst VARIANT_MAP: Record<string, { suffix: string; width: number }> = {\n thumb: { suffix: '-thumb', width: 150 },\n sm: { suffix: '-sm', width: 640 },\n md: { suffix: '-md', width: 1024 },\n lg: { suffix: '-lg', width: 1920 },\n}\n\n/** Which variants each context includes (smallest → largest). */\nconst CONTEXT_VARIANTS: Record<ImageContext, string[]> = {\n hero: ['sm', 'md', 'lg'],\n card: ['thumb', 'sm', 'md'],\n thumbnail: ['thumb', 'sm'],\n content: ['sm', 'md', 'lg'],\n og: [], // OG uses the dedicated -og variant, not srcset\n lightbox: ['md', 'lg'], // Full-screen lightbox — only large variants\n}\n\n/**\n * Source-fit variant selection — board C-804, answering Q-561=A (2026-08-19).\n *\n * The DCS upload pipeline (`internal/imaging`, `ExpectedVariants`) never\n * upscales: a fit variant whose target box already contains the source is not\n * generated at all. This module used to build its srcset from the FULL variant\n * set for the context regardless, so a narrow source advertised widths that\n * either 404'd or — once someone hand-patched the missing file in with\n * `cli/image-variant-fix` — resolved to a capped copy carrying no extra detail.\n * That is the measured iron-oak case: 1024px-wide banner sources served an\n * `-lg` advertised at `1920w` whose real resolution is 1024px, so a wide\n * viewport paid ~22.7 KB for what `-md` already delivered in ~21.75 KB.\n *\n * The rule (Q-561 option A, \"OMIT the variant when the source is narrower than\n * its target\"):\n *\n * - `sourceWidth` unknown / non-positive → **no filtering at all**. Callers\n * that cannot supply an intrinsic width keep the pre-C-804 output\n * byte-for-byte; the change is strictly opt-in on dimension knowledge.\n * - `-thumb` is **never** omitted. The pipeline generates the thumbnail\n * unconditionally (picker-grid performance), so it always exists.\n * - Any other variant is emitted iff `targetWidth <= sourceWidth`.\n *\n * **Equality keeps the variant.** Q-561=A says \"narrower than\", not \"not wider\n * than\", so a 1024px source keeps its `1024w` entry as the largest honest step\n * rather than dropping to `640w`. Documented residual: the generator's skip\n * test is `srcW <= targetW && srcH <= targetH`, so for a source that is exactly\n * the target width AND shorter than the target height (iron-oak's 1024x212)\n * the generated file may be absent even though we keep the descriptor. That is\n * the one remaining miss class — strictly narrower than before this change,\n * never wider — and the `onerror` self-heal in `ManagedImage`/`ResponsiveImage`\n * already covers it. Aligning `imaging.ExpectedVariants` onto a width-only skip\n * test would close it entirely; that rides the server, not the cms release.\n *\n * The caller uses the returned list for BOTH the file references and their `w`\n * descriptors, so an omitted variant can never leave a dangling descriptor.\n *\n * @param keys Context variant keys, ordered smallest → largest.\n * @param sourceWidth Intrinsic pixel width of the ORIGINAL uploaded image.\n * @returns The subset that the pipeline can honestly deliver.\n */\nexport function selectSourceFitVariants(keys: string[], sourceWidth?: number): string[] {\n if (typeof sourceWidth !== 'number' || !Number.isFinite(sourceWidth) || sourceWidth <= 0) {\n return keys\n }\n return keys.filter((key) => {\n // The thumbnail is always generated, whatever the source size.\n if (key === 'thumb') return true\n const v = VARIANT_MAP[key]\n if (!v) return false\n return v.width <= sourceWidth\n })\n}\n\n/** Default `sizes` attribute per context. */\nconst DEFAULT_SIZES: Record<ImageContext, string> = {\n hero: '100vw',\n card: '(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw',\n thumbnail: '150px',\n content: '(max-width: 1024px) 100vw, 1024px',\n og: '',\n lightbox: '100vw',\n}\n\n/**\n * Resolves responsive image metadata from a DCS CDN URL.\n *\n * If the URL matches the CDN asset pattern, a srcset of WebP variants\n * is generated based on the sizing `context`. Non-CDN URLs are returned\n * as-is with no variants.\n *\n * @example\n * ```ts\n * const result = resolveResponsiveImage({\n * src: 'https://files.duffcloudservices.com/kept/assets/hero/abc-123.jpg',\n * alt: 'Hero image',\n * context: 'hero',\n * })\n * // result.hasVariants === true\n * // result.sources[0].srcset contains sm, md, lg WebP sizes\n * // result.imgProps.loading === 'eager' (hero context)\n * ```\n */\nexport function resolveResponsiveImage(options: ResponsiveImageOptions): ResponsiveImageResult {\n const { src, alt, context = 'content', sizes, original, width, height } = options\n\n const hints = dimensionHints(context, width, height)\n\n const fallback: ResponsiveImageResult = {\n imgProps: {\n src,\n alt,\n loading: context === 'hero' ? 'eager' : 'lazy',\n decoding: context === 'hero' ? 'auto' : 'async',\n ...hints,\n },\n sources: [],\n hasVariants: false,\n }\n\n if (original) {\n return fallback\n }\n\n const match = src.match(CDN_PATTERN)\n if (!match) {\n return fallback\n }\n\n const [, basePath, uuid] = match\n\n const contextVariants = CONTEXT_VARIANTS[context] ?? CONTEXT_VARIANTS.content\n if (contextVariants.length === 0) {\n return fallback\n }\n\n // C-804: drop variants the pipeline cannot honestly deliver for this source.\n // No `width` supplied → `variants === contextVariants` and everything below\n // is byte-identical to the pre-C-804 output.\n const variants = selectSourceFitVariants(contextVariants, width)\n if (variants.length === 0) {\n // Every configured step is wider than the source. There is no honest\n // variant to advertise, so serve the original rather than a srcset of\n // references the pipeline never produced.\n return fallback\n }\n\n const srcsetParts = variants.map((key) => {\n const v = VARIANT_MAP[key]\n return `${basePath}${uuid}${v.suffix}.webp ${v.width}w`\n })\n\n const resolvedSizes = sizes ?? DEFAULT_SIZES[context] ?? DEFAULT_SIZES.content\n\n // `<img src>` fallback. Historically always `-md` (`-lg` for lightbox, for\n // max quality). When the source-fit filter removed that exact variant from\n // THIS context, fall back to the largest variant that survived — otherwise\n // the `<img>` would point at a file the pipeline never generated, which is\n // the very defect C-804 closes. When the filter removed nothing (or ran on a\n // context that never carried the preferred key, e.g. `thumbnail`), the\n // historical choice is preserved verbatim.\n const preferredKey = context === 'lightbox' ? 'lg' : 'md'\n const preferredWasDropped =\n contextVariants.includes(preferredKey) && !variants.includes(preferredKey)\n const largestEmitted = variants.reduce((a, b) =>\n VARIANT_MAP[b].width > VARIANT_MAP[a].width ? b : a,\n )\n const fallbackSuffix = preferredWasDropped\n ? VARIANT_MAP[largestEmitted].suffix\n : VARIANT_MAP[preferredKey].suffix\n\n return {\n imgProps: {\n src: `${basePath}${uuid}${fallbackSuffix}.webp`,\n alt,\n loading: context === 'hero' ? 'eager' : 'lazy',\n decoding: context === 'hero' ? 'auto' : 'async',\n ...hints,\n },\n sources: [\n {\n srcset: srcsetParts.join(', '),\n type: 'image/webp',\n ...(resolvedSizes ? { sizes: resolvedSizes } : {}),\n },\n ],\n hasVariants: true,\n }\n}\n\n/**\n * Tests whether a URL matches the DCS CDN asset pattern.\n *\n * Useful when rendering a mix of CDN-hosted and local images\n * to decide whether responsive treatment is applicable.\n */\nexport function isCdnAssetUrl(url: string): boolean {\n return CDN_PATTERN.test(url)\n}\n","import { platformFetch, readPlatformJson } from './platform-fetch'\n\nexport type DcsPasskeyOptions = {\n apiBaseUrl?: string\n returnTo?: string\n immediate?: boolean\n}\n\nexport type DcsPasskeyResult = {\n success: boolean\n redirectTo?: string\n visitor?: {\n email: string\n name: string\n picture?: string\n }\n}\n\ntype CeremonyOptions = {\n state: string\n publicKey: Record<string, unknown>\n}\n\ntype CredentialRequestWithImmediateUI = CredentialRequestOptions & {\n uiMode?: 'immediate'\n}\n\ntype ClientCapabilities = {\n immediateGet?: boolean\n}\n\ntype PublicKeyCredentialConstructorWithCapabilities = typeof PublicKeyCredential & {\n getClientCapabilities?: () => Promise<ClientCapabilities>\n}\n\nconst getPublicKeyCredentialConstructor = (): PublicKeyCredentialConstructorWithCapabilities | undefined => {\n if (typeof window === 'undefined' || typeof window.PublicKeyCredential === 'undefined') {\n return undefined\n }\n return window.PublicKeyCredential as PublicKeyCredentialConstructorWithCapabilities\n}\n\nconst apiBase = (value?: string) => (value ?? '/api/v1').replace(/\\/$/u, '')\n\nconst base64UrlToBuffer = (value: string): ArrayBuffer => {\n const base64 = value.replace(/-/g, '+').replace(/_/g, '/')\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=')\n const raw = window.atob(padded)\n const output = new Uint8Array(raw.length)\n for (let index = 0; index < raw.length; index += 1) {\n output[index] = raw.charCodeAt(index)\n }\n return output.buffer\n}\n\nconst bufferToBase64Url = (value: ArrayBuffer | null): string | null => {\n if (!value) {\n return null\n }\n const bytes = new Uint8Array(value)\n let binary = ''\n bytes.forEach(byte => {\n binary += String.fromCharCode(byte)\n })\n return window.btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/u, '')\n}\n\nconst decodeDescriptors = (items: unknown): PublicKeyCredentialDescriptor[] | undefined => {\n if (!Array.isArray(items)) {\n return undefined\n }\n return items.map(item => {\n const descriptor = item as { id?: unknown; type?: PublicKeyCredentialType; transports?: AuthenticatorTransport[] }\n return {\n ...descriptor,\n id: typeof descriptor.id === 'string' ? base64UrlToBuffer(descriptor.id) : descriptor.id,\n type: descriptor.type ?? 'public-key',\n } as PublicKeyCredentialDescriptor\n })\n}\n\nconst decodeCreationOptions = (publicKey: Record<string, unknown>): PublicKeyCredentialCreationOptions => {\n const user = publicKey.user as { id?: unknown } | undefined\n return {\n ...publicKey,\n challenge: base64UrlToBuffer(String(publicKey.challenge)),\n user: {\n ...(user ?? {}),\n id: typeof user?.id === 'string' ? base64UrlToBuffer(user.id) : user?.id,\n } as PublicKeyCredentialUserEntity,\n excludeCredentials: decodeDescriptors(publicKey.excludeCredentials),\n } as PublicKeyCredentialCreationOptions\n}\n\nconst decodeRequestOptions = (publicKey: Record<string, unknown>): PublicKeyCredentialRequestOptions => ({\n ...publicKey,\n challenge: base64UrlToBuffer(String(publicKey.challenge)),\n allowCredentials: decodeDescriptors(publicKey.allowCredentials),\n}) as PublicKeyCredentialRequestOptions\n\nconst serializeCredential = (credential: PublicKeyCredential): Record<string, unknown> => {\n const response = credential.response\n const base = {\n id: credential.id,\n type: credential.type,\n rawId: bufferToBase64Url(credential.rawId),\n authenticatorAttachment: credential.authenticatorAttachment,\n clientExtensionResults: credential.getClientExtensionResults(),\n }\n\n if (response instanceof AuthenticatorAttestationResponse) {\n return {\n ...base,\n response: {\n clientDataJSON: bufferToBase64Url(response.clientDataJSON),\n attestationObject: bufferToBase64Url(response.attestationObject),\n transports: response.getTransports?.() ?? [],\n },\n }\n }\n\n const assertion = response as AuthenticatorAssertionResponse\n return {\n ...base,\n response: {\n clientDataJSON: bufferToBase64Url(assertion.clientDataJSON),\n authenticatorData: bufferToBase64Url(assertion.authenticatorData),\n signature: bufferToBase64Url(assertion.signature),\n userHandle: bufferToBase64Url(assertion.userHandle),\n },\n }\n}\n\n// C-298 layer 2 adoption. `apiBase()` defaults to the RELATIVE `/api/v1`, so on a host\n// whose Front Door config has no `/api/v1/*` route these ceremonies get the SPA shell at\n// HTTP 200 — `response.ok` is true and `response.json()` throws a bare\n// \"Unexpected token '<'\". Passkey login was one of the nine dead KEPT site-auth routes.\n// platformFetch names the URL, the content-type and the body instead; a genuine non-2xx\n// JSON error still lands in the existing `!response.ok` branch untouched.\nconst postJSON = async <T>(url: string, body?: unknown): Promise<T> => {\n const response = await platformFetch(url, {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n })\n if (!response.ok) {\n throw new Error(`Passkey request failed (${response.status})`)\n }\n return await readPlatformJson<T>(response)\n}\n\nexport const isDcsPasskeySupported = (): boolean =>\n getPublicKeyCredentialConstructor() !== undefined &&\n typeof navigator !== 'undefined' &&\n typeof navigator.credentials?.get === 'function' &&\n typeof navigator.credentials?.create === 'function'\n\nexport const isDcsPasskeyImmediateUIAvailable = async (): Promise<boolean> => {\n const publicKeyCredential = getPublicKeyCredentialConstructor()\n if (!isDcsPasskeySupported() || typeof publicKeyCredential?.getClientCapabilities !== 'function') {\n return false\n }\n try {\n const capabilities = await publicKeyCredential.getClientCapabilities()\n return capabilities.immediateGet === true\n } catch {\n return false\n }\n}\n\nexport const registerDcsSitePasskey = async (options?: DcsPasskeyOptions): Promise<string | null> => {\n if (!isDcsPasskeySupported()) {\n throw new Error('Passkeys are not available in this browser.')\n }\n const base = apiBase(options?.apiBaseUrl)\n const ceremony = await postJSON<CeremonyOptions>(`${base}/site-auth/passkeys/register/options`)\n const credential = await navigator.credentials.create({\n publicKey: decodeCreationOptions(ceremony.publicKey),\n })\n if (!(credential instanceof PublicKeyCredential)) {\n throw new Error('Passkey registration was cancelled.')\n }\n const result = await postJSON<{ success: boolean; credentialId?: string }>(\n `${base}/site-auth/passkeys/register/verify`,\n {\n state: ceremony.state,\n credential: serializeCredential(credential),\n },\n )\n if (!result.success) {\n throw new Error('Passkey registration failed.')\n }\n return result.credentialId ?? null\n}\n\nexport const authenticateDcsSitePasskey = async (options?: DcsPasskeyOptions): Promise<DcsPasskeyResult> => {\n if (!isDcsPasskeySupported()) {\n throw new Error('Passkeys are not available in this browser.')\n }\n const base = apiBase(options?.apiBaseUrl)\n const ceremony = await postJSON<CeremonyOptions>(`${base}/site-auth/passkeys/authenticate/options`)\n const request: CredentialRequestWithImmediateUI = {\n publicKey: decodeRequestOptions(ceremony.publicKey),\n }\n if (options?.immediate) {\n request.uiMode = 'immediate'\n }\n const credential = await navigator.credentials.get(request)\n if (!(credential instanceof PublicKeyCredential)) {\n throw new Error('Passkey sign-in was cancelled.')\n }\n return await postJSON<DcsPasskeyResult>(`${base}/site-auth/passkeys/authenticate/verify`, {\n state: ceremony.state,\n credential: serializeCredential(credential),\n returnTo: options?.returnTo ?? '/',\n })\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/content.ts","../src/seo.ts","../src/loaders.ts","../src/fetch.ts","../src/platform-fetch.ts","../src/responsive-image.ts","../src/passkeys.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Content resolution utilities\n */\n\nimport type { ContentConfiguration } from './types'\n\n/**\n * Resolve a text key for a specific page.\n * Checks page-specific content first, then falls back to global content.\n *\n * @param content - The content configuration\n * @param page - The page slug\n * @param key - The text key to resolve\n * @returns The resolved text value, or undefined if not found\n */\nexport function resolveTextKey(\n content: ContentConfiguration,\n page: string,\n key: string\n): string | undefined {\n // Check page-specific content first\n const pageContent = content.pages?.[page]\n if (pageContent && key in pageContent) {\n return pageContent[key]\n }\n\n // Fall back to global content\n if (content.global && key in content.global) {\n return content.global[key]\n }\n\n return undefined\n}\n\n/**\n * Get all content for a specific page, merging global and page-specific.\n *\n * @param content - The content configuration\n * @param page - The page slug\n * @returns Merged content object (global values overridden by page values)\n */\nexport function getPageContent(\n content: ContentConfiguration,\n page: string\n): Record<string, string> {\n const global = content.global ?? {}\n const pageContent = content.pages?.[page] ?? {}\n return { ...global, ...pageContent }\n}\n\n/**\n * Get only the global content.\n *\n * @param content - The content configuration\n * @returns Global content object\n */\nexport function getGlobalContent(\n content: ContentConfiguration\n): Record<string, string> {\n return content.global ?? {}\n}\n","/**\n * SEO resolution utilities\n */\n\nimport type {\n SeoConfiguration,\n ResolvedSeo,\n SeoOpenGraphConfig,\n SeoTwitterConfig,\n} from './types'\n\n/**\n * Resolve SEO configuration for a specific page.\n * Merges global defaults with page-specific overrides.\n *\n * @param seo - The SEO configuration\n * @param page - The page slug\n * @returns Resolved SEO object with all values filled in\n */\nexport function resolveSeoForPage(\n seo: SeoConfiguration,\n page: string\n): ResolvedSeo {\n const global = seo.global ?? {}\n const pageSeo = seo.pages?.[page] ?? {}\n\n // Resolve title with template\n let title = pageSeo.title ?? global.defaultTitle ?? ''\n if (title && global.titleTemplate && !pageSeo.noTitleTemplate) {\n title = global.titleTemplate.replace('%s', title)\n }\n\n const description = pageSeo.description ?? global.defaultDescription ?? ''\n\n // Resolve Open Graph\n const ogImage =\n pageSeo.openGraph?.image ?? global.images?.ogDefault ?? undefined\n const openGraph = resolveOpenGraph(pageSeo.openGraph, ogImage, title, description)\n\n // Resolve Twitter Card\n const twitterImage =\n pageSeo.twitter?.image ?? global.images?.twitterDefault ?? ogImage\n const twitter = resolveTwitterCard(\n pageSeo.twitter,\n twitterImage,\n title,\n description,\n global.social?.twitter\n )\n\n return {\n title,\n description,\n image: ogImage,\n siteName: global.siteName,\n siteUrl: global.siteUrl,\n locale: global.locale,\n canonical: pageSeo.canonical,\n robots: pageSeo.robots ?? global.robots,\n noIndex: pageSeo.robots?.includes('noindex'),\n openGraph,\n twitter,\n schemas: pageSeo.schemas ?? global.schemas,\n alternates: pageSeo.alternates,\n }\n}\n\n/**\n * Resolve Open Graph configuration\n */\nfunction resolveOpenGraph(\n pageOg: SeoOpenGraphConfig | undefined,\n ogImage: string | undefined,\n title: string,\n description: string\n): SeoOpenGraphConfig | undefined {\n if (pageOg) {\n return {\n ...pageOg,\n title: pageOg.title ?? title,\n description: pageOg.description ?? description,\n image: ogImage,\n }\n }\n\n if (ogImage) {\n return {\n title,\n description,\n image: ogImage,\n type: 'website',\n }\n }\n\n return undefined\n}\n\n/**\n * Resolve Twitter Card configuration\n */\nfunction resolveTwitterCard(\n pageTwitter: SeoTwitterConfig | undefined,\n twitterImage: string | undefined,\n title: string,\n description: string,\n globalTwitterHandle: string | undefined\n): SeoTwitterConfig | undefined {\n if (pageTwitter) {\n return {\n card: pageTwitter.card ?? 'summary_large_image',\n site: pageTwitter.site ?? globalTwitterHandle,\n ...pageTwitter,\n title: pageTwitter.title ?? title,\n description: pageTwitter.description ?? description,\n image: twitterImage,\n }\n }\n\n if (globalTwitterHandle || twitterImage) {\n return {\n card: 'summary_large_image',\n site: globalTwitterHandle,\n title,\n description,\n image: twitterImage,\n }\n }\n\n return undefined\n}\n\n/**\n * Meta tag representation for framework-agnostic usage\n */\nexport interface MetaTag {\n name?: string\n property?: string\n content: string\n}\n\n/**\n * Helper to add a meta tag if content exists\n */\nfunction addTag(\n tags: MetaTag[],\n content: string | undefined,\n attr: { name?: string; property?: string }\n): void {\n if (content) {\n tags.push({ ...attr, content })\n }\n}\n\n/**\n * Build Open Graph meta tags\n */\nfunction buildOpenGraphTags(\n tags: MetaTag[],\n seo: ResolvedSeo\n): void {\n const og = seo.openGraph\n if (!og) return\n\n addTag(tags, og.title, { property: 'og:title' })\n addTag(tags, og.description, { property: 'og:description' })\n addTag(tags, og.image, { property: 'og:image' })\n addTag(tags, og.imageAlt, { property: 'og:image:alt' })\n addTag(tags, og.imageWidth?.toString(), { property: 'og:image:width' })\n addTag(tags, og.imageHeight?.toString(), { property: 'og:image:height' })\n addTag(tags, og.type, { property: 'og:type' })\n addTag(tags, og.url, { property: 'og:url' })\n addTag(tags, seo.siteName, { property: 'og:site_name' })\n addTag(tags, seo.locale, { property: 'og:locale' })\n}\n\n/**\n * Build Twitter Card meta tags\n */\nfunction buildTwitterTags(\n tags: MetaTag[],\n seo: ResolvedSeo\n): void {\n const tw = seo.twitter\n if (!tw) return\n\n addTag(tags, tw.card, { name: 'twitter:card' })\n addTag(tags, tw.site ? `@${tw.site}` : undefined, { name: 'twitter:site' })\n addTag(tags, tw.creator ? `@${tw.creator}` : undefined, { name: 'twitter:creator' })\n addTag(tags, tw.title, { name: 'twitter:title' })\n addTag(tags, tw.description, { name: 'twitter:description' })\n addTag(tags, tw.image, { name: 'twitter:image' })\n addTag(tags, tw.imageAlt, { name: 'twitter:image:alt' })\n}\n\n/**\n * Build an array of meta tags from resolved SEO.\n * Useful for frameworks that need to manually set meta tags.\n *\n * @param seo - Resolved SEO object\n * @returns Array of meta tag objects\n */\nexport function buildMetaTags(seo: ResolvedSeo): MetaTag[] {\n const tags: MetaTag[] = []\n\n // Basic meta\n addTag(tags, seo.description, { name: 'description' })\n addTag(tags, seo.robots, { name: 'robots' })\n\n // Open Graph\n buildOpenGraphTags(tags, seo)\n\n // Twitter Card\n buildTwitterTags(tags, seo)\n\n return tags\n}\n","/**\n * YAML file loaders for DCS configuration files\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\nimport type {\n ContentConfiguration,\n SeoConfiguration,\n PagesConfiguration,\n UnifiedConfiguration,\n} from './types'\n\n/**\n * Load and parse .dcs/content.yaml\n *\n * @param filePath - Path to content.yaml (absolute or relative to cwd)\n * @returns Parsed content configuration\n * @throws Error if file not found or parse fails\n */\nexport async function loadContentYaml(\n filePath: string\n): Promise<ContentConfiguration> {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath)\n\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\n const content = yaml.load(fileContent) as ContentConfiguration\n\n // Ensure required fields exist\n if (!content.version) {\n content.version = 1\n }\n if (!content.global) {\n content.global = {}\n }\n if (!content.pages) {\n content.pages = {}\n }\n\n return content\n}\n\n/**\n * Load and parse .dcs/seo.yaml\n *\n * @param filePath - Path to seo.yaml (absolute or relative to cwd)\n * @returns Parsed SEO configuration\n * @throws Error if file not found or parse fails\n */\nexport async function loadSeoYaml(filePath: string): Promise<SeoConfiguration> {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath)\n\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\n const seo = yaml.load(fileContent) as SeoConfiguration\n\n // Ensure required fields exist\n if (!seo.version) {\n seo.version = 1\n }\n\n return seo\n}\n\n/**\n * Load and parse .dcs/pages.yaml\n *\n * @param filePath - Path to pages.yaml (absolute or relative to cwd)\n * @returns Parsed pages configuration\n * @throws Error if file not found or parse fails\n */\nexport async function loadPagesYaml(\n filePath: string\n): Promise<PagesConfiguration> {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(process.cwd(), filePath)\n\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\n const pages = yaml.load(fileContent) as PagesConfiguration\n\n // Ensure required fields exist\n if (!pages.version) {\n pages.version = 3\n }\n if (!pages.pages) {\n pages.pages = []\n }\n\n return pages\n}\n\n/**\n * Try to find a DCS config file in common locations\n *\n * @param filename - The config file name (e.g., 'content.yaml')\n * @param projectRoot - The project root directory\n * @returns The found path, or undefined if not found\n */\nexport function findDcsConfigFile(\n filename: string,\n projectRoot: string\n): string | undefined {\n const possiblePaths = [\n path.resolve(projectRoot, '.dcs', filename),\n path.resolve(projectRoot, '..', '.dcs', filename), // For VitePress docs folder\n path.resolve(process.cwd(), '.dcs', filename),\n ]\n\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n return testPath\n }\n }\n\n return undefined\n}\n\n// ─── Unified Config Loader ──────────────────────────────────────\n\n/**\n * Result of loading DCS configuration, whether from unified config.yaml\n * or individual split files.\n */\nexport interface DcsConfigResult {\n /** Resolved content configuration */\n content: ContentConfiguration\n /** Resolved SEO configuration */\n seo: SeoConfiguration\n /** Resolved pages configuration */\n pages: PagesConfiguration\n /** Whether the configuration was loaded from a unified config.yaml */\n isUnified: boolean\n /** The source file path(s) that were loaded */\n sources: string[]\n}\n\n/**\n * Load DCS configuration from either a unified `.dcs/config.yaml` (version 2)\n * or fall back to individual split files (content.yaml, seo.yaml, pages.yaml).\n *\n * The unified format is preferred when `.dcs/config.yaml` exists and has `version: 2`.\n * This provides a single source of truth for all site configuration.\n *\n * @param projectRoot - The project root directory containing `.dcs/`\n * @returns Resolved configuration from all sources\n *\n * @example\n * ```typescript\n * const config = await loadConfigYaml('/path/to/site')\n * console.log(config.content.pages.home)\n * console.log(config.seo.global?.siteName)\n * console.log(config.pages.pages)\n * console.log(config.isUnified) // true if loaded from config.yaml\n * ```\n */\nexport async function loadConfigYaml(projectRoot: string): Promise<DcsConfigResult> {\n // Try unified config.yaml first\n const unifiedPath = findDcsConfigFile('config.yaml', projectRoot)\n if (unifiedPath) {\n try {\n const fileContent = fs.readFileSync(unifiedPath, 'utf8')\n const raw = yaml.load(fileContent) as Record<string, unknown>\n\n // Only treat as unified if version === 2\n if (raw && raw.version === 2) {\n const unified = raw as unknown as UnifiedConfiguration\n return {\n content: unified.content ?? { version: 1, global: {}, pages: {} },\n seo: unified.seo ?? { version: 1 },\n pages: {\n version: 3,\n siteSlug: unified.siteSlug ?? unified.site?.slug ?? '',\n pages: unified.pages ?? [],\n },\n isUnified: true,\n sources: [unifiedPath],\n }\n }\n } catch {\n // config.yaml exists but failed to parse — fall through to split files\n }\n }\n\n // Fall back to individual split files\n const sources: string[] = []\n\n let content: ContentConfiguration = { version: 1, global: {}, pages: {} }\n const contentPath = findDcsConfigFile('content.yaml', projectRoot)\n if (contentPath) {\n content = await loadContentYaml(contentPath)\n sources.push(contentPath)\n }\n\n let seo: SeoConfiguration = { version: 1 }\n const seoPath = findDcsConfigFile('seo.yaml', projectRoot)\n if (seoPath) {\n seo = await loadSeoYaml(seoPath)\n sources.push(seoPath)\n }\n\n let pages: PagesConfiguration = { version: 3, siteSlug: '', pages: [] }\n const pagesPath = findDcsConfigFile('pages.yaml', projectRoot)\n if (pagesPath) {\n pages = await loadPagesYaml(pagesPath)\n sources.push(pagesPath)\n }\n\n return {\n content,\n seo,\n pages,\n isUnified: false,\n sources,\n }\n}\n","/**\n * Runtime content fetching for premium tier customers.\n *\n * ⚠️ BOTH HELPERS ARE INERT. See the Q-297 block below before touching them.\n */\n\nimport type { ContentConfiguration, SeoConfiguration } from './types'\n\n/**\n * Options for runtime fetch operations\n */\nexport interface FetchOptions {\n /** Base URL for the DCS API */\n apiBaseUrl?: string\n /** Timeout in milliseconds (default: 5000) */\n timeout?: number\n /** Custom headers to include */\n headers?: Record<string, string>\n}\n\nconst DEFAULT_API_URL = 'https://api.duffcloudservices.com'\nconst DEFAULT_TIMEOUT = 5000\n\n/*\n * ─── Q-297 · the runtime hydration endpoints never existed ────────────────────\n *\n * These two helpers were written against `GET /api/v1/content/runtime` and\n * `GET /api/v1/seo/runtime`. NO DCS server mode has EVER served either path.\n * A `-S` pickaxe over every `*.go` file on every branch returns zero commits for\n * either string; the identical pickaxe for `agent/config` returns seven, so the\n * instrument works. The client drifted ahead of a contract that was designed and\n * never implemented — it was born broken in 6812eed2d (2026-01-07), which called\n * an equally phantom `/portal/sites/{slug}/content/runtime`, and ddfcff9a2\n * (2026-05-30) merely re-pointed one phantom path at another while making the\n * fleet-wide slug-free correction.\n *\n * The cost was a silent-failure class: a 404 fell through `!response.ok` and\n * returned `null` with no warning, so runtime content and SEO hydration died\n * quietly on customer sites while the calls piled up in the 404 numerator\n * (234 in 7 days on api.duffcloudservices.com alone, UA `node`).\n *\n * Q-297 (answered 2026-07-12) put the choice to the owner:\n * A. Delete the phantom callers in the next cms-core major. ← CHOSEN\n * B. Implement the endpoints server-side if premium runtime\n * hydration is on the revenue roadmap. rejected\n *\n * Option B is off the table, so there is no endpoint to re-point at: the real\n * content API is per-resource (`/api/v1/content/pages`, `/api/v1/content/pages/\n * {pageSlug}`, `/api/v1/pages/{pageSlug}/text`) and serves nothing shaped like a\n * whole ContentConfiguration or SeoConfiguration document.\n *\n * Option A's deletion is a breaking change to a published package's exported\n * surface (cms-react's useTextContent and cms-angular's ContentService both\n * import `fetchRuntimeContent`), so it is gated on the cms-core major that has\n * not been scheduled. Until then these helpers are INERT: they issue no request\n * and return `null` immediately.\n *\n * That is not a behaviour change. `null` is what production has always returned\n * here — the 404 guaranteed it — and every consumer already branches `if (data)`\n * onto build-time content. Removing the request changes no observable outcome;\n * it only stops the doomed call and converts the silence into a warning.\n *\n * DO NOT \"restore\" the fetch without a server route to point it at. Adding one is\n * option B, which was declined. `fetch.test.ts` pins all of this.\n * ─────────────────────────────────────────────────────────────────────────────\n */\n\nconst PHANTOM_ENDPOINT_NOTICE =\n '[DCS] Runtime hydration is inert (Q-297): the DCS API has never served a ' +\n 'runtime content/SEO endpoint, so this call is a no-op and the site falls ' +\n 'back to build-time content. Remove the call — the helper is deleted in the ' +\n 'next cms-core major.'\n\n/**\n * Fetch runtime content from the DCS API.\n *\n * @deprecated INERT — always resolves to `null` without issuing a request. The\n * endpoint it was written against has never existed in any server mode; Q-297\n * chose deletion over implementation. Use build-time content (`loadContentYaml`\n * / `resolveTextKey`). Scheduled for removal in the next cms-core major.\n *\n * @param siteSlug - Ignored. Retained only for source compatibility.\n * @param options - Ignored. Retained only for source compatibility.\n * @returns Always `null` — the build-time-content fallback.\n */\nexport async function fetchRuntimeContent(\n siteSlug: string,\n options: FetchOptions = {}\n): Promise<ContentConfiguration | null> {\n // Both arguments are intentionally unused: there is no endpoint to address.\n void siteSlug\n void options\n void DEFAULT_API_URL\n void DEFAULT_TIMEOUT\n\n console.warn(PHANTOM_ENDPOINT_NOTICE)\n return null\n}\n\n/**\n * Fetch runtime SEO configuration from the DCS API.\n *\n * @deprecated INERT — always resolves to `null` without issuing a request. The\n * endpoint it was written against has never existed in any server mode; Q-297\n * chose deletion over implementation. Use build-time SEO (`loadSeoYaml` /\n * `resolveSeoForPage`). Scheduled for removal in the next cms-core major.\n *\n * @param siteSlug - Ignored. Retained only for source compatibility.\n * @param options - Ignored. Retained only for source compatibility.\n * @returns Always `null` — the build-time-SEO fallback.\n */\nexport async function fetchRuntimeSeo(\n siteSlug: string,\n options: FetchOptions = {}\n): Promise<SeoConfiguration | null> {\n void siteSlug\n void options\n\n console.warn(PHANTOM_ENDPOINT_NOTICE)\n return null\n}\n","/**\n * platformFetch — a `fetch` for DCS platform API calls that CANNOT swallow HTML.\n *\n * WHY THIS EXISTS (C-298 layer 2; .docs/analysis/fleet-api-reachability-2026-07-26.md §5)\n * ---------------------------------------------------------------------------------------\n * A customer site reaches the platform API only if two independent things agree:\n * (a) the base URL the bundle resolved at build time, and\n * (b) a `customDomains` array on one Front Door route.\n * Nothing checks the agreement, and when they disagree the failure mode is\n * **HTTP 200 `text/html`** — a relative `/api/v1/*` call falls through Front Door's\n * catch-all to the static site's `index.html`. Every status-only check reads that as\n * healthy.\n *\n * The consequence is not a visible error, it is SILENCE. KEPT (a paying customer) ran for\n * ~2 weeks with 24 dead endpoints — the whole revenue rail, membership, push, and all NINE\n * login routes — because each consumer swallowed the HTML:\n * * `useRevenueSiteConfig` `await response.json()` threw on `<!DOCTYPE html>`, the\n * composable caught it, and `subscriptionTermsEnabled` silently kept its `false`\n * default. That is also why C-141 N-7 could never be verified.\n * * `fetchSiteVisitorSession` treats an unparseable body as \"signed out\", which is the\n * EXPECTED state on most page loads — so a dead auth rail is indistinguishable from a\n * visitor who simply is not logged in.\n *\n * One assertion converts a silent feature outage into a diagnosable, named error.\n *\n * THE CONTRACT\n * ------------\n * `platformFetch` is a drop-in for `fetch` that returns the SAME `Response`, so every\n * existing `if (!response.ok)` branch keeps working byte-for-byte. It throws\n * {@link PlatformFetchError} in exactly ONE situation: the response carried a body that is\n * not JSON. Specifically:\n *\n * | response | platformFetch |\n * |--------------------------------------------|----------------------------------------|\n * | 2xx `application/json` | passes through |\n * | non-2xx `application/json` (a REAL API error, incl. 404/401/403/500) | passes through UNTOUCHED — callers keep their own error contract |\n * | `application/problem+json`, `text/json` | passes through (any `*json*` type) |\n * | no content-type, body sniffs as `{`/`[` | passes through (tolerates thin origins) |\n * | 204 / 205 / 304, or a genuinely empty body | passes through (no body to assert) |\n * | `text/html` (the C-261 class), `text/plain`, anything else non-empty | **THROWS** `PlatformFetchError` |\n *\n * That table is the whole design: **being loud is scoped to the misroute class, and\n * genuine API errors are never reclassified.** A 404 that answers with JSON is the API\n * working correctly and must never be reported as a reachability failure — the API said\n * \"not found\" and it was heard.\n *\n * Network-layer failures (DNS, TLS, offline, abort) reject with the platform's own\n * `TypeError`/`AbortError` exactly as `fetch` does. They are NOT wrapped: existing\n * `catch`/timeout handling around every call site depends on those shapes, and a network\n * error was never the silent class — it already throws.\n *\n * VOCABULARY SHARED WITH THE PROBE\n * --------------------------------\n * `bodyClass` here uses the same words as `cli/probe-agent-config/` (`json` | `spa-html` |\n * `html` | `empty` | `other`), so the client-side error and the per-site reachability\n * contract (C-298 layer 4) describe the same failure with the same term.\n *\n * @example\n * ```ts\n * import { platformFetch, PlatformFetchError } from '@duffcloudservices/cms-core'\n *\n * try {\n * const res = await platformFetch(`${base}/api/v1/revenue/config`)\n * if (!res.ok) return null // unchanged: a real API error\n * return await res.json()\n * } catch (e) {\n * if (e instanceof PlatformFetchError) {\n * // LOUD: names the URL, the content-type, and the body prefix.\n * console.error(e.message)\n * }\n * return null\n * }\n * ```\n */\n\n/** How a response body classifies. Same vocabulary as `cli/probe-agent-config/`. */\nexport type PlatformBodyClass = 'json' | 'spa-html' | 'html' | 'empty' | 'other'\n\n/**\n * Why a platform call failed the JSON assertion.\n *\n * - `non-json-body` — the response carried a non-JSON body. On `bodyClass: 'spa-html'`\n * this is the C-261 Front-Door-catch-all class almost by definition.\n * - `unparseable-json` — the content-type claimed JSON but the body would not parse\n * (only produced by {@link readPlatformJson}).\n */\nexport type PlatformFetchErrorKind = 'non-json-body' | 'unparseable-json'\n\n/** How much of the offending body the error message quotes. */\nconst BODY_PREFIX_CHARS = 180\n\n/** Statuses that are defined to carry no body — nothing to assert. */\nconst BODYLESS_STATUSES = new Set([204, 205, 304])\n\n/**\n * A platform API call whose response was not JSON.\n *\n * Carries everything needed to diagnose the misroute without re-running anything: the\n * URL that was called, the status, the received content-type, the `bodyClass`, and a\n * short body prefix. `instanceof` works across bundlers (the prototype is re-pinned for\n * transpiled `extends Error`).\n */\nexport class PlatformFetchError extends Error {\n /** Stable discriminator; survives minification, unlike a class-name check. */\n readonly isPlatformFetchError = true as const\n readonly kind: PlatformFetchErrorKind\n /** The URL that was requested (as passed in). */\n readonly url: string\n /** HTTP method, upper-cased. */\n readonly method: string\n /** HTTP status of the response that failed the assertion. */\n readonly status: number\n /** The `content-type` header as received (`''` when absent). */\n readonly contentType: string\n /** Classification of the received body. */\n readonly bodyClass: PlatformBodyClass\n /** First {@link BODY_PREFIX_CHARS} characters of the body, whitespace-collapsed. */\n readonly bodyPrefix: string\n\n constructor(init: {\n kind: PlatformFetchErrorKind\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n }) {\n super(buildMessage(init))\n this.name = 'PlatformFetchError'\n this.kind = init.kind\n this.url = init.url\n this.method = init.method\n this.status = init.status\n this.contentType = init.contentType\n this.bodyClass = init.bodyClass\n this.bodyPrefix = init.bodyPrefix\n // Transpiled `extends Error` loses the prototype chain on some targets.\n Object.setPrototypeOf(this, PlatformFetchError.prototype)\n }\n}\n\n/**\n * Narrow an unknown error to a {@link PlatformFetchError} without relying on `instanceof`\n * (safe across duplicated module instances / bundler boundaries).\n */\nexport function isPlatformFetchError(error: unknown): error is PlatformFetchError {\n return (\n typeof error === 'object' &&\n error !== null &&\n (error as { isPlatformFetchError?: unknown }).isPlatformFetchError === true\n )\n}\n\nfunction buildMessage(init: {\n kind: PlatformFetchErrorKind\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n}): string {\n const ct = init.contentType ? `\"${init.contentType}\"` : '(no content-type header)'\n const head =\n init.kind === 'unparseable-json'\n ? `[DCS] platformFetch: ${init.method} ${init.url} claimed ${ct} but the body did not parse as JSON`\n : `[DCS] platformFetch: ${init.method} ${init.url} answered with ${ct} where application/json was required`\n const why =\n init.bodyClass === 'spa-html' || init.bodyClass === 'html'\n ? ' — an HTML body on a platform API path means the request never reached the API: it fell through to a static site shell (Front Door catch-all -> index.html). Check that this host routes /api/v1/* to the platform API, or call the absolute public API host instead. See C-261 / C-298.'\n : ''\n return (\n `${head} (status ${init.status}, bodyClass=${init.bodyClass}).${why}` +\n (init.bodyPrefix ? ` Body starts: ${JSON.stringify(init.bodyPrefix)}` : '')\n )\n}\n\n/** `true` for `application/json`, `text/json`, `application/problem+json`, … */\nexport function isJsonContentType(contentType: string | null | undefined): boolean {\n return /json/i.test(contentType ?? '')\n}\n\n/**\n * Classify a body the way `cli/probe-agent-config/` does, so a client-side error and a\n * server-side reachability probe describe the same failure with the same word.\n */\nexport function classifyBodyClass(\n contentType: string | null | undefined,\n body: string | null | undefined,\n): PlatformBodyClass {\n const raw = body ?? ''\n if (raw.trim() === '') return 'empty'\n const trimmed = raw.trimStart()\n if (isJsonContentType(contentType) || trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return 'json'\n }\n if (/html/i.test(contentType ?? '') || trimmed.startsWith('<')) {\n // An SPA shell served in place of an API response is the trap this exists for.\n return /<div id=\"app\"|<div id=\"root\"|type=\"module\"/i.test(raw) ? 'spa-html' : 'html'\n }\n return 'other'\n}\n\n/** A violation observed by {@link platformFetch}. */\nexport interface PlatformFetchViolation {\n url: string\n method: string\n status: number\n contentType: string\n bodyClass: PlatformBodyClass\n bodyPrefix: string\n kind: PlatformFetchErrorKind\n}\n\ntype ViolationReporter = (violation: PlatformFetchViolation) => void\n\nlet reporter: ViolationReporter | null = null\n\n/**\n * Register a side-channel for violations — e.g. an Application Insights\n * `trackEvent({ name: 'PlatformApiNonJsonResponse', ... })`. Optional by design:\n * `cms-core` must stay dependency-free, and the console error + thrown error are the\n * primary signal. Pass `null` to clear.\n */\nexport function setPlatformFetchReporter(fn: ViolationReporter | null): void {\n reporter = fn\n}\n\nexport interface PlatformFetchOptions {\n /**\n * Suppress the `console.error`. The error is still thrown — this only silences the\n * console leg, for consumers that log the thrown error themselves.\n */\n silent?: boolean\n}\n\n/** Read a clone's body without consuming the caller's `Response`. Best-effort. */\nasync function peekBody(response: Response): Promise<string | null> {\n const cloneable = typeof (response as { clone?: unknown }).clone === 'function'\n try {\n const source = cloneable ? response.clone() : response\n if (typeof source.text !== 'function') return null\n return await source.text()\n } catch {\n return null\n }\n}\n\nfunction collapse(body: string): string {\n const flat = body.replace(/\\s+/g, ' ').trim()\n return flat.length > BODY_PREFIX_CHARS ? `${flat.slice(0, BODY_PREFIX_CHARS)}…` : flat\n}\n\nfunction raise(\n violation: PlatformFetchViolation,\n options: PlatformFetchOptions | undefined,\n): PlatformFetchError {\n const error = new PlatformFetchError(violation)\n if (!options?.silent) console.error(error.message)\n try {\n reporter?.(violation)\n } catch {\n // A broken reporter must never mask the real failure.\n }\n return error\n}\n\n/**\n * `fetch` for platform API calls, with a content-type assertion that makes an HTML body\n * LOUD instead of swallowed. Returns the untouched `Response` (body unread) on success.\n *\n * @throws {PlatformFetchError} when the response carried a non-JSON body. Never for a\n * non-2xx JSON response — see the contract table at the top of this file.\n */\nexport async function platformFetch(\n input: string | URL | Request,\n init?: RequestInit,\n options?: PlatformFetchOptions,\n): Promise<Response> {\n const response = await fetch(input as RequestInfo, init)\n\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : ((input as Request).url ?? String(input))\n const method = String(init?.method ?? (input as Request)?.method ?? 'GET').toUpperCase()\n\n // Nothing to assert: no body is defined for these, and a HEAD never carries one.\n if (BODYLESS_STATUSES.has(response.status) || method === 'HEAD') return response\n\n const contentType = response.headers?.get?.('content-type') ?? ''\n\n // FAST PATH — the overwhelming majority. Never touches the body, so the caller's\n // `response.json()` / streaming behaviour is completely unaffected.\n if (isJsonContentType(contentType)) return response\n\n // Slow path only: we have to look at the body to tell \"HTML shell\" (loud) from\n // \"empty body\" / \"JSON without a content-type header\" (both tolerated).\n const body = await peekBody(response)\n if (body === null) {\n // Body not inspectable (no clone(), already consumed). Judge on the header alone:\n // an explicit non-JSON content-type is still the misroute class; a MISSING header\n // with no way to sniff is given the benefit of the doubt rather than invented.\n if (!contentType) return response\n throw raise(\n {\n kind: 'non-json-body',\n url,\n method,\n status: response.status,\n contentType,\n bodyClass: classifyBodyClass(contentType, ''),\n bodyPrefix: '',\n },\n options,\n )\n }\n\n const bodyClass = classifyBodyClass(contentType, body)\n // `json` covers a `{`/`[` body from an origin that forgot the header; `empty` covers a\n // 200 with no body, which is not the swallow class and whose handling belongs to the\n // caller (it would already fail its own `.json()` loudly).\n if (bodyClass === 'json' || bodyClass === 'empty') return response\n\n throw raise(\n {\n kind: 'non-json-body',\n url,\n method,\n status: response.status,\n contentType,\n bodyClass,\n bodyPrefix: collapse(body),\n },\n options,\n )\n}\n\n/**\n * Parse a `Response` body as JSON, turning a parse failure into the same named error\n * rather than a bare `SyntaxError: Unexpected token '<'`. Use after {@link platformFetch}\n * when you want the parse step named too.\n *\n * @throws {PlatformFetchError} `kind: 'unparseable-json'`\n */\nexport async function readPlatformJson<T>(\n response: Response,\n options?: PlatformFetchOptions,\n): Promise<T> {\n const contentType = response.headers?.get?.('content-type') ?? ''\n // Clone BEFORE parsing: once `.json()` has consumed the body, `clone()` throws and the\n // offending bytes are gone — which is exactly the evidence the error needs to quote.\n let spare: Response | null = null\n try {\n spare = typeof (response as { clone?: unknown }).clone === 'function' ? response.clone() : null\n } catch {\n spare = null\n }\n try {\n return (await response.json()) as T\n } catch {\n const body = spare ? await peekBody(spare) : null\n throw raise(\n {\n kind: 'unparseable-json',\n url: response.url ?? '',\n method: 'GET',\n status: response.status,\n contentType,\n // Classify from the BODY alone: reaching here means the content-type header\n // claimed JSON and was wrong, so it has no vote. A `text/html` shell served\n // under an `application/json` header is still the C-261 misroute class.\n bodyClass: classifyBodyClass(null, body),\n bodyPrefix: body ? collapse(body) : '',\n },\n options,\n )\n }\n}\n","/**\n * Pure functions for resolving responsive image variants from DCS CDN URLs.\n *\n * The DCS imaging pipeline generates WebP variants during upload:\n * - thumb (150px) — grid thumbnails\n * - sm (640px) — mobile displays\n * - md (1024px) — tablet / blog body\n * - lg (1920px) — desktop / hero sections\n * - og (1200×630) — social sharing (fixed aspect)\n *\n * This module provides framework-agnostic resolution logic that\n * framework packages (@duffcloudservices/cms, cms-react, etc.) wrap\n * in their respective APIs.\n *\n * @module responsive-image\n */\n\n/** Sizing context that controls which variants are included in the srcset. */\nexport type ImageContext = 'hero' | 'card' | 'thumbnail' | 'content' | 'og' | 'lightbox'\n\n/** Options for resolving a responsive image. */\nexport interface ResponsiveImageOptions {\n /** Source URL — original CDN URL or local path. */\n src: string\n /** Alt text for accessibility. */\n alt: string\n /** Sizing context — determines which variants to include. */\n context?: ImageContext\n /** Optional explicit width hints for the browser `sizes` attribute. */\n sizes?: string\n /** Skip variant resolution and return the original URL only. */\n original?: boolean\n /**\n * Intrinsic pixel width of the source image. Serves TWO purposes:\n *\n * 1. **Layout-shift hint.** When BOTH `width` and `height` are known and\n * positive, the resolver emits them as `<img width>`/`<img height>`\n * attributes so the browser reserves layout space and Cumulative Layout\n * Shift (CLS) is eliminated. These are aspect-ratio hints only — CSS\n * (`width:100%; height:auto`) still controls the rendered size. Never\n * fabricate these from a variant's target width: a `height` of 0 means\n * \"unknown\" and MUST be omitted rather than guessed (see the CDN\n * image-map, where older entries carry `height:0`).\n * 2. **Source-fit variant selection (Q-561=A / board C-804).** When `width`\n * alone is known and positive, variants whose target width EXCEEDS it are\n * omitted from the srcset — see {@link selectSourceFitVariants}. Supplying\n * `width` is therefore what makes a narrow-source image stop advertising\n * widths it cannot deliver. Omitting it preserves the pre-C-804 behaviour\n * byte-for-byte.\n */\n width?: number\n /** Intrinsic pixel height of the source image. See `width`. */\n height?: number\n}\n\n/** A single `<source>` entry for a `<picture>` element. */\nexport interface ResponsiveSource {\n srcset: string\n type: string\n sizes?: string\n}\n\n/** Resolved image properties for rendering. */\nexport interface ResponsiveImageResult {\n /** Props spreadable onto an `<img>` element. */\n imgProps: {\n src: string\n alt: string\n loading: 'lazy' | 'eager'\n decoding: 'async' | 'auto'\n /**\n * Intrinsic width — emitted only when both dimensions are known and positive\n * (layout-shift hint). Absent when dimensions are unknown; never fabricated.\n */\n width?: number\n /** Intrinsic height — see `width`. */\n height?: number\n /**\n * `high` for the hero/LCP context so the browser prioritises the fetch.\n * Absent for every other context.\n */\n fetchpriority?: 'high'\n }\n /** WebP `<source>` entries for a `<picture>` element. */\n sources: ResponsiveSource[]\n /** Whether responsive variants were detected. */\n hasVariants: boolean\n}\n\n/**\n * Build the layout-shift + priority hint fragment shared by both the\n * fallback and variant return paths. Dimensions are emitted ONLY when both\n * width and height are known and strictly positive — a missing or zero value\n * means \"unknown\" and is omitted rather than guessed, so we never fabricate a\n * wrong aspect ratio.\n */\nfunction dimensionHints(\n context: ImageContext,\n width?: number,\n height?: number,\n): { width?: number; height?: number; fetchpriority?: 'high' } {\n const hints: { width?: number; height?: number; fetchpriority?: 'high' } = {}\n if (typeof width === 'number' && width > 0 && typeof height === 'number' && height > 0) {\n hints.width = width\n hints.height = height\n }\n if (context === 'hero') {\n hints.fetchpriority = 'high'\n }\n return hints\n}\n\n/**\n * Regex that matches DCS CDN asset URLs and captures the base path, UUID, and extension.\n *\n * Supports both URL formats:\n * - `https://files.duffcloudservices.com/{slug}/assets/{uuid}.ext` (branding container)\n * - `https://files.duffcloudservices.com/content/{slug}/assets/{uuid}.ext` (content container)\n *\n * Group 1: base path including trailing slash (e.g. `https://files.duffcloudservices.com/content/kept/assets/`)\n * Group 2: UUID (e.g. `abc-123`)\n * Group 3: file extension (e.g. `jpg`)\n */\nconst CDN_PATTERN = /^(https?:\\/\\/files\\.[^/]+\\/(?:content\\/)?[^/]+\\/assets\\/(?:[^/]+\\/)*)([a-f0-9-]+)\\.(\\w+)$/\n\n/** Variant definitions mapping suffix → pixel width. */\nconst VARIANT_MAP: Record<string, { suffix: string; width: number }> = {\n thumb: { suffix: '-thumb', width: 150 },\n sm: { suffix: '-sm', width: 640 },\n md: { suffix: '-md', width: 1024 },\n lg: { suffix: '-lg', width: 1920 },\n}\n\n/** Which variants each context includes (smallest → largest). */\nconst CONTEXT_VARIANTS: Record<ImageContext, string[]> = {\n hero: ['sm', 'md', 'lg'],\n card: ['thumb', 'sm', 'md'],\n thumbnail: ['thumb', 'sm'],\n content: ['sm', 'md', 'lg'],\n og: [], // OG uses the dedicated -og variant, not srcset\n lightbox: ['md', 'lg'], // Full-screen lightbox — only large variants\n}\n\n/**\n * Source-fit variant selection — board C-804, answering Q-561=A (2026-08-19).\n *\n * The DCS upload pipeline (`internal/imaging`, `ExpectedVariants`) never\n * upscales: a fit variant whose target box already contains the source is not\n * generated at all. This module used to build its srcset from the FULL variant\n * set for the context regardless, so a narrow source advertised widths that\n * either 404'd or — once someone hand-patched the missing file in with\n * `cli/image-variant-fix` — resolved to a capped copy carrying no extra detail.\n * That is the measured iron-oak case: 1024px-wide banner sources served an\n * `-lg` advertised at `1920w` whose real resolution is 1024px, so a wide\n * viewport paid ~22.7 KB for what `-md` already delivered in ~21.75 KB.\n *\n * The rule (Q-561 option A, \"OMIT the variant when the source is narrower than\n * its target\"):\n *\n * - `sourceWidth` unknown / non-positive → **no filtering at all**. Callers\n * that cannot supply an intrinsic width keep the pre-C-804 output\n * byte-for-byte; the change is strictly opt-in on dimension knowledge.\n * - `-thumb` is **never** omitted. The pipeline generates the thumbnail\n * unconditionally (picker-grid performance), so it always exists.\n * - Any other variant is emitted iff `targetWidth <= sourceWidth`.\n *\n * **Equality keeps the variant.** Q-561=A says \"narrower than\", not \"not wider\n * than\", so a 1024px source keeps its `1024w` entry as the largest honest step\n * rather than dropping to `640w`. Documented residual: the generator's skip\n * test is `srcW <= targetW && srcH <= targetH`, so for a source that is exactly\n * the target width AND shorter than the target height (iron-oak's 1024x212)\n * the generated file may be absent even though we keep the descriptor. That is\n * the one remaining miss class — strictly narrower than before this change,\n * never wider — and the `onerror` self-heal in `ManagedImage`/`ResponsiveImage`\n * already covers it. Aligning `imaging.ExpectedVariants` onto a width-only skip\n * test would close it entirely; that rides the server, not the cms release.\n *\n * The caller uses the returned list for BOTH the file references and their `w`\n * descriptors, so an omitted variant can never leave a dangling descriptor.\n *\n * @param keys Context variant keys, ordered smallest → largest.\n * @param sourceWidth Intrinsic pixel width of the ORIGINAL uploaded image.\n * @returns The subset that the pipeline can honestly deliver.\n */\nexport function selectSourceFitVariants(keys: string[], sourceWidth?: number): string[] {\n if (typeof sourceWidth !== 'number' || !Number.isFinite(sourceWidth) || sourceWidth <= 0) {\n return keys\n }\n return keys.filter((key) => {\n // The thumbnail is always generated, whatever the source size.\n if (key === 'thumb') return true\n const v = VARIANT_MAP[key]\n if (!v) return false\n return v.width <= sourceWidth\n })\n}\n\n/** Default `sizes` attribute per context. */\nconst DEFAULT_SIZES: Record<ImageContext, string> = {\n hero: '100vw',\n card: '(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw',\n thumbnail: '150px',\n content: '(max-width: 1024px) 100vw, 1024px',\n og: '',\n lightbox: '100vw',\n}\n\n/**\n * Resolves responsive image metadata from a DCS CDN URL.\n *\n * If the URL matches the CDN asset pattern, a srcset of WebP variants\n * is generated based on the sizing `context`. Non-CDN URLs are returned\n * as-is with no variants.\n *\n * @example\n * ```ts\n * const result = resolveResponsiveImage({\n * src: 'https://files.duffcloudservices.com/kept/assets/hero/abc-123.jpg',\n * alt: 'Hero image',\n * context: 'hero',\n * })\n * // result.hasVariants === true\n * // result.sources[0].srcset contains sm, md, lg WebP sizes\n * // result.imgProps.loading === 'eager' (hero context)\n * ```\n */\nexport function resolveResponsiveImage(options: ResponsiveImageOptions): ResponsiveImageResult {\n const { src, alt, context = 'content', sizes, original, width, height } = options\n\n const hints = dimensionHints(context, width, height)\n\n const fallback: ResponsiveImageResult = {\n imgProps: {\n src,\n alt,\n loading: context === 'hero' ? 'eager' : 'lazy',\n decoding: context === 'hero' ? 'auto' : 'async',\n ...hints,\n },\n sources: [],\n hasVariants: false,\n }\n\n if (original) {\n return fallback\n }\n\n const match = src.match(CDN_PATTERN)\n if (!match) {\n return fallback\n }\n\n const [, basePath, uuid] = match\n\n const contextVariants = CONTEXT_VARIANTS[context] ?? CONTEXT_VARIANTS.content\n if (contextVariants.length === 0) {\n return fallback\n }\n\n // C-804: drop variants the pipeline cannot honestly deliver for this source.\n // No `width` supplied → `variants === contextVariants` and everything below\n // is byte-identical to the pre-C-804 output.\n const variants = selectSourceFitVariants(contextVariants, width)\n if (variants.length === 0) {\n // Every configured step is wider than the source. There is no honest\n // variant to advertise, so serve the original rather than a srcset of\n // references the pipeline never produced.\n return fallback\n }\n\n const srcsetParts = variants.map((key) => {\n const v = VARIANT_MAP[key]\n return `${basePath}${uuid}${v.suffix}.webp ${v.width}w`\n })\n\n const resolvedSizes = sizes ?? DEFAULT_SIZES[context] ?? DEFAULT_SIZES.content\n\n // `<img src>` fallback. Historically always `-md` (`-lg` for lightbox, for\n // max quality). When the source-fit filter removed that exact variant from\n // THIS context, fall back to the largest variant that survived — otherwise\n // the `<img>` would point at a file the pipeline never generated, which is\n // the very defect C-804 closes. When the filter removed nothing (or ran on a\n // context that never carried the preferred key, e.g. `thumbnail`), the\n // historical choice is preserved verbatim.\n const preferredKey = context === 'lightbox' ? 'lg' : 'md'\n const preferredWasDropped =\n contextVariants.includes(preferredKey) && !variants.includes(preferredKey)\n const largestEmitted = variants.reduce((a, b) =>\n VARIANT_MAP[b].width > VARIANT_MAP[a].width ? b : a,\n )\n const fallbackSuffix = preferredWasDropped\n ? VARIANT_MAP[largestEmitted].suffix\n : VARIANT_MAP[preferredKey].suffix\n\n return {\n imgProps: {\n src: `${basePath}${uuid}${fallbackSuffix}.webp`,\n alt,\n loading: context === 'hero' ? 'eager' : 'lazy',\n decoding: context === 'hero' ? 'auto' : 'async',\n ...hints,\n },\n sources: [\n {\n srcset: srcsetParts.join(', '),\n type: 'image/webp',\n ...(resolvedSizes ? { sizes: resolvedSizes } : {}),\n },\n ],\n hasVariants: true,\n }\n}\n\n/**\n * Tests whether a URL matches the DCS CDN asset pattern.\n *\n * Useful when rendering a mix of CDN-hosted and local images\n * to decide whether responsive treatment is applicable.\n */\nexport function isCdnAssetUrl(url: string): boolean {\n return CDN_PATTERN.test(url)\n}\n","import { platformFetch, readPlatformJson } from './platform-fetch'\n\nexport type DcsPasskeyOptions = {\n apiBaseUrl?: string\n returnTo?: string\n immediate?: boolean\n}\n\nexport type DcsPasskeyResult = {\n success: boolean\n redirectTo?: string\n visitor?: {\n email: string\n name: string\n picture?: string\n }\n}\n\ntype CeremonyOptions = {\n state: string\n publicKey: Record<string, unknown>\n}\n\ntype CredentialRequestWithImmediateUI = CredentialRequestOptions & {\n uiMode?: 'immediate'\n}\n\ntype ClientCapabilities = {\n immediateGet?: boolean\n}\n\ntype PublicKeyCredentialConstructorWithCapabilities = typeof PublicKeyCredential & {\n getClientCapabilities?: () => Promise<ClientCapabilities>\n}\n\nconst getPublicKeyCredentialConstructor = (): PublicKeyCredentialConstructorWithCapabilities | undefined => {\n if (typeof window === 'undefined' || typeof window.PublicKeyCredential === 'undefined') {\n return undefined\n }\n return window.PublicKeyCredential as PublicKeyCredentialConstructorWithCapabilities\n}\n\nconst apiBase = (value?: string) => (value ?? '/api/v1').replace(/\\/$/u, '')\n\nconst base64UrlToBuffer = (value: string): ArrayBuffer => {\n const base64 = value.replace(/-/g, '+').replace(/_/g, '/')\n const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=')\n const raw = window.atob(padded)\n const output = new Uint8Array(raw.length)\n for (let index = 0; index < raw.length; index += 1) {\n output[index] = raw.charCodeAt(index)\n }\n return output.buffer\n}\n\nconst bufferToBase64Url = (value: ArrayBuffer | null): string | null => {\n if (!value) {\n return null\n }\n const bytes = new Uint8Array(value)\n let binary = ''\n bytes.forEach(byte => {\n binary += String.fromCharCode(byte)\n })\n return window.btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/u, '')\n}\n\nconst decodeDescriptors = (items: unknown): PublicKeyCredentialDescriptor[] | undefined => {\n if (!Array.isArray(items)) {\n return undefined\n }\n return items.map(item => {\n const descriptor = item as { id?: unknown; type?: PublicKeyCredentialType; transports?: AuthenticatorTransport[] }\n return {\n ...descriptor,\n id: typeof descriptor.id === 'string' ? base64UrlToBuffer(descriptor.id) : descriptor.id,\n type: descriptor.type ?? 'public-key',\n } as PublicKeyCredentialDescriptor\n })\n}\n\nconst decodeCreationOptions = (publicKey: Record<string, unknown>): PublicKeyCredentialCreationOptions => {\n const user = publicKey.user as { id?: unknown } | undefined\n return {\n ...publicKey,\n challenge: base64UrlToBuffer(String(publicKey.challenge)),\n user: {\n ...(user ?? {}),\n id: typeof user?.id === 'string' ? base64UrlToBuffer(user.id) : user?.id,\n } as PublicKeyCredentialUserEntity,\n excludeCredentials: decodeDescriptors(publicKey.excludeCredentials),\n } as PublicKeyCredentialCreationOptions\n}\n\nconst decodeRequestOptions = (publicKey: Record<string, unknown>): PublicKeyCredentialRequestOptions => ({\n ...publicKey,\n challenge: base64UrlToBuffer(String(publicKey.challenge)),\n allowCredentials: decodeDescriptors(publicKey.allowCredentials),\n}) as PublicKeyCredentialRequestOptions\n\nconst serializeCredential = (credential: PublicKeyCredential): Record<string, unknown> => {\n const response = credential.response\n const base = {\n id: credential.id,\n type: credential.type,\n rawId: bufferToBase64Url(credential.rawId),\n authenticatorAttachment: credential.authenticatorAttachment,\n clientExtensionResults: credential.getClientExtensionResults(),\n }\n\n if (response instanceof AuthenticatorAttestationResponse) {\n return {\n ...base,\n response: {\n clientDataJSON: bufferToBase64Url(response.clientDataJSON),\n attestationObject: bufferToBase64Url(response.attestationObject),\n transports: response.getTransports?.() ?? [],\n },\n }\n }\n\n const assertion = response as AuthenticatorAssertionResponse\n return {\n ...base,\n response: {\n clientDataJSON: bufferToBase64Url(assertion.clientDataJSON),\n authenticatorData: bufferToBase64Url(assertion.authenticatorData),\n signature: bufferToBase64Url(assertion.signature),\n userHandle: bufferToBase64Url(assertion.userHandle),\n },\n }\n}\n\n// C-298 layer 2 adoption. `apiBase()` defaults to the RELATIVE `/api/v1`, so on a host\n// whose Front Door config has no `/api/v1/*` route these ceremonies get the SPA shell at\n// HTTP 200 — `response.ok` is true and `response.json()` throws a bare\n// \"Unexpected token '<'\". Passkey login was one of the nine dead KEPT site-auth routes.\n// platformFetch names the URL, the content-type and the body instead; a genuine non-2xx\n// JSON error still lands in the existing `!response.ok` branch untouched.\nconst postJSON = async <T>(url: string, body?: unknown): Promise<T> => {\n const response = await platformFetch(url, {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n })\n if (!response.ok) {\n throw new Error(`Passkey request failed (${response.status})`)\n }\n return await readPlatformJson<T>(response)\n}\n\nexport const isDcsPasskeySupported = (): boolean =>\n getPublicKeyCredentialConstructor() !== undefined &&\n typeof navigator !== 'undefined' &&\n typeof navigator.credentials?.get === 'function' &&\n typeof navigator.credentials?.create === 'function'\n\nexport const isDcsPasskeyImmediateUIAvailable = async (): Promise<boolean> => {\n const publicKeyCredential = getPublicKeyCredentialConstructor()\n if (!isDcsPasskeySupported() || typeof publicKeyCredential?.getClientCapabilities !== 'function') {\n return false\n }\n try {\n const capabilities = await publicKeyCredential.getClientCapabilities()\n return capabilities.immediateGet === true\n } catch {\n return false\n }\n}\n\nexport const registerDcsSitePasskey = async (options?: DcsPasskeyOptions): Promise<string | null> => {\n if (!isDcsPasskeySupported()) {\n throw new Error('Passkeys are not available in this browser.')\n }\n const base = apiBase(options?.apiBaseUrl)\n const ceremony = await postJSON<CeremonyOptions>(`${base}/site-auth/passkeys/register/options`)\n const credential = await navigator.credentials.create({\n publicKey: decodeCreationOptions(ceremony.publicKey),\n })\n if (!(credential instanceof PublicKeyCredential)) {\n throw new Error('Passkey registration was cancelled.')\n }\n const result = await postJSON<{ success: boolean; credentialId?: string }>(\n `${base}/site-auth/passkeys/register/verify`,\n {\n state: ceremony.state,\n credential: serializeCredential(credential),\n },\n )\n if (!result.success) {\n throw new Error('Passkey registration failed.')\n }\n return result.credentialId ?? null\n}\n\nexport const authenticateDcsSitePasskey = async (options?: DcsPasskeyOptions): Promise<DcsPasskeyResult> => {\n if (!isDcsPasskeySupported()) {\n throw new Error('Passkeys are not available in this browser.')\n }\n const base = apiBase(options?.apiBaseUrl)\n const ceremony = await postJSON<CeremonyOptions>(`${base}/site-auth/passkeys/authenticate/options`)\n const request: CredentialRequestWithImmediateUI = {\n publicKey: decodeRequestOptions(ceremony.publicKey),\n }\n if (options?.immediate) {\n request.uiMode = 'immediate'\n }\n const credential = await navigator.credentials.get(request)\n if (!(credential instanceof PublicKeyCredential)) {\n throw new Error('Passkey sign-in was cancelled.')\n }\n return await postJSON<DcsPasskeyResult>(`${base}/site-auth/passkeys/authenticate/verify`, {\n state: ceremony.state,\n credential: serializeCredential(credential),\n returnTo: options?.returnTo ?? '/',\n })\n}\n"]}
|