@duffcloudservices/cms-core 0.7.0 → 0.8.0
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.js +141 -2
- package/dist/browser.js.map +1 -1
- package/dist/index.d.ts +174 -1
- package/dist/index.js +154 -3
- package/dist/index.js.map +1 -1
- package/package.json +24 -1
package/dist/browser.js
CHANGED
|
@@ -209,6 +209,145 @@ async function fetchRuntimeSeo(siteSlug, options = {}) {
|
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
// src/platform-fetch.ts
|
|
213
|
+
var BODY_PREFIX_CHARS = 180;
|
|
214
|
+
var BODYLESS_STATUSES = /* @__PURE__ */ new Set([204, 205, 304]);
|
|
215
|
+
var PlatformFetchError = class _PlatformFetchError extends Error {
|
|
216
|
+
/** Stable discriminator; survives minification, unlike a class-name check. */
|
|
217
|
+
isPlatformFetchError = true;
|
|
218
|
+
kind;
|
|
219
|
+
/** The URL that was requested (as passed in). */
|
|
220
|
+
url;
|
|
221
|
+
/** HTTP method, upper-cased. */
|
|
222
|
+
method;
|
|
223
|
+
/** HTTP status of the response that failed the assertion. */
|
|
224
|
+
status;
|
|
225
|
+
/** The `content-type` header as received (`''` when absent). */
|
|
226
|
+
contentType;
|
|
227
|
+
/** Classification of the received body. */
|
|
228
|
+
bodyClass;
|
|
229
|
+
/** First {@link BODY_PREFIX_CHARS} characters of the body, whitespace-collapsed. */
|
|
230
|
+
bodyPrefix;
|
|
231
|
+
constructor(init) {
|
|
232
|
+
super(buildMessage(init));
|
|
233
|
+
this.name = "PlatformFetchError";
|
|
234
|
+
this.kind = init.kind;
|
|
235
|
+
this.url = init.url;
|
|
236
|
+
this.method = init.method;
|
|
237
|
+
this.status = init.status;
|
|
238
|
+
this.contentType = init.contentType;
|
|
239
|
+
this.bodyClass = init.bodyClass;
|
|
240
|
+
this.bodyPrefix = init.bodyPrefix;
|
|
241
|
+
Object.setPrototypeOf(this, _PlatformFetchError.prototype);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
function buildMessage(init) {
|
|
245
|
+
const ct = init.contentType ? `"${init.contentType}"` : "(no content-type header)";
|
|
246
|
+
const head = init.kind === "unparseable-json" ? `[DCS] platformFetch: ${init.method} ${init.url} claimed ${ct} but the body did not parse as JSON` : `[DCS] platformFetch: ${init.method} ${init.url} answered with ${ct} where application/json was required`;
|
|
247
|
+
const why = init.bodyClass === "spa-html" || init.bodyClass === "html" ? " \u2014 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." : "";
|
|
248
|
+
return `${head} (status ${init.status}, bodyClass=${init.bodyClass}).${why}` + (init.bodyPrefix ? ` Body starts: ${JSON.stringify(init.bodyPrefix)}` : "");
|
|
249
|
+
}
|
|
250
|
+
function isJsonContentType(contentType) {
|
|
251
|
+
return /json/i.test(contentType ?? "");
|
|
252
|
+
}
|
|
253
|
+
function classifyBodyClass(contentType, body) {
|
|
254
|
+
const raw = body ?? "";
|
|
255
|
+
if (raw.trim() === "") return "empty";
|
|
256
|
+
const trimmed = raw.trimStart();
|
|
257
|
+
if (isJsonContentType(contentType) || trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
258
|
+
return "json";
|
|
259
|
+
}
|
|
260
|
+
if (/html/i.test(contentType ?? "") || trimmed.startsWith("<")) {
|
|
261
|
+
return /<div id="app"|<div id="root"|type="module"/i.test(raw) ? "spa-html" : "html";
|
|
262
|
+
}
|
|
263
|
+
return "other";
|
|
264
|
+
}
|
|
265
|
+
var reporter = null;
|
|
266
|
+
async function peekBody(response) {
|
|
267
|
+
const cloneable = typeof response.clone === "function";
|
|
268
|
+
try {
|
|
269
|
+
const source = cloneable ? response.clone() : response;
|
|
270
|
+
if (typeof source.text !== "function") return null;
|
|
271
|
+
return await source.text();
|
|
272
|
+
} catch {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function collapse(body) {
|
|
277
|
+
const flat = body.replace(/\s+/g, " ").trim();
|
|
278
|
+
return flat.length > BODY_PREFIX_CHARS ? `${flat.slice(0, BODY_PREFIX_CHARS)}\u2026` : flat;
|
|
279
|
+
}
|
|
280
|
+
function raise(violation, options) {
|
|
281
|
+
const error = new PlatformFetchError(violation);
|
|
282
|
+
console.error(error.message);
|
|
283
|
+
try {
|
|
284
|
+
reporter?.(violation);
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
return error;
|
|
288
|
+
}
|
|
289
|
+
async function platformFetch(input, init, options) {
|
|
290
|
+
const response = await fetch(input, init);
|
|
291
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url ?? String(input);
|
|
292
|
+
const method = String(init?.method).toUpperCase();
|
|
293
|
+
if (BODYLESS_STATUSES.has(response.status) || method === "HEAD") return response;
|
|
294
|
+
const contentType = response.headers?.get?.("content-type") ?? "";
|
|
295
|
+
if (isJsonContentType(contentType)) return response;
|
|
296
|
+
const body = await peekBody(response);
|
|
297
|
+
if (body === null) {
|
|
298
|
+
if (!contentType) return response;
|
|
299
|
+
throw raise(
|
|
300
|
+
{
|
|
301
|
+
kind: "non-json-body",
|
|
302
|
+
url,
|
|
303
|
+
method,
|
|
304
|
+
status: response.status,
|
|
305
|
+
contentType,
|
|
306
|
+
bodyClass: classifyBodyClass(contentType, ""),
|
|
307
|
+
bodyPrefix: ""
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
const bodyClass = classifyBodyClass(contentType, body);
|
|
311
|
+
if (bodyClass === "json" || bodyClass === "empty") return response;
|
|
312
|
+
throw raise(
|
|
313
|
+
{
|
|
314
|
+
kind: "non-json-body",
|
|
315
|
+
url,
|
|
316
|
+
method,
|
|
317
|
+
status: response.status,
|
|
318
|
+
contentType,
|
|
319
|
+
bodyClass,
|
|
320
|
+
bodyPrefix: collapse(body)
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
async function readPlatformJson(response, options) {
|
|
324
|
+
const contentType = response.headers?.get?.("content-type") ?? "";
|
|
325
|
+
let spare = null;
|
|
326
|
+
try {
|
|
327
|
+
spare = typeof response.clone === "function" ? response.clone() : null;
|
|
328
|
+
} catch {
|
|
329
|
+
spare = null;
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
return await response.json();
|
|
333
|
+
} catch {
|
|
334
|
+
const body = spare ? await peekBody(spare) : null;
|
|
335
|
+
throw raise(
|
|
336
|
+
{
|
|
337
|
+
kind: "unparseable-json",
|
|
338
|
+
url: response.url ?? "",
|
|
339
|
+
method: "GET",
|
|
340
|
+
status: response.status,
|
|
341
|
+
contentType,
|
|
342
|
+
// Classify from the BODY alone: reaching here means the content-type header
|
|
343
|
+
// claimed JSON and was wrong, so it has no vote. A `text/html` shell served
|
|
344
|
+
// under an `application/json` header is still the C-261 misroute class.
|
|
345
|
+
bodyClass: classifyBodyClass(null, body),
|
|
346
|
+
bodyPrefix: body ? collapse(body) : ""
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
212
351
|
// src/passkeys.ts
|
|
213
352
|
var getPublicKeyCredentialConstructor = () => {
|
|
214
353
|
if (typeof window === "undefined" || typeof window.PublicKeyCredential === "undefined") {
|
|
@@ -299,7 +438,7 @@ var serializeCredential = (credential) => {
|
|
|
299
438
|
};
|
|
300
439
|
};
|
|
301
440
|
var postJSON = async (url, body) => {
|
|
302
|
-
const response = await
|
|
441
|
+
const response = await platformFetch(url, {
|
|
303
442
|
method: "POST",
|
|
304
443
|
credentials: "include",
|
|
305
444
|
headers: {
|
|
@@ -310,7 +449,7 @@ var postJSON = async (url, body) => {
|
|
|
310
449
|
if (!response.ok) {
|
|
311
450
|
throw new Error(`Passkey request failed (${response.status})`);
|
|
312
451
|
}
|
|
313
|
-
return await response
|
|
452
|
+
return await readPlatformJson(response);
|
|
314
453
|
};
|
|
315
454
|
var isDcsPasskeySupported = () => getPublicKeyCredentialConstructor() !== void 0 && typeof navigator !== "undefined" && typeof navigator.credentials?.get === "function" && typeof navigator.credentials?.create === "function";
|
|
316
455
|
var isDcsPasskeyImmediateUIAvailable = async () => {
|
package/dist/browser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/content.ts","../src/seo.ts","../src/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","export 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\nconst postJSON = async <T>(url: string, body?: unknown): Promise<T> => {\n const response = await fetch(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 response.json() as T\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\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"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -66,6 +66,179 @@ interface DcsConfigResult {
|
|
|
66
66
|
*/
|
|
67
67
|
declare function loadConfigYaml(projectRoot: string): Promise<DcsConfigResult>;
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* platformFetch — a `fetch` for DCS platform API calls that CANNOT swallow HTML.
|
|
71
|
+
*
|
|
72
|
+
* WHY THIS EXISTS (C-298 layer 2; .docs/analysis/fleet-api-reachability-2026-07-26.md §5)
|
|
73
|
+
* ---------------------------------------------------------------------------------------
|
|
74
|
+
* A customer site reaches the platform API only if two independent things agree:
|
|
75
|
+
* (a) the base URL the bundle resolved at build time, and
|
|
76
|
+
* (b) a `customDomains` array on one Front Door route.
|
|
77
|
+
* Nothing checks the agreement, and when they disagree the failure mode is
|
|
78
|
+
* **HTTP 200 `text/html`** — a relative `/api/v1/*` call falls through Front Door's
|
|
79
|
+
* catch-all to the static site's `index.html`. Every status-only check reads that as
|
|
80
|
+
* healthy.
|
|
81
|
+
*
|
|
82
|
+
* The consequence is not a visible error, it is SILENCE. KEPT (a paying customer) ran for
|
|
83
|
+
* ~2 weeks with 24 dead endpoints — the whole revenue rail, membership, push, and all NINE
|
|
84
|
+
* login routes — because each consumer swallowed the HTML:
|
|
85
|
+
* * `useRevenueSiteConfig` `await response.json()` threw on `<!DOCTYPE html>`, the
|
|
86
|
+
* composable caught it, and `subscriptionTermsEnabled` silently kept its `false`
|
|
87
|
+
* default. That is also why C-141 N-7 could never be verified.
|
|
88
|
+
* * `fetchSiteVisitorSession` treats an unparseable body as "signed out", which is the
|
|
89
|
+
* EXPECTED state on most page loads — so a dead auth rail is indistinguishable from a
|
|
90
|
+
* visitor who simply is not logged in.
|
|
91
|
+
*
|
|
92
|
+
* One assertion converts a silent feature outage into a diagnosable, named error.
|
|
93
|
+
*
|
|
94
|
+
* THE CONTRACT
|
|
95
|
+
* ------------
|
|
96
|
+
* `platformFetch` is a drop-in for `fetch` that returns the SAME `Response`, so every
|
|
97
|
+
* existing `if (!response.ok)` branch keeps working byte-for-byte. It throws
|
|
98
|
+
* {@link PlatformFetchError} in exactly ONE situation: the response carried a body that is
|
|
99
|
+
* not JSON. Specifically:
|
|
100
|
+
*
|
|
101
|
+
* | response | platformFetch |
|
|
102
|
+
* |--------------------------------------------|----------------------------------------|
|
|
103
|
+
* | 2xx `application/json` | passes through |
|
|
104
|
+
* | non-2xx `application/json` (a REAL API error, incl. 404/401/403/500) | passes through UNTOUCHED — callers keep their own error contract |
|
|
105
|
+
* | `application/problem+json`, `text/json` | passes through (any `*json*` type) |
|
|
106
|
+
* | no content-type, body sniffs as `{`/`[` | passes through (tolerates thin origins) |
|
|
107
|
+
* | 204 / 205 / 304, or a genuinely empty body | passes through (no body to assert) |
|
|
108
|
+
* | `text/html` (the C-261 class), `text/plain`, anything else non-empty | **THROWS** `PlatformFetchError` |
|
|
109
|
+
*
|
|
110
|
+
* That table is the whole design: **being loud is scoped to the misroute class, and
|
|
111
|
+
* genuine API errors are never reclassified.** A 404 that answers with JSON is the API
|
|
112
|
+
* working correctly and must never be reported as a reachability failure — the API said
|
|
113
|
+
* "not found" and it was heard.
|
|
114
|
+
*
|
|
115
|
+
* Network-layer failures (DNS, TLS, offline, abort) reject with the platform's own
|
|
116
|
+
* `TypeError`/`AbortError` exactly as `fetch` does. They are NOT wrapped: existing
|
|
117
|
+
* `catch`/timeout handling around every call site depends on those shapes, and a network
|
|
118
|
+
* error was never the silent class — it already throws.
|
|
119
|
+
*
|
|
120
|
+
* VOCABULARY SHARED WITH THE PROBE
|
|
121
|
+
* --------------------------------
|
|
122
|
+
* `bodyClass` here uses the same words as `cli/probe-agent-config/` (`json` | `spa-html` |
|
|
123
|
+
* `html` | `empty` | `other`), so the client-side error and the per-site reachability
|
|
124
|
+
* contract (C-298 layer 4) describe the same failure with the same term.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* import { platformFetch, PlatformFetchError } from '@duffcloudservices/cms-core'
|
|
129
|
+
*
|
|
130
|
+
* try {
|
|
131
|
+
* const res = await platformFetch(`${base}/api/v1/revenue/config`)
|
|
132
|
+
* if (!res.ok) return null // unchanged: a real API error
|
|
133
|
+
* return await res.json()
|
|
134
|
+
* } catch (e) {
|
|
135
|
+
* if (e instanceof PlatformFetchError) {
|
|
136
|
+
* // LOUD: names the URL, the content-type, and the body prefix.
|
|
137
|
+
* console.error(e.message)
|
|
138
|
+
* }
|
|
139
|
+
* return null
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
/** How a response body classifies. Same vocabulary as `cli/probe-agent-config/`. */
|
|
144
|
+
type PlatformBodyClass = 'json' | 'spa-html' | 'html' | 'empty' | 'other';
|
|
145
|
+
/**
|
|
146
|
+
* Why a platform call failed the JSON assertion.
|
|
147
|
+
*
|
|
148
|
+
* - `non-json-body` — the response carried a non-JSON body. On `bodyClass: 'spa-html'`
|
|
149
|
+
* this is the C-261 Front-Door-catch-all class almost by definition.
|
|
150
|
+
* - `unparseable-json` — the content-type claimed JSON but the body would not parse
|
|
151
|
+
* (only produced by {@link readPlatformJson}).
|
|
152
|
+
*/
|
|
153
|
+
type PlatformFetchErrorKind = 'non-json-body' | 'unparseable-json';
|
|
154
|
+
/**
|
|
155
|
+
* A platform API call whose response was not JSON.
|
|
156
|
+
*
|
|
157
|
+
* Carries everything needed to diagnose the misroute without re-running anything: the
|
|
158
|
+
* URL that was called, the status, the received content-type, the `bodyClass`, and a
|
|
159
|
+
* short body prefix. `instanceof` works across bundlers (the prototype is re-pinned for
|
|
160
|
+
* transpiled `extends Error`).
|
|
161
|
+
*/
|
|
162
|
+
declare class PlatformFetchError extends Error {
|
|
163
|
+
/** Stable discriminator; survives minification, unlike a class-name check. */
|
|
164
|
+
readonly isPlatformFetchError: true;
|
|
165
|
+
readonly kind: PlatformFetchErrorKind;
|
|
166
|
+
/** The URL that was requested (as passed in). */
|
|
167
|
+
readonly url: string;
|
|
168
|
+
/** HTTP method, upper-cased. */
|
|
169
|
+
readonly method: string;
|
|
170
|
+
/** HTTP status of the response that failed the assertion. */
|
|
171
|
+
readonly status: number;
|
|
172
|
+
/** The `content-type` header as received (`''` when absent). */
|
|
173
|
+
readonly contentType: string;
|
|
174
|
+
/** Classification of the received body. */
|
|
175
|
+
readonly bodyClass: PlatformBodyClass;
|
|
176
|
+
/** First {@link BODY_PREFIX_CHARS} characters of the body, whitespace-collapsed. */
|
|
177
|
+
readonly bodyPrefix: string;
|
|
178
|
+
constructor(init: {
|
|
179
|
+
kind: PlatformFetchErrorKind;
|
|
180
|
+
url: string;
|
|
181
|
+
method: string;
|
|
182
|
+
status: number;
|
|
183
|
+
contentType: string;
|
|
184
|
+
bodyClass: PlatformBodyClass;
|
|
185
|
+
bodyPrefix: string;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Narrow an unknown error to a {@link PlatformFetchError} without relying on `instanceof`
|
|
190
|
+
* (safe across duplicated module instances / bundler boundaries).
|
|
191
|
+
*/
|
|
192
|
+
declare function isPlatformFetchError(error: unknown): error is PlatformFetchError;
|
|
193
|
+
/** `true` for `application/json`, `text/json`, `application/problem+json`, … */
|
|
194
|
+
declare function isJsonContentType(contentType: string | null | undefined): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Classify a body the way `cli/probe-agent-config/` does, so a client-side error and a
|
|
197
|
+
* server-side reachability probe describe the same failure with the same word.
|
|
198
|
+
*/
|
|
199
|
+
declare function classifyBodyClass(contentType: string | null | undefined, body: string | null | undefined): PlatformBodyClass;
|
|
200
|
+
/** A violation observed by {@link platformFetch}. */
|
|
201
|
+
interface PlatformFetchViolation {
|
|
202
|
+
url: string;
|
|
203
|
+
method: string;
|
|
204
|
+
status: number;
|
|
205
|
+
contentType: string;
|
|
206
|
+
bodyClass: PlatformBodyClass;
|
|
207
|
+
bodyPrefix: string;
|
|
208
|
+
kind: PlatformFetchErrorKind;
|
|
209
|
+
}
|
|
210
|
+
type ViolationReporter = (violation: PlatformFetchViolation) => void;
|
|
211
|
+
/**
|
|
212
|
+
* Register a side-channel for violations — e.g. an Application Insights
|
|
213
|
+
* `trackEvent({ name: 'PlatformApiNonJsonResponse', ... })`. Optional by design:
|
|
214
|
+
* `cms-core` must stay dependency-free, and the console error + thrown error are the
|
|
215
|
+
* primary signal. Pass `null` to clear.
|
|
216
|
+
*/
|
|
217
|
+
declare function setPlatformFetchReporter(fn: ViolationReporter | null): void;
|
|
218
|
+
interface PlatformFetchOptions {
|
|
219
|
+
/**
|
|
220
|
+
* Suppress the `console.error`. The error is still thrown — this only silences the
|
|
221
|
+
* console leg, for consumers that log the thrown error themselves.
|
|
222
|
+
*/
|
|
223
|
+
silent?: boolean;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* `fetch` for platform API calls, with a content-type assertion that makes an HTML body
|
|
227
|
+
* LOUD instead of swallowed. Returns the untouched `Response` (body unread) on success.
|
|
228
|
+
*
|
|
229
|
+
* @throws {PlatformFetchError} when the response carried a non-JSON body. Never for a
|
|
230
|
+
* non-2xx JSON response — see the contract table at the top of this file.
|
|
231
|
+
*/
|
|
232
|
+
declare function platformFetch(input: string | URL | Request, init?: RequestInit, options?: PlatformFetchOptions): Promise<Response>;
|
|
233
|
+
/**
|
|
234
|
+
* Parse a `Response` body as JSON, turning a parse failure into the same named error
|
|
235
|
+
* rather than a bare `SyntaxError: Unexpected token '<'`. Use after {@link platformFetch}
|
|
236
|
+
* when you want the parse step named too.
|
|
237
|
+
*
|
|
238
|
+
* @throws {PlatformFetchError} `kind: 'unparseable-json'`
|
|
239
|
+
*/
|
|
240
|
+
declare function readPlatformJson<T>(response: Response, options?: PlatformFetchOptions): Promise<T>;
|
|
241
|
+
|
|
69
242
|
/**
|
|
70
243
|
* Pure functions for resolving responsive image variants from DCS CDN URLs.
|
|
71
244
|
*
|
|
@@ -170,4 +343,4 @@ declare function resolveResponsiveImage(options: ResponsiveImageOptions): Respon
|
|
|
170
343
|
*/
|
|
171
344
|
declare function isCdnAssetUrl(url: string): boolean;
|
|
172
345
|
|
|
173
|
-
export { ContentConfiguration, type DcsConfigResult, type ImageContext, PagesConfiguration, type ResponsiveImageOptions, type ResponsiveImageResult, type ResponsiveSource, SeoConfiguration, isCdnAssetUrl, loadConfigYaml, loadContentYaml, loadPagesYaml, loadSeoYaml, resolveResponsiveImage };
|
|
346
|
+
export { ContentConfiguration, type DcsConfigResult, type ImageContext, PagesConfiguration, type PlatformBodyClass, PlatformFetchError, type PlatformFetchErrorKind, type PlatformFetchOptions, type PlatformFetchViolation, type ResponsiveImageOptions, type ResponsiveImageResult, type ResponsiveSource, SeoConfiguration, classifyBodyClass, isCdnAssetUrl, isJsonContentType, isPlatformFetchError, loadConfigYaml, loadContentYaml, loadPagesYaml, loadSeoYaml, platformFetch, readPlatformJson, resolveResponsiveImage, setPlatformFetchReporter };
|
package/dist/index.js
CHANGED
|
@@ -313,6 +313,157 @@ async function fetchRuntimeSeo(siteSlug, options = {}) {
|
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
+
// src/platform-fetch.ts
|
|
317
|
+
var BODY_PREFIX_CHARS = 180;
|
|
318
|
+
var BODYLESS_STATUSES = /* @__PURE__ */ new Set([204, 205, 304]);
|
|
319
|
+
var PlatformFetchError = class _PlatformFetchError extends Error {
|
|
320
|
+
/** Stable discriminator; survives minification, unlike a class-name check. */
|
|
321
|
+
isPlatformFetchError = true;
|
|
322
|
+
kind;
|
|
323
|
+
/** The URL that was requested (as passed in). */
|
|
324
|
+
url;
|
|
325
|
+
/** HTTP method, upper-cased. */
|
|
326
|
+
method;
|
|
327
|
+
/** HTTP status of the response that failed the assertion. */
|
|
328
|
+
status;
|
|
329
|
+
/** The `content-type` header as received (`''` when absent). */
|
|
330
|
+
contentType;
|
|
331
|
+
/** Classification of the received body. */
|
|
332
|
+
bodyClass;
|
|
333
|
+
/** First {@link BODY_PREFIX_CHARS} characters of the body, whitespace-collapsed. */
|
|
334
|
+
bodyPrefix;
|
|
335
|
+
constructor(init) {
|
|
336
|
+
super(buildMessage(init));
|
|
337
|
+
this.name = "PlatformFetchError";
|
|
338
|
+
this.kind = init.kind;
|
|
339
|
+
this.url = init.url;
|
|
340
|
+
this.method = init.method;
|
|
341
|
+
this.status = init.status;
|
|
342
|
+
this.contentType = init.contentType;
|
|
343
|
+
this.bodyClass = init.bodyClass;
|
|
344
|
+
this.bodyPrefix = init.bodyPrefix;
|
|
345
|
+
Object.setPrototypeOf(this, _PlatformFetchError.prototype);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
function isPlatformFetchError(error) {
|
|
349
|
+
return typeof error === "object" && error !== null && error.isPlatformFetchError === true;
|
|
350
|
+
}
|
|
351
|
+
function buildMessage(init) {
|
|
352
|
+
const ct = init.contentType ? `"${init.contentType}"` : "(no content-type header)";
|
|
353
|
+
const head = init.kind === "unparseable-json" ? `[DCS] platformFetch: ${init.method} ${init.url} claimed ${ct} but the body did not parse as JSON` : `[DCS] platformFetch: ${init.method} ${init.url} answered with ${ct} where application/json was required`;
|
|
354
|
+
const why = init.bodyClass === "spa-html" || init.bodyClass === "html" ? " \u2014 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." : "";
|
|
355
|
+
return `${head} (status ${init.status}, bodyClass=${init.bodyClass}).${why}` + (init.bodyPrefix ? ` Body starts: ${JSON.stringify(init.bodyPrefix)}` : "");
|
|
356
|
+
}
|
|
357
|
+
function isJsonContentType(contentType) {
|
|
358
|
+
return /json/i.test(contentType ?? "");
|
|
359
|
+
}
|
|
360
|
+
function classifyBodyClass(contentType, body) {
|
|
361
|
+
const raw = body ?? "";
|
|
362
|
+
if (raw.trim() === "") return "empty";
|
|
363
|
+
const trimmed = raw.trimStart();
|
|
364
|
+
if (isJsonContentType(contentType) || trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
365
|
+
return "json";
|
|
366
|
+
}
|
|
367
|
+
if (/html/i.test(contentType ?? "") || trimmed.startsWith("<")) {
|
|
368
|
+
return /<div id="app"|<div id="root"|type="module"/i.test(raw) ? "spa-html" : "html";
|
|
369
|
+
}
|
|
370
|
+
return "other";
|
|
371
|
+
}
|
|
372
|
+
var reporter = null;
|
|
373
|
+
function setPlatformFetchReporter(fn) {
|
|
374
|
+
reporter = fn;
|
|
375
|
+
}
|
|
376
|
+
async function peekBody(response) {
|
|
377
|
+
const cloneable = typeof response.clone === "function";
|
|
378
|
+
try {
|
|
379
|
+
const source = cloneable ? response.clone() : response;
|
|
380
|
+
if (typeof source.text !== "function") return null;
|
|
381
|
+
return await source.text();
|
|
382
|
+
} catch {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function collapse(body) {
|
|
387
|
+
const flat = body.replace(/\s+/g, " ").trim();
|
|
388
|
+
return flat.length > BODY_PREFIX_CHARS ? `${flat.slice(0, BODY_PREFIX_CHARS)}\u2026` : flat;
|
|
389
|
+
}
|
|
390
|
+
function raise(violation, options) {
|
|
391
|
+
const error = new PlatformFetchError(violation);
|
|
392
|
+
if (!options?.silent) console.error(error.message);
|
|
393
|
+
try {
|
|
394
|
+
reporter?.(violation);
|
|
395
|
+
} catch {
|
|
396
|
+
}
|
|
397
|
+
return error;
|
|
398
|
+
}
|
|
399
|
+
async function platformFetch(input, init, options) {
|
|
400
|
+
const response = await fetch(input, init);
|
|
401
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url ?? String(input);
|
|
402
|
+
const method = String(init?.method ?? input?.method ?? "GET").toUpperCase();
|
|
403
|
+
if (BODYLESS_STATUSES.has(response.status) || method === "HEAD") return response;
|
|
404
|
+
const contentType = response.headers?.get?.("content-type") ?? "";
|
|
405
|
+
if (isJsonContentType(contentType)) return response;
|
|
406
|
+
const body = await peekBody(response);
|
|
407
|
+
if (body === null) {
|
|
408
|
+
if (!contentType) return response;
|
|
409
|
+
throw raise(
|
|
410
|
+
{
|
|
411
|
+
kind: "non-json-body",
|
|
412
|
+
url,
|
|
413
|
+
method,
|
|
414
|
+
status: response.status,
|
|
415
|
+
contentType,
|
|
416
|
+
bodyClass: classifyBodyClass(contentType, ""),
|
|
417
|
+
bodyPrefix: ""
|
|
418
|
+
},
|
|
419
|
+
options
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
const bodyClass = classifyBodyClass(contentType, body);
|
|
423
|
+
if (bodyClass === "json" || bodyClass === "empty") return response;
|
|
424
|
+
throw raise(
|
|
425
|
+
{
|
|
426
|
+
kind: "non-json-body",
|
|
427
|
+
url,
|
|
428
|
+
method,
|
|
429
|
+
status: response.status,
|
|
430
|
+
contentType,
|
|
431
|
+
bodyClass,
|
|
432
|
+
bodyPrefix: collapse(body)
|
|
433
|
+
},
|
|
434
|
+
options
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
async function readPlatformJson(response, options) {
|
|
438
|
+
const contentType = response.headers?.get?.("content-type") ?? "";
|
|
439
|
+
let spare = null;
|
|
440
|
+
try {
|
|
441
|
+
spare = typeof response.clone === "function" ? response.clone() : null;
|
|
442
|
+
} catch {
|
|
443
|
+
spare = null;
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
return await response.json();
|
|
447
|
+
} catch {
|
|
448
|
+
const body = spare ? await peekBody(spare) : null;
|
|
449
|
+
throw raise(
|
|
450
|
+
{
|
|
451
|
+
kind: "unparseable-json",
|
|
452
|
+
url: response.url ?? "",
|
|
453
|
+
method: "GET",
|
|
454
|
+
status: response.status,
|
|
455
|
+
contentType,
|
|
456
|
+
// Classify from the BODY alone: reaching here means the content-type header
|
|
457
|
+
// claimed JSON and was wrong, so it has no vote. A `text/html` shell served
|
|
458
|
+
// under an `application/json` header is still the C-261 misroute class.
|
|
459
|
+
bodyClass: classifyBodyClass(null, body),
|
|
460
|
+
bodyPrefix: body ? collapse(body) : ""
|
|
461
|
+
},
|
|
462
|
+
options
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
316
467
|
// src/responsive-image.ts
|
|
317
468
|
function dimensionHints(context, width, height) {
|
|
318
469
|
const hints = {};
|
|
@@ -494,7 +645,7 @@ var serializeCredential = (credential) => {
|
|
|
494
645
|
};
|
|
495
646
|
};
|
|
496
647
|
var postJSON = async (url, body) => {
|
|
497
|
-
const response = await
|
|
648
|
+
const response = await platformFetch(url, {
|
|
498
649
|
method: "POST",
|
|
499
650
|
credentials: "include",
|
|
500
651
|
headers: {
|
|
@@ -505,7 +656,7 @@ var postJSON = async (url, body) => {
|
|
|
505
656
|
if (!response.ok) {
|
|
506
657
|
throw new Error(`Passkey request failed (${response.status})`);
|
|
507
658
|
}
|
|
508
|
-
return await response
|
|
659
|
+
return await readPlatformJson(response);
|
|
509
660
|
};
|
|
510
661
|
var isDcsPasskeySupported = () => getPublicKeyCredentialConstructor() !== void 0 && typeof navigator !== "undefined" && typeof navigator.credentials?.get === "function" && typeof navigator.credentials?.create === "function";
|
|
511
662
|
var isDcsPasskeyImmediateUIAvailable = async () => {
|
|
@@ -567,6 +718,6 @@ var authenticateDcsSitePasskey = async (options) => {
|
|
|
567
718
|
});
|
|
568
719
|
};
|
|
569
720
|
|
|
570
|
-
export { authenticateDcsSitePasskey, buildMetaTags, fetchRuntimeContent, fetchRuntimeSeo, getGlobalContent, getPageContent, isCdnAssetUrl, isDcsPasskeyImmediateUIAvailable, isDcsPasskeySupported, loadConfigYaml, loadContentYaml, loadPagesYaml, loadSeoYaml, registerDcsSitePasskey, resolveResponsiveImage, resolveSeoForPage, resolveTextKey };
|
|
721
|
+
export { PlatformFetchError, authenticateDcsSitePasskey, buildMetaTags, classifyBodyClass, fetchRuntimeContent, fetchRuntimeSeo, getGlobalContent, getPageContent, isCdnAssetUrl, isDcsPasskeyImmediateUIAvailable, isDcsPasskeySupported, isJsonContentType, isPlatformFetchError, loadConfigYaml, loadContentYaml, loadPagesYaml, loadSeoYaml, platformFetch, readPlatformJson, registerDcsSitePasskey, resolveResponsiveImage, resolveSeoForPage, resolveTextKey, setPlatformFetchReporter };
|
|
571
722
|
//# sourceMappingURL=index.js.map
|
|
572
723
|
//# sourceMappingURL=index.js.map
|
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/responsive-image.ts","../src/passkeys.ts"],"names":[],"mappings":";;;;;AAeO,SAAS,cAAA,CACd,OAAA,EACA,IAAA,EACA,GAAA,EACoB;AAEpB,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,KAAA,GAAQ,IAAI,CAAA;AACxC,EAAA,IAAI,WAAA,IAAe,OAAO,WAAA,EAAa;AACrC,IAAA,OAAO,YAAY,GAAG,CAAA;AAAA,EACxB;AAGA,EAAA,IAAI,OAAA,CAAQ,MAAA,IAAU,GAAA,IAAO,OAAA,CAAQ,MAAA,EAAQ;AAC3C,IAAA,OAAO,OAAA,CAAQ,OAAO,GAAG,CAAA;AAAA,EAC3B;AAEA,EAAA,OAAO,MAAA;AACT;AASO,SAAS,cAAA,CACd,SACA,IAAA,EACwB;AACxB,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,IAAU,EAAC;AAClC,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,KAAA,GAAQ,IAAI,KAAK,EAAC;AAC9C,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,GAAG,WAAA,EAAY;AACrC;AAQO,SAAS,iBACd,OAAA,EACwB;AACxB,EAAA,OAAO,OAAA,CAAQ,UAAU,EAAC;AAC5B;;;ACzCO,SAAS,iBAAA,CACd,KACA,IAAA,EACa;AACb,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,MAAA,IAAU,EAAC;AAC9B,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,KAAA,GAAQ,IAAI,KAAK,EAAC;AAGtC,EAAA,IAAI,KAAA,GAAQ,OAAA,CAAQ,KAAA,IAAS,MAAA,CAAO,YAAA,IAAgB,EAAA;AACpD,EAAA,IAAI,KAAA,IAAS,MAAA,CAAO,aAAA,IAAiB,CAAC,QAAQ,eAAA,EAAiB;AAC7D,IAAA,KAAA,GAAQ,MAAA,CAAO,aAAA,CAAc,OAAA,CAAQ,IAAA,EAAM,KAAK,CAAA;AAAA,EAClD;AAEA,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,WAAA,IAAe,MAAA,CAAO,kBAAA,IAAsB,EAAA;AAGxE,EAAA,MAAM,UACJ,OAAA,CAAQ,SAAA,EAAW,KAAA,IAAS,MAAA,CAAO,QAAQ,SAAA,IAAa,MAAA;AAC1D,EAAA,MAAM,YAAY,gBAAA,CAAiB,OAAA,CAAQ,SAAA,EAAW,OAAA,EAAS,OAAO,WAAW,CAAA;AAGjF,EAAA,MAAM,eACJ,OAAA,CAAQ,OAAA,EAAS,KAAA,IAAS,MAAA,CAAO,QAAQ,cAAA,IAAkB,OAAA;AAC7D,EAAA,MAAM,OAAA,GAAU,kBAAA;AAAA,IACd,OAAA,CAAQ,OAAA;AAAA,IACR,YAAA;AAAA,IACA,KAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAO,MAAA,EAAQ;AAAA,GACjB;AAEA,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,WAAA;AAAA,IACA,KAAA,EAAO,OAAA;AAAA,IACP,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,MAAA,EAAQ,OAAA,CAAQ,MAAA,IAAU,MAAA,CAAO,MAAA;AAAA,IACjC,OAAA,EAAS,OAAA,CAAQ,MAAA,EAAQ,QAAA,CAAS,SAAS,CAAA;AAAA,IAC3C,SAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,MAAA,CAAO,OAAA;AAAA,IACnC,YAAY,OAAA,CAAQ;AAAA,GACtB;AACF;AAKA,SAAS,gBAAA,CACP,MAAA,EACA,OAAA,EACA,KAAA,EACA,WAAA,EACgC;AAChC,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,OAAO;AAAA,MACL,GAAG,MAAA;AAAA,MACH,KAAA,EAAO,OAAO,KAAA,IAAS,KAAA;AAAA,MACvB,WAAA,EAAa,OAAO,WAAA,IAAe,WAAA;AAAA,MACnC,KAAA,EAAO;AAAA,KACT;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,OAAO;AAAA,MACL,KAAA;AAAA,MACA,WAAA;AAAA,MACA,KAAA,EAAO,OAAA;AAAA,MACP,IAAA,EAAM;AAAA,KACR;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAKA,SAAS,kBAAA,CACP,WAAA,EACA,YAAA,EACA,KAAA,EACA,aACA,mBAAA,EAC8B;AAC9B,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,YAAY,IAAA,IAAQ,qBAAA;AAAA,MAC1B,IAAA,EAAM,YAAY,IAAA,IAAQ,mBAAA;AAAA,MAC1B,GAAG,WAAA;AAAA,MACH,KAAA,EAAO,YAAY,KAAA,IAAS,KAAA;AAAA,MAC5B,WAAA,EAAa,YAAY,WAAA,IAAe,WAAA;AAAA,MACxC,KAAA,EAAO;AAAA,KACT;AAAA,EACF;AAEA,EAAA,IAAI,uBAAuB,YAAA,EAAc;AACvC,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,qBAAA;AAAA,MACN,IAAA,EAAM,mBAAA;AAAA,MACN,KAAA;AAAA,MACA,WAAA;AAAA,MACA,KAAA,EAAO;AAAA,KACT;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAcA,SAAS,MAAA,CACP,IAAA,EACA,OAAA,EACA,IAAA,EACM;AACN,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,IAAA,CAAK,IAAA,CAAK,EAAE,GAAG,IAAA,EAAM,SAAS,CAAA;AAAA,EAChC;AACF;AAKA,SAAS,kBAAA,CACP,MACA,GAAA,EACM;AACN,EAAA,MAAM,KAAK,GAAA,CAAI,SAAA;AACf,EAAA,IAAI,CAAC,EAAA,EAAI;AAET,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,KAAA,EAAO,EAAE,QAAA,EAAU,YAAY,CAAA;AAC/C,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,WAAA,EAAa,EAAE,QAAA,EAAU,kBAAkB,CAAA;AAC3D,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,KAAA,EAAO,EAAE,QAAA,EAAU,YAAY,CAAA;AAC/C,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,QAAA,EAAU,EAAE,QAAA,EAAU,gBAAgB,CAAA;AACtD,EAAA,MAAA,CAAO,IAAA,EAAM,GAAG,UAAA,EAAY,QAAA,IAAY,EAAE,QAAA,EAAU,kBAAkB,CAAA;AACtE,EAAA,MAAA,CAAO,IAAA,EAAM,GAAG,WAAA,EAAa,QAAA,IAAY,EAAE,QAAA,EAAU,mBAAmB,CAAA;AACxE,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,IAAA,EAAM,EAAE,QAAA,EAAU,WAAW,CAAA;AAC7C,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,GAAA,EAAK,EAAE,QAAA,EAAU,UAAU,CAAA;AAC3C,EAAA,MAAA,CAAO,MAAM,GAAA,CAAI,QAAA,EAAU,EAAE,QAAA,EAAU,gBAAgB,CAAA;AACvD,EAAA,MAAA,CAAO,MAAM,GAAA,CAAI,MAAA,EAAQ,EAAE,QAAA,EAAU,aAAa,CAAA;AACpD;AAKA,SAAS,gBAAA,CACP,MACA,GAAA,EACM;AACN,EAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,EAAA,IAAI,CAAC,EAAA,EAAI;AAET,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,IAAA,EAAM,EAAE,IAAA,EAAM,gBAAgB,CAAA;AAC9C,EAAA,MAAA,CAAO,IAAA,EAAM,EAAA,CAAG,IAAA,GAAO,CAAA,CAAA,EAAI,EAAA,CAAG,IAAI,CAAA,CAAA,GAAK,MAAA,EAAW,EAAE,IAAA,EAAM,cAAA,EAAgB,CAAA;AAC1E,EAAA,MAAA,CAAO,IAAA,EAAM,EAAA,CAAG,OAAA,GAAU,CAAA,CAAA,EAAI,EAAA,CAAG,OAAO,CAAA,CAAA,GAAK,MAAA,EAAW,EAAE,IAAA,EAAM,iBAAA,EAAmB,CAAA;AACnF,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,KAAA,EAAO,EAAE,IAAA,EAAM,iBAAiB,CAAA;AAChD,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,WAAA,EAAa,EAAE,IAAA,EAAM,uBAAuB,CAAA;AAC5D,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,KAAA,EAAO,EAAE,IAAA,EAAM,iBAAiB,CAAA;AAChD,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,QAAA,EAAU,EAAE,IAAA,EAAM,qBAAqB,CAAA;AACzD;AASO,SAAS,cAAc,GAAA,EAA6B;AACzD,EAAA,MAAM,OAAkB,EAAC;AAGzB,EAAA,MAAA,CAAO,MAAM,GAAA,CAAI,WAAA,EAAa,EAAE,IAAA,EAAM,eAAe,CAAA;AACrD,EAAA,MAAA,CAAO,MAAM,GAAA,CAAI,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAU,CAAA;AAG3C,EAAA,kBAAA,CAAmB,MAAM,GAAG,CAAA;AAG5B,EAAA,gBAAA,CAAiB,MAAM,GAAG,CAAA;AAE1B,EAAA,OAAO,IAAA;AACT;AClMA,eAAsB,gBACpB,QAAA,EAC+B;AAC/B,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,UAAA,CAAW,QAAQ,CAAA,GACzC,QAAA,GACA,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,QAAQ,CAAA;AAExC,EAAA,MAAM,WAAA,GAAc,EAAA,CAAG,YAAA,CAAa,YAAA,EAAc,MAAM,CAAA;AACxD,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA;AAGrC,EAAA,IAAI,CAAC,QAAQ,OAAA,EAAS;AACpB,IAAA,OAAA,CAAQ,OAAA,GAAU,CAAA;AAAA,EACpB;AACA,EAAA,IAAI,CAAC,QAAQ,MAAA,EAAQ;AACnB,IAAA,OAAA,CAAQ,SAAS,EAAC;AAAA,EACpB;AACA,EAAA,IAAI,CAAC,QAAQ,KAAA,EAAO;AAClB,IAAA,OAAA,CAAQ,QAAQ,EAAC;AAAA,EACnB;AAEA,EAAA,OAAO,OAAA;AACT;AASA,eAAsB,YAAY,QAAA,EAA6C;AAC7E,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,UAAA,CAAW,QAAQ,CAAA,GACzC,QAAA,GACA,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,QAAQ,CAAA;AAExC,EAAA,MAAM,WAAA,GAAc,EAAA,CAAG,YAAA,CAAa,YAAA,EAAc,MAAM,CAAA;AACxD,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA;AAGjC,EAAA,IAAI,CAAC,IAAI,OAAA,EAAS;AAChB,IAAA,GAAA,CAAI,OAAA,GAAU,CAAA;AAAA,EAChB;AAEA,EAAA,OAAO,GAAA;AACT;AASA,eAAsB,cACpB,QAAA,EAC6B;AAC7B,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,UAAA,CAAW,QAAQ,CAAA,GACzC,QAAA,GACA,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,QAAQ,CAAA;AAExC,EAAA,MAAM,WAAA,GAAc,EAAA,CAAG,YAAA,CAAa,YAAA,EAAc,MAAM,CAAA;AACxD,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA;AAGnC,EAAA,IAAI,CAAC,MAAM,OAAA,EAAS;AAClB,IAAA,KAAA,CAAM,OAAA,GAAU,CAAA;AAAA,EAClB;AACA,EAAA,IAAI,CAAC,MAAM,KAAA,EAAO;AAChB,IAAA,KAAA,CAAM,QAAQ,EAAC;AAAA,EACjB;AAEA,EAAA,OAAO,KAAA;AACT;AASO,SAAS,iBAAA,CACd,UACA,WAAA,EACoB;AACpB,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,MAAA,EAAQ,QAAQ,CAAA;AAAA,IAC1C,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,IAAA,EAAM,QAAQ,QAAQ,CAAA;AAAA;AAAA,IAChD,KAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,QAAQ,QAAQ;AAAA,GAC9C;AAEA,EAAA,KAAA,MAAW,YAAY,aAAA,EAAe;AACpC,IAAA,IAAI,EAAA,CAAG,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC3B,MAAA,OAAO,QAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAwCA,eAAsB,eAAe,WAAA,EAA+C;AAElF,EAAA,MAAM,WAAA,GAAc,iBAAA,CAAkB,aAAA,EAAe,WAAW,CAAA;AAChE,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,IAAI;AACF,MAAA,MAAM,WAAA,GAAc,EAAA,CAAG,YAAA,CAAa,WAAA,EAAa,MAAM,CAAA;AACvD,MAAA,MAAM,GAAA,GAAM,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA;AAGjC,MAAA,IAAI,GAAA,IAAO,GAAA,CAAI,OAAA,KAAY,CAAA,EAAG;AAC5B,QAAA,MAAM,OAAA,GAAU,GAAA;AAChB,QAAA,OAAO;AAAA,UACL,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,EAAE,OAAA,EAAS,CAAA,EAAG,MAAA,EAAQ,EAAC,EAAG,KAAA,EAAO,EAAC,EAAE;AAAA,UAChE,GAAA,EAAK,OAAA,CAAQ,GAAA,IAAO,EAAE,SAAS,CAAA,EAAE;AAAA,UACjC,KAAA,EAAO;AAAA,YACL,OAAA,EAAS,CAAA;AAAA,YACT,QAAA,EAAU,OAAA,CAAQ,QAAA,IAAY,OAAA,CAAQ,MAAM,IAAA,IAAQ,EAAA;AAAA,YACpD,KAAA,EAAO,OAAA,CAAQ,KAAA,IAAS;AAAC,WAC3B;AAAA,UACA,SAAA,EAAW,IAAA;AAAA,UACX,OAAA,EAAS,CAAC,WAAW;AAAA,SACvB;AAAA,MACF;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAGA,EAAA,MAAM,UAAoB,EAAC;AAE3B,EAAA,IAAI,OAAA,GAAgC,EAAE,OAAA,EAAS,CAAA,EAAG,QAAQ,EAAC,EAAG,KAAA,EAAO,EAAC,EAAE;AACxE,EAAA,MAAM,WAAA,GAAc,iBAAA,CAAkB,cAAA,EAAgB,WAAW,CAAA;AACjE,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,OAAA,GAAU,MAAM,gBAAgB,WAAW,CAAA;AAC3C,IAAA,OAAA,CAAQ,KAAK,WAAW,CAAA;AAAA,EAC1B;AAEA,EAAA,IAAI,GAAA,GAAwB,EAAE,OAAA,EAAS,CAAA,EAAE;AACzC,EAAA,MAAM,OAAA,GAAU,iBAAA,CAAkB,UAAA,EAAY,WAAW,CAAA;AACzD,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,GAAA,GAAM,MAAM,YAAY,OAAO,CAAA;AAC/B,IAAA,OAAA,CAAQ,KAAK,OAAO,CAAA;AAAA,EACtB;AAEA,EAAA,IAAI,KAAA,GAA4B,EAAE,OAAA,EAAS,CAAA,EAAG,UAAU,EAAA,EAAI,KAAA,EAAO,EAAC,EAAE;AACtE,EAAA,MAAM,SAAA,GAAY,iBAAA,CAAkB,YAAA,EAAc,WAAW,CAAA;AAC7D,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,KAAA,GAAQ,MAAM,cAAc,SAAS,CAAA;AACrC,IAAA,OAAA,CAAQ,KAAK,SAAS,CAAA;AAAA,EACxB;AAEA,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,GAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA,EAAW,KAAA;AAAA,IACX;AAAA,GACF;AACF;;;ACzMA,IAAM,eAAA,GAAkB,mCAAA;AACxB,IAAM,eAAA,GAAkB,GAAA;AAaxB,eAAsB,mBAAA,CACpB,QAAA,EACA,OAAA,GAAwB,EAAC,EACa;AAGtC,EAAA,MAAM,EAAE,UAAA,GAAa,eAAA,EAAiB,OAAA,GAAU,iBAAgB,GAAI,OAAA;AAEpE,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,YAAY,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,OAAO,CAAA;AAE9D,EAAA,IAAI;AACF,IAAA,MAAM,WAAW,MAAM,KAAA;AAAA,MACrB,GAAG,UAAU,CAAA,uBAAA,CAAA;AAAA,MACb;AAAA,QACE,MAAA,EAAQ,KAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB,kBAAA;AAAA,UAChB,GAAG,OAAA,CAAQ;AAAA,SACb;AAAA,QACA,QAAQ,UAAA,CAAW;AAAA;AACrB,KACF;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN;AAAA,SACF;AAAA,MACF;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,EAC9B,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,YAAA,EAAc;AACzD,MAAA,OAAA,CAAQ,KAAK,uCAAuC,CAAA;AAAA,IACtD,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAA,CAAK,uCAAuC,KAAK,CAAA;AAAA,IAC3D;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,SAAE;AACA,IAAA,YAAA,CAAa,SAAS,CAAA;AAAA,EACxB;AACF;AAaA,eAAsB,eAAA,CACpB,QAAA,EACA,OAAA,GAAwB,EAAC,EACS;AAGlC,EAAA,MAAM,EAAE,UAAA,GAAa,eAAA,EAAiB,OAAA,GAAU,iBAAgB,GAAI,OAAA;AAEpE,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,YAAY,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,OAAO,CAAA;AAE9D,EAAA,IAAI;AACF,IAAA,MAAM,WAAW,MAAM,KAAA;AAAA,MACrB,GAAG,UAAU,CAAA,mBAAA,CAAA;AAAA,MACb;AAAA,QACE,MAAA,EAAQ,KAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB,kBAAA;AAAA,UAChB,GAAG,OAAA,CAAQ;AAAA,SACb;AAAA,QACA,QAAQ,UAAA,CAAW;AAAA;AACrB,KACF;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN;AAAA,SACF;AAAA,MACF;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,EAC9B,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,YAAA,EAAc;AACzD,MAAA,OAAA,CAAQ,KAAK,mCAAmC,CAAA;AAAA,IAClD,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAA,CAAK,mCAAmC,KAAK,CAAA;AAAA,IACvD;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,SAAE;AACA,IAAA,YAAA,CAAa,SAAS,CAAA;AAAA,EACxB;AACF;;;AC7CA,SAAS,cAAA,CACP,OAAA,EACA,KAAA,EACA,MAAA,EAC6D;AAC7D,EAAA,MAAM,QAAqE,EAAC;AAC5E,EAAA,IAAI,OAAO,UAAU,QAAA,IAAY,KAAA,GAAQ,KAAK,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,GAAS,CAAA,EAAG;AACtF,IAAA,KAAA,CAAM,KAAA,GAAQ,KAAA;AACd,IAAA,KAAA,CAAM,MAAA,GAAS,MAAA;AAAA,EACjB;AACA,EAAA,IAAI,YAAY,MAAA,EAAQ;AACtB,IAAA,KAAA,CAAM,aAAA,GAAgB,MAAA;AAAA,EACxB;AACA,EAAA,OAAO,KAAA;AACT;AAaA,IAAM,WAAA,GAAc,2FAAA;AAGpB,IAAM,WAAA,GAAiE;AAAA,EACrE,KAAA,EAAO,EAAE,MAAA,EAAQ,QAAA,EAAU,OAAO,GAAA,EAAI;AAAA,EACtC,EAAA,EAAI,EAAE,MAAA,EAAQ,KAAA,EAAO,OAAO,GAAA,EAAI;AAAA,EAChC,EAAA,EAAI,EAAE,MAAA,EAAQ,KAAA,EAAO,OAAO,IAAA,EAAK;AAAA,EACjC,EAAA,EAAI,EAAE,MAAA,EAAQ,KAAA,EAAO,OAAO,IAAA;AAC9B,CAAA;AAGA,IAAM,gBAAA,GAAmD;AAAA,EACvD,IAAA,EAAM,CAAC,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA;AAAA,EACvB,IAAA,EAAM,CAAC,OAAA,EAAS,IAAA,EAAM,IAAI,CAAA;AAAA,EAC1B,SAAA,EAAW,CAAC,OAAA,EAAS,IAAI,CAAA;AAAA,EACzB,OAAA,EAAS,CAAC,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA;AAAA,EAC1B,IAAI,EAAC;AAAA;AAAA,EACL,QAAA,EAAU,CAAC,IAAA,EAAM,IAAI;AAAA;AACvB,CAAA;AAGA,IAAM,aAAA,GAA8C;AAAA,EAClD,IAAA,EAAM,OAAA;AAAA,EACN,IAAA,EAAM,0DAAA;AAAA,EACN,SAAA,EAAW,OAAA;AAAA,EACX,OAAA,EAAS,mCAAA;AAAA,EACT,EAAA,EAAI,EAAA;AAAA,EACJ,QAAA,EAAU;AACZ,CAAA;AAqBO,SAAS,uBAAuB,OAAA,EAAwD;AAC7F,EAAA,MAAM,EAAE,KAAK,GAAA,EAAK,OAAA,GAAU,WAAW,KAAA,EAAO,QAAA,EAAU,KAAA,EAAO,MAAA,EAAO,GAAI,OAAA;AAE1E,EAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,OAAA,EAAS,KAAA,EAAO,MAAM,CAAA;AAEnD,EAAA,MAAM,QAAA,GAAkC;AAAA,IACtC,QAAA,EAAU;AAAA,MACR,GAAA;AAAA,MACA,GAAA;AAAA,MACA,OAAA,EAAS,OAAA,KAAY,MAAA,GAAS,OAAA,GAAU,MAAA;AAAA,MACxC,QAAA,EAAU,OAAA,KAAY,MAAA,GAAS,MAAA,GAAS,OAAA;AAAA,MACxC,GAAG;AAAA,KACL;AAAA,IACA,SAAS,EAAC;AAAA,IACV,WAAA,EAAa;AAAA,GACf;AAEA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,WAAW,CAAA;AACnC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAG,QAAA,EAAU,IAAI,CAAA,GAAI,KAAA;AAE3B,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,OAAO,CAAA,IAAK,gBAAA,CAAiB,OAAA;AAC/D,EAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAA,GAAc,QAAA,CAAS,GAAA,CAAI,CAAC,GAAA,KAAQ;AACxC,IAAA,MAAM,CAAA,GAAI,YAAY,GAAG,CAAA;AACzB,IAAA,OAAO,CAAA,EAAG,QAAQ,CAAA,EAAG,IAAI,GAAG,CAAA,CAAE,MAAM,CAAA,MAAA,EAAS,CAAA,CAAE,KAAK,CAAA,CAAA,CAAA;AAAA,EACtD,CAAC,CAAA;AAED,EAAA,MAAM,aAAA,GAAgB,KAAA,IAAS,aAAA,CAAc,OAAO,KAAK,aAAA,CAAc,OAAA;AAEvE,EAAA,OAAO;AAAA,IACL,QAAA,EAAU;AAAA,MACR,GAAA,EAAK,GAAG,QAAQ,CAAA,EAAG,IAAI,CAAA,EAAG,OAAA,KAAY,UAAA,GAAa,KAAA,GAAQ,KAAK,CAAA,KAAA,CAAA;AAAA;AAAA,MAChE,GAAA;AAAA,MACA,OAAA,EAAS,OAAA,KAAY,MAAA,GAAS,OAAA,GAAU,MAAA;AAAA,MACxC,QAAA,EAAU,OAAA,KAAY,MAAA,GAAS,MAAA,GAAS,OAAA;AAAA,MACxC,GAAG;AAAA,KACL;AAAA,IACA,OAAA,EAAS;AAAA,MACP;AAAA,QACE,MAAA,EAAQ,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAAA,QAC7B,IAAA,EAAM,YAAA;AAAA,QACN,GAAI,aAAA,GAAgB,EAAE,KAAA,EAAO,aAAA,KAAkB;AAAC;AAClD,KACF;AAAA,IACA,WAAA,EAAa;AAAA,GACf;AACF;AAQO,SAAS,cAAc,GAAA,EAAsB;AAClD,EAAA,OAAO,WAAA,CAAY,KAAK,GAAG,CAAA;AAC7B;;;ACtMA,IAAM,oCAAoC,MAAkE;AAC1G,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,MAAA,CAAO,wBAAwB,WAAA,EAAa;AACtF,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,MAAA,CAAO,mBAAA;AAChB,CAAA;AAEA,IAAM,UAAU,CAAC,KAAA,KAAA,CAAoB,SAAS,SAAA,EAAW,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAE3E,IAAM,iBAAA,GAAoB,CAAC,KAAA,KAA+B;AACxD,EAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,MAAM,GAAG,CAAA;AACzD,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,OAAO,MAAA,GAAS,CAAC,CAAA,GAAI,CAAA,EAAG,GAAG,CAAA;AAClE,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA;AAC9B,EAAA,MAAM,MAAA,GAAS,IAAI,UAAA,CAAW,GAAA,CAAI,MAAM,CAAA;AACxC,EAAA,KAAA,IAAS,QAAQ,CAAA,EAAG,KAAA,GAAQ,GAAA,CAAI,MAAA,EAAQ,SAAS,CAAA,EAAG;AAClD,IAAA,MAAA,CAAO,KAAK,CAAA,GAAI,GAAA,CAAI,UAAA,CAAW,KAAK,CAAA;AAAA,EACtC;AACA,EAAA,OAAO,MAAA,CAAO,MAAA;AAChB,CAAA;AAEA,IAAM,iBAAA,GAAoB,CAAC,KAAA,KAA6C;AACtE,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,CAAM,QAAQ,CAAA,IAAA,KAAQ;AACpB,IAAA,MAAA,IAAU,MAAA,CAAO,aAAa,IAAI,CAAA;AAAA,EACpC,CAAC,CAAA;AACD,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,QAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACvF,CAAA;AAEA,IAAM,iBAAA,GAAoB,CAAC,KAAA,KAAgE;AACzF,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACzB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA,CAAM,IAAI,CAAA,IAAA,KAAQ;AACvB,IAAA,MAAM,UAAA,GAAa,IAAA;AACnB,IAAA,OAAO;AAAA,MACL,GAAG,UAAA;AAAA,MACH,EAAA,EAAI,OAAO,UAAA,CAAW,EAAA,KAAO,WAAW,iBAAA,CAAkB,UAAA,CAAW,EAAE,CAAA,GAAI,UAAA,CAAW,EAAA;AAAA,MACtF,IAAA,EAAM,WAAW,IAAA,IAAQ;AAAA,KAC3B;AAAA,EACF,CAAC,CAAA;AACH,CAAA;AAEA,IAAM,qBAAA,GAAwB,CAAC,SAAA,KAA2E;AACxG,EAAA,MAAM,OAAO,SAAA,CAAU,IAAA;AACvB,EAAA,OAAO;AAAA,IACL,GAAG,SAAA;AAAA,IACH,SAAA,EAAW,iBAAA,CAAkB,MAAA,CAAO,SAAA,CAAU,SAAS,CAAC,CAAA;AAAA,IACxD,IAAA,EAAM;AAAA,MACJ,GAAI,QAAQ,EAAC;AAAA,MACb,EAAA,EAAI,OAAO,IAAA,EAAM,EAAA,KAAO,WAAW,iBAAA,CAAkB,IAAA,CAAK,EAAE,CAAA,GAAI,IAAA,EAAM;AAAA,KACxE;AAAA,IACA,kBAAA,EAAoB,iBAAA,CAAkB,SAAA,CAAU,kBAAkB;AAAA,GACpE;AACF,CAAA;AAEA,IAAM,oBAAA,GAAuB,CAAC,SAAA,MAA2E;AAAA,EACvG,GAAG,SAAA;AAAA,EACH,SAAA,EAAW,iBAAA,CAAkB,MAAA,CAAO,SAAA,CAAU,SAAS,CAAC,CAAA;AAAA,EACxD,gBAAA,EAAkB,iBAAA,CAAkB,SAAA,CAAU,gBAAgB;AAChE,CAAA,CAAA;AAEA,IAAM,mBAAA,GAAsB,CAAC,UAAA,KAA6D;AACxF,EAAA,MAAM,WAAW,UAAA,CAAW,QAAA;AAC5B,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,IAAI,UAAA,CAAW,EAAA;AAAA,IACf,MAAM,UAAA,CAAW,IAAA;AAAA,IACjB,KAAA,EAAO,iBAAA,CAAkB,UAAA,CAAW,KAAK,CAAA;AAAA,IACzC,yBAAyB,UAAA,CAAW,uBAAA;AAAA,IACpC,sBAAA,EAAwB,WAAW,yBAAA;AAA0B,GAC/D;AAEA,EAAA,IAAI,oBAAoB,gCAAA,EAAkC;AACxD,IAAA,OAAO;AAAA,MACL,GAAG,IAAA;AAAA,MACH,QAAA,EAAU;AAAA,QACR,cAAA,EAAgB,iBAAA,CAAkB,QAAA,CAAS,cAAc,CAAA;AAAA,QACzD,iBAAA,EAAmB,iBAAA,CAAkB,QAAA,CAAS,iBAAiB,CAAA;AAAA,QAC/D,UAAA,EAAY,QAAA,CAAS,aAAA,IAAgB,IAAK;AAAC;AAC7C,KACF;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,GAAY,QAAA;AAClB,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,QAAA,EAAU;AAAA,MACR,cAAA,EAAgB,iBAAA,CAAkB,SAAA,CAAU,cAAc,CAAA;AAAA,MAC1D,iBAAA,EAAmB,iBAAA,CAAkB,SAAA,CAAU,iBAAiB,CAAA;AAAA,MAChE,SAAA,EAAW,iBAAA,CAAkB,SAAA,CAAU,SAAS,CAAA;AAAA,MAChD,UAAA,EAAY,iBAAA,CAAkB,SAAA,CAAU,UAAU;AAAA;AACpD,GACF;AACF,CAAA;AAEA,IAAM,QAAA,GAAW,OAAU,GAAA,EAAa,IAAA,KAA+B;AACrE,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,IAChC,MAAA,EAAQ,MAAA;AAAA,IACR,WAAA,EAAa,SAAA;AAAA,IACb,OAAA,EAAS;AAAA,MACP,cAAA,EAAgB;AAAA,KAClB;AAAA,IACA,MAAM,IAAA,KAAS,MAAA,GAAY,MAAA,GAAY,IAAA,CAAK,UAAU,IAAI;AAAA,GAC3D,CAAA;AACD,EAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,QAAA,CAAS,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EAC/D;AACA,EAAA,OAAO,MAAM,SAAS,IAAA,EAAK;AAC7B,CAAA;AAEO,IAAM,wBAAwB,MACnC,iCAAA,EAAkC,KAAM,MAAA,IACxC,OAAO,SAAA,KAAc,WAAA,IACrB,OAAO,SAAA,CAAU,aAAa,GAAA,KAAQ,UAAA,IACtC,OAAO,SAAA,CAAU,aAAa,MAAA,KAAW;AAEpC,IAAM,mCAAmC,YAA8B;AAC5E,EAAA,MAAM,sBAAsB,iCAAA,EAAkC;AAC9D,EAAA,IAAI,CAAC,qBAAA,EAAsB,IAAK,OAAO,mBAAA,EAAqB,0BAA0B,UAAA,EAAY;AAChG,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI;AACF,IAAA,MAAM,YAAA,GAAe,MAAM,mBAAA,CAAoB,qBAAA,EAAsB;AACrE,IAAA,OAAO,aAAa,YAAA,KAAiB,IAAA;AAAA,EACvC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEO,IAAM,sBAAA,GAAyB,OAAO,OAAA,KAAwD;AACnG,EAAA,IAAI,CAAC,uBAAsB,EAAG;AAC5B,IAAA,MAAM,IAAI,MAAM,6CAA6C,CAAA;AAAA,EAC/D;AACA,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,EAAS,UAAU,CAAA;AACxC,EAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAA0B,CAAA,EAAG,IAAI,CAAA,oCAAA,CAAsC,CAAA;AAC9F,EAAA,MAAM,UAAA,GAAa,MAAM,SAAA,CAAU,WAAA,CAAY,MAAA,CAAO;AAAA,IACpD,SAAA,EAAW,qBAAA,CAAsB,QAAA,CAAS,SAAS;AAAA,GACpD,CAAA;AACD,EAAA,IAAI,EAAE,sBAAsB,mBAAA,CAAA,EAAsB;AAChD,IAAA,MAAM,IAAI,MAAM,qCAAqC,CAAA;AAAA,EACvD;AACA,EAAA,MAAM,SAAS,MAAM,QAAA;AAAA,IACnB,GAAG,IAAI,CAAA,mCAAA,CAAA;AAAA,IACP;AAAA,MACE,OAAO,QAAA,CAAS,KAAA;AAAA,MAChB,UAAA,EAAY,oBAAoB,UAAU;AAAA;AAC5C,GACF;AACA,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,MAAM,IAAI,MAAM,8BAA8B,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,OAAO,YAAA,IAAgB,IAAA;AAChC;AAEO,IAAM,0BAAA,GAA6B,OAAO,OAAA,KAA2D;AAC1G,EAAA,IAAI,CAAC,uBAAsB,EAAG;AAC5B,IAAA,MAAM,IAAI,MAAM,6CAA6C,CAAA;AAAA,EAC/D;AACA,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,EAAS,UAAU,CAAA;AACxC,EAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAA0B,CAAA,EAAG,IAAI,CAAA,wCAAA,CAA0C,CAAA;AAClG,EAAA,MAAM,OAAA,GAA4C;AAAA,IAChD,SAAA,EAAW,oBAAA,CAAqB,QAAA,CAAS,SAAS;AAAA,GACpD;AACA,EAAA,IAAI,SAAS,SAAA,EAAW;AACtB,IAAA,OAAA,CAAQ,MAAA,GAAS,WAAA;AAAA,EACnB;AACA,EAAA,MAAM,UAAA,GAAa,MAAM,SAAA,CAAU,WAAA,CAAY,IAAI,OAAO,CAAA;AAC1D,EAAA,IAAI,EAAE,sBAAsB,mBAAA,CAAA,EAAsB;AAChD,IAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,EAClD;AACA,EAAA,OAAO,MAAM,QAAA,CAA2B,CAAA,EAAG,IAAI,CAAA,uCAAA,CAAA,EAA2C;AAAA,IACxF,OAAO,QAAA,CAAS,KAAA;AAAA,IAChB,UAAA,EAAY,oBAAoB,UAAU,CAAA;AAAA,IAC1C,QAAA,EAAU,SAAS,QAAA,IAAY;AAAA,GAChC,CAAA;AACH","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 * 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. When BOTH `width` and `height`\n * are known and positive, the resolver emits them as `<img width>`/`<img\n * height>` attributes so the browser reserves layout space and Cumulative\n * Layout Shift (CLS) is eliminated. These are aspect-ratio hints only — CSS\n * (`width:100%; height:auto`) still controls the rendered size. Never fabricate\n * these from a variant's target width: a `height` of 0 means \"unknown\" and\n * MUST be omitted rather than guessed (see the CDN image-map, where older\n * entries carry `height:0`).\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/** 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 variants = CONTEXT_VARIANTS[context] ?? CONTEXT_VARIANTS.content\n if (variants.length === 0) {\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 return {\n imgProps: {\n src: `${basePath}${uuid}${context === 'lightbox' ? '-lg' : '-md'}.webp`, // Lightbox uses lg fallback for max quality\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","export 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\nconst postJSON = async <T>(url: string, body?: unknown): Promise<T> => {\n const response = await fetch(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 response.json() as T\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":";;;;;AAeO,SAAS,cAAA,CACd,OAAA,EACA,IAAA,EACA,GAAA,EACoB;AAEpB,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,KAAA,GAAQ,IAAI,CAAA;AACxC,EAAA,IAAI,WAAA,IAAe,OAAO,WAAA,EAAa;AACrC,IAAA,OAAO,YAAY,GAAG,CAAA;AAAA,EACxB;AAGA,EAAA,IAAI,OAAA,CAAQ,MAAA,IAAU,GAAA,IAAO,OAAA,CAAQ,MAAA,EAAQ;AAC3C,IAAA,OAAO,OAAA,CAAQ,OAAO,GAAG,CAAA;AAAA,EAC3B;AAEA,EAAA,OAAO,MAAA;AACT;AASO,SAAS,cAAA,CACd,SACA,IAAA,EACwB;AACxB,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,IAAU,EAAC;AAClC,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,KAAA,GAAQ,IAAI,KAAK,EAAC;AAC9C,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,GAAG,WAAA,EAAY;AACrC;AAQO,SAAS,iBACd,OAAA,EACwB;AACxB,EAAA,OAAO,OAAA,CAAQ,UAAU,EAAC;AAC5B;;;ACzCO,SAAS,iBAAA,CACd,KACA,IAAA,EACa;AACb,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,MAAA,IAAU,EAAC;AAC9B,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,KAAA,GAAQ,IAAI,KAAK,EAAC;AAGtC,EAAA,IAAI,KAAA,GAAQ,OAAA,CAAQ,KAAA,IAAS,MAAA,CAAO,YAAA,IAAgB,EAAA;AACpD,EAAA,IAAI,KAAA,IAAS,MAAA,CAAO,aAAA,IAAiB,CAAC,QAAQ,eAAA,EAAiB;AAC7D,IAAA,KAAA,GAAQ,MAAA,CAAO,aAAA,CAAc,OAAA,CAAQ,IAAA,EAAM,KAAK,CAAA;AAAA,EAClD;AAEA,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,WAAA,IAAe,MAAA,CAAO,kBAAA,IAAsB,EAAA;AAGxE,EAAA,MAAM,UACJ,OAAA,CAAQ,SAAA,EAAW,KAAA,IAAS,MAAA,CAAO,QAAQ,SAAA,IAAa,MAAA;AAC1D,EAAA,MAAM,YAAY,gBAAA,CAAiB,OAAA,CAAQ,SAAA,EAAW,OAAA,EAAS,OAAO,WAAW,CAAA;AAGjF,EAAA,MAAM,eACJ,OAAA,CAAQ,OAAA,EAAS,KAAA,IAAS,MAAA,CAAO,QAAQ,cAAA,IAAkB,OAAA;AAC7D,EAAA,MAAM,OAAA,GAAU,kBAAA;AAAA,IACd,OAAA,CAAQ,OAAA;AAAA,IACR,YAAA;AAAA,IACA,KAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAO,MAAA,EAAQ;AAAA,GACjB;AAEA,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,WAAA;AAAA,IACA,KAAA,EAAO,OAAA;AAAA,IACP,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,WAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,MAAA,EAAQ,OAAA,CAAQ,MAAA,IAAU,MAAA,CAAO,MAAA;AAAA,IACjC,OAAA,EAAS,OAAA,CAAQ,MAAA,EAAQ,QAAA,CAAS,SAAS,CAAA;AAAA,IAC3C,SAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,MAAA,CAAO,OAAA;AAAA,IACnC,YAAY,OAAA,CAAQ;AAAA,GACtB;AACF;AAKA,SAAS,gBAAA,CACP,MAAA,EACA,OAAA,EACA,KAAA,EACA,WAAA,EACgC;AAChC,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,OAAO;AAAA,MACL,GAAG,MAAA;AAAA,MACH,KAAA,EAAO,OAAO,KAAA,IAAS,KAAA;AAAA,MACvB,WAAA,EAAa,OAAO,WAAA,IAAe,WAAA;AAAA,MACnC,KAAA,EAAO;AAAA,KACT;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,OAAO;AAAA,MACL,KAAA;AAAA,MACA,WAAA;AAAA,MACA,KAAA,EAAO,OAAA;AAAA,MACP,IAAA,EAAM;AAAA,KACR;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAKA,SAAS,kBAAA,CACP,WAAA,EACA,YAAA,EACA,KAAA,EACA,aACA,mBAAA,EAC8B;AAC9B,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,YAAY,IAAA,IAAQ,qBAAA;AAAA,MAC1B,IAAA,EAAM,YAAY,IAAA,IAAQ,mBAAA;AAAA,MAC1B,GAAG,WAAA;AAAA,MACH,KAAA,EAAO,YAAY,KAAA,IAAS,KAAA;AAAA,MAC5B,WAAA,EAAa,YAAY,WAAA,IAAe,WAAA;AAAA,MACxC,KAAA,EAAO;AAAA,KACT;AAAA,EACF;AAEA,EAAA,IAAI,uBAAuB,YAAA,EAAc;AACvC,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,qBAAA;AAAA,MACN,IAAA,EAAM,mBAAA;AAAA,MACN,KAAA;AAAA,MACA,WAAA;AAAA,MACA,KAAA,EAAO;AAAA,KACT;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAcA,SAAS,MAAA,CACP,IAAA,EACA,OAAA,EACA,IAAA,EACM;AACN,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,IAAA,CAAK,IAAA,CAAK,EAAE,GAAG,IAAA,EAAM,SAAS,CAAA;AAAA,EAChC;AACF;AAKA,SAAS,kBAAA,CACP,MACA,GAAA,EACM;AACN,EAAA,MAAM,KAAK,GAAA,CAAI,SAAA;AACf,EAAA,IAAI,CAAC,EAAA,EAAI;AAET,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,KAAA,EAAO,EAAE,QAAA,EAAU,YAAY,CAAA;AAC/C,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,WAAA,EAAa,EAAE,QAAA,EAAU,kBAAkB,CAAA;AAC3D,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,KAAA,EAAO,EAAE,QAAA,EAAU,YAAY,CAAA;AAC/C,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,QAAA,EAAU,EAAE,QAAA,EAAU,gBAAgB,CAAA;AACtD,EAAA,MAAA,CAAO,IAAA,EAAM,GAAG,UAAA,EAAY,QAAA,IAAY,EAAE,QAAA,EAAU,kBAAkB,CAAA;AACtE,EAAA,MAAA,CAAO,IAAA,EAAM,GAAG,WAAA,EAAa,QAAA,IAAY,EAAE,QAAA,EAAU,mBAAmB,CAAA;AACxE,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,IAAA,EAAM,EAAE,QAAA,EAAU,WAAW,CAAA;AAC7C,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,GAAA,EAAK,EAAE,QAAA,EAAU,UAAU,CAAA;AAC3C,EAAA,MAAA,CAAO,MAAM,GAAA,CAAI,QAAA,EAAU,EAAE,QAAA,EAAU,gBAAgB,CAAA;AACvD,EAAA,MAAA,CAAO,MAAM,GAAA,CAAI,MAAA,EAAQ,EAAE,QAAA,EAAU,aAAa,CAAA;AACpD;AAKA,SAAS,gBAAA,CACP,MACA,GAAA,EACM;AACN,EAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,EAAA,IAAI,CAAC,EAAA,EAAI;AAET,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,IAAA,EAAM,EAAE,IAAA,EAAM,gBAAgB,CAAA;AAC9C,EAAA,MAAA,CAAO,IAAA,EAAM,EAAA,CAAG,IAAA,GAAO,CAAA,CAAA,EAAI,EAAA,CAAG,IAAI,CAAA,CAAA,GAAK,MAAA,EAAW,EAAE,IAAA,EAAM,cAAA,EAAgB,CAAA;AAC1E,EAAA,MAAA,CAAO,IAAA,EAAM,EAAA,CAAG,OAAA,GAAU,CAAA,CAAA,EAAI,EAAA,CAAG,OAAO,CAAA,CAAA,GAAK,MAAA,EAAW,EAAE,IAAA,EAAM,iBAAA,EAAmB,CAAA;AACnF,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,KAAA,EAAO,EAAE,IAAA,EAAM,iBAAiB,CAAA;AAChD,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,WAAA,EAAa,EAAE,IAAA,EAAM,uBAAuB,CAAA;AAC5D,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,KAAA,EAAO,EAAE,IAAA,EAAM,iBAAiB,CAAA;AAChD,EAAA,MAAA,CAAO,MAAM,EAAA,CAAG,QAAA,EAAU,EAAE,IAAA,EAAM,qBAAqB,CAAA;AACzD;AASO,SAAS,cAAc,GAAA,EAA6B;AACzD,EAAA,MAAM,OAAkB,EAAC;AAGzB,EAAA,MAAA,CAAO,MAAM,GAAA,CAAI,WAAA,EAAa,EAAE,IAAA,EAAM,eAAe,CAAA;AACrD,EAAA,MAAA,CAAO,MAAM,GAAA,CAAI,MAAA,EAAQ,EAAE,IAAA,EAAM,UAAU,CAAA;AAG3C,EAAA,kBAAA,CAAmB,MAAM,GAAG,CAAA;AAG5B,EAAA,gBAAA,CAAiB,MAAM,GAAG,CAAA;AAE1B,EAAA,OAAO,IAAA;AACT;AClMA,eAAsB,gBACpB,QAAA,EAC+B;AAC/B,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,UAAA,CAAW,QAAQ,CAAA,GACzC,QAAA,GACA,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,QAAQ,CAAA;AAExC,EAAA,MAAM,WAAA,GAAc,EAAA,CAAG,YAAA,CAAa,YAAA,EAAc,MAAM,CAAA;AACxD,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA;AAGrC,EAAA,IAAI,CAAC,QAAQ,OAAA,EAAS;AACpB,IAAA,OAAA,CAAQ,OAAA,GAAU,CAAA;AAAA,EACpB;AACA,EAAA,IAAI,CAAC,QAAQ,MAAA,EAAQ;AACnB,IAAA,OAAA,CAAQ,SAAS,EAAC;AAAA,EACpB;AACA,EAAA,IAAI,CAAC,QAAQ,KAAA,EAAO;AAClB,IAAA,OAAA,CAAQ,QAAQ,EAAC;AAAA,EACnB;AAEA,EAAA,OAAO,OAAA;AACT;AASA,eAAsB,YAAY,QAAA,EAA6C;AAC7E,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,UAAA,CAAW,QAAQ,CAAA,GACzC,QAAA,GACA,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,QAAQ,CAAA;AAExC,EAAA,MAAM,WAAA,GAAc,EAAA,CAAG,YAAA,CAAa,YAAA,EAAc,MAAM,CAAA;AACxD,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA;AAGjC,EAAA,IAAI,CAAC,IAAI,OAAA,EAAS;AAChB,IAAA,GAAA,CAAI,OAAA,GAAU,CAAA;AAAA,EAChB;AAEA,EAAA,OAAO,GAAA;AACT;AASA,eAAsB,cACpB,QAAA,EAC6B;AAC7B,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,UAAA,CAAW,QAAQ,CAAA,GACzC,QAAA,GACA,IAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,QAAQ,CAAA;AAExC,EAAA,MAAM,WAAA,GAAc,EAAA,CAAG,YAAA,CAAa,YAAA,EAAc,MAAM,CAAA;AACxD,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA;AAGnC,EAAA,IAAI,CAAC,MAAM,OAAA,EAAS;AAClB,IAAA,KAAA,CAAM,OAAA,GAAU,CAAA;AAAA,EAClB;AACA,EAAA,IAAI,CAAC,MAAM,KAAA,EAAO;AAChB,IAAA,KAAA,CAAM,QAAQ,EAAC;AAAA,EACjB;AAEA,EAAA,OAAO,KAAA;AACT;AASO,SAAS,iBAAA,CACd,UACA,WAAA,EACoB;AACpB,EAAA,MAAM,aAAA,GAAgB;AAAA,IACpB,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,MAAA,EAAQ,QAAQ,CAAA;AAAA,IAC1C,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,IAAA,EAAM,QAAQ,QAAQ,CAAA;AAAA;AAAA,IAChD,KAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,EAAG,QAAQ,QAAQ;AAAA,GAC9C;AAEA,EAAA,KAAA,MAAW,YAAY,aAAA,EAAe;AACpC,IAAA,IAAI,EAAA,CAAG,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC3B,MAAA,OAAO,QAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;AAwCA,eAAsB,eAAe,WAAA,EAA+C;AAElF,EAAA,MAAM,WAAA,GAAc,iBAAA,CAAkB,aAAA,EAAe,WAAW,CAAA;AAChE,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,IAAI;AACF,MAAA,MAAM,WAAA,GAAc,EAAA,CAAG,YAAA,CAAa,WAAA,EAAa,MAAM,CAAA;AACvD,MAAA,MAAM,GAAA,GAAM,IAAA,CAAK,IAAA,CAAK,WAAW,CAAA;AAGjC,MAAA,IAAI,GAAA,IAAO,GAAA,CAAI,OAAA,KAAY,CAAA,EAAG;AAC5B,QAAA,MAAM,OAAA,GAAU,GAAA;AAChB,QAAA,OAAO;AAAA,UACL,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,EAAE,OAAA,EAAS,CAAA,EAAG,MAAA,EAAQ,EAAC,EAAG,KAAA,EAAO,EAAC,EAAE;AAAA,UAChE,GAAA,EAAK,OAAA,CAAQ,GAAA,IAAO,EAAE,SAAS,CAAA,EAAE;AAAA,UACjC,KAAA,EAAO;AAAA,YACL,OAAA,EAAS,CAAA;AAAA,YACT,QAAA,EAAU,OAAA,CAAQ,QAAA,IAAY,OAAA,CAAQ,MAAM,IAAA,IAAQ,EAAA;AAAA,YACpD,KAAA,EAAO,OAAA,CAAQ,KAAA,IAAS;AAAC,WAC3B;AAAA,UACA,SAAA,EAAW,IAAA;AAAA,UACX,OAAA,EAAS,CAAC,WAAW;AAAA,SACvB;AAAA,MACF;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAGA,EAAA,MAAM,UAAoB,EAAC;AAE3B,EAAA,IAAI,OAAA,GAAgC,EAAE,OAAA,EAAS,CAAA,EAAG,QAAQ,EAAC,EAAG,KAAA,EAAO,EAAC,EAAE;AACxE,EAAA,MAAM,WAAA,GAAc,iBAAA,CAAkB,cAAA,EAAgB,WAAW,CAAA;AACjE,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,OAAA,GAAU,MAAM,gBAAgB,WAAW,CAAA;AAC3C,IAAA,OAAA,CAAQ,KAAK,WAAW,CAAA;AAAA,EAC1B;AAEA,EAAA,IAAI,GAAA,GAAwB,EAAE,OAAA,EAAS,CAAA,EAAE;AACzC,EAAA,MAAM,OAAA,GAAU,iBAAA,CAAkB,UAAA,EAAY,WAAW,CAAA;AACzD,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,GAAA,GAAM,MAAM,YAAY,OAAO,CAAA;AAC/B,IAAA,OAAA,CAAQ,KAAK,OAAO,CAAA;AAAA,EACtB;AAEA,EAAA,IAAI,KAAA,GAA4B,EAAE,OAAA,EAAS,CAAA,EAAG,UAAU,EAAA,EAAI,KAAA,EAAO,EAAC,EAAE;AACtE,EAAA,MAAM,SAAA,GAAY,iBAAA,CAAkB,YAAA,EAAc,WAAW,CAAA;AAC7D,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,KAAA,GAAQ,MAAM,cAAc,SAAS,CAAA;AACrC,IAAA,OAAA,CAAQ,KAAK,SAAS,CAAA;AAAA,EACxB;AAEA,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,GAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA,EAAW,KAAA;AAAA,IACX;AAAA,GACF;AACF;;;ACzMA,IAAM,eAAA,GAAkB,mCAAA;AACxB,IAAM,eAAA,GAAkB,GAAA;AAaxB,eAAsB,mBAAA,CACpB,QAAA,EACA,OAAA,GAAwB,EAAC,EACa;AAGtC,EAAA,MAAM,EAAE,UAAA,GAAa,eAAA,EAAiB,OAAA,GAAU,iBAAgB,GAAI,OAAA;AAEpE,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,YAAY,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,OAAO,CAAA;AAE9D,EAAA,IAAI;AACF,IAAA,MAAM,WAAW,MAAM,KAAA;AAAA,MACrB,GAAG,UAAU,CAAA,uBAAA,CAAA;AAAA,MACb;AAAA,QACE,MAAA,EAAQ,KAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB,kBAAA;AAAA,UAChB,GAAG,OAAA,CAAQ;AAAA,SACb;AAAA,QACA,QAAQ,UAAA,CAAW;AAAA;AACrB,KACF;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN;AAAA,SACF;AAAA,MACF;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,EAC9B,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,YAAA,EAAc;AACzD,MAAA,OAAA,CAAQ,KAAK,uCAAuC,CAAA;AAAA,IACtD,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAA,CAAK,uCAAuC,KAAK,CAAA;AAAA,IAC3D;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,SAAE;AACA,IAAA,YAAA,CAAa,SAAS,CAAA;AAAA,EACxB;AACF;AAaA,eAAsB,eAAA,CACpB,QAAA,EACA,OAAA,GAAwB,EAAC,EACS;AAGlC,EAAA,MAAM,EAAE,UAAA,GAAa,eAAA,EAAiB,OAAA,GAAU,iBAAgB,GAAI,OAAA;AAEpE,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,YAAY,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,OAAO,CAAA;AAE9D,EAAA,IAAI;AACF,IAAA,MAAM,WAAW,MAAM,KAAA;AAAA,MACrB,GAAG,UAAU,CAAA,mBAAA,CAAA;AAAA,MACb;AAAA,QACE,MAAA,EAAQ,KAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB,kBAAA;AAAA,UAChB,GAAG,OAAA,CAAQ;AAAA,SACb;AAAA,QACA,QAAQ,UAAA,CAAW;AAAA;AACrB,KACF;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN;AAAA,SACF;AAAA,MACF;AACA,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,EAC9B,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,YAAA,EAAc;AACzD,MAAA,OAAA,CAAQ,KAAK,mCAAmC,CAAA;AAAA,IAClD,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAA,CAAK,mCAAmC,KAAK,CAAA;AAAA,IACvD;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,SAAE;AACA,IAAA,YAAA,CAAa,SAAS,CAAA;AAAA,EACxB;AACF;;;AC5CA,IAAM,iBAAA,GAAoB,GAAA;AAG1B,IAAM,oCAAoB,IAAI,GAAA,CAAI,CAAC,GAAA,EAAK,GAAA,EAAK,GAAG,CAAC,CAAA;AAU1C,IAAM,kBAAA,GAAN,MAAM,mBAAA,SAA2B,KAAA,CAAM;AAAA;AAAA,EAEnC,oBAAA,GAAuB,IAAA;AAAA,EACvB,IAAA;AAAA;AAAA,EAEA,GAAA;AAAA;AAAA,EAEA,MAAA;AAAA;AAAA,EAEA,MAAA;AAAA;AAAA,EAEA,WAAA;AAAA;AAAA,EAEA,SAAA;AAAA;AAAA,EAEA,UAAA;AAAA,EAET,YAAY,IAAA,EAQT;AACD,IAAA,KAAA,CAAM,YAAA,CAAa,IAAI,CAAC,CAAA;AACxB,IAAA,IAAA,CAAK,IAAA,GAAO,oBAAA;AACZ,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA;AAChB,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,cAAc,IAAA,CAAK,WAAA;AACxB,IAAA,IAAA,CAAK,YAAY,IAAA,CAAK,SAAA;AACtB,IAAA,IAAA,CAAK,aAAa,IAAA,CAAK,UAAA;AAEvB,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,mBAAA,CAAmB,SAAS,CAAA;AAAA,EAC1D;AACF;AAMO,SAAS,qBAAqB,KAAA,EAA6C;AAChF,EAAA,OACE,OAAO,KAAA,KAAU,QAAA,IACjB,KAAA,KAAU,IAAA,IACT,MAA6C,oBAAA,KAAyB,IAAA;AAE3E;AAEA,SAAS,aAAa,IAAA,EAQX;AACT,EAAA,MAAM,KAAK,IAAA,CAAK,WAAA,GAAc,CAAA,CAAA,EAAI,IAAA,CAAK,WAAW,CAAA,CAAA,CAAA,GAAM,0BAAA;AACxD,EAAA,MAAM,IAAA,GACJ,KAAK,IAAA,KAAS,kBAAA,GACV,wBAAwB,IAAA,CAAK,MAAM,IAAI,IAAA,CAAK,GAAG,YAAY,EAAE,CAAA,mCAAA,CAAA,GAC7D,wBAAwB,IAAA,CAAK,MAAM,IAAI,IAAA,CAAK,GAAG,kBAAkB,EAAE,CAAA,oCAAA,CAAA;AACzE,EAAA,MAAM,MACJ,IAAA,CAAK,SAAA,KAAc,cAAc,IAAA,CAAK,SAAA,KAAc,SAChD,+RAAA,GACA,EAAA;AACN,EAAA,OACE,GAAG,IAAI,CAAA,SAAA,EAAY,KAAK,MAAM,CAAA,YAAA,EAAe,KAAK,SAAS,CAAA,EAAA,EAAK,GAAG,CAAA,CAAA,IAClE,IAAA,CAAK,aAAa,CAAA,cAAA,EAAiB,IAAA,CAAK,UAAU,IAAA,CAAK,UAAU,CAAC,CAAA,CAAA,GAAK,EAAA,CAAA;AAE5E;AAGO,SAAS,kBAAkB,WAAA,EAAiD;AACjF,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,WAAA,IAAe,EAAE,CAAA;AACvC;AAMO,SAAS,iBAAA,CACd,aACA,IAAA,EACmB;AACnB,EAAA,MAAM,MAAM,IAAA,IAAQ,EAAA;AACpB,EAAA,IAAI,GAAA,CAAI,IAAA,EAAK,KAAM,EAAA,EAAI,OAAO,OAAA;AAC9B,EAAA,MAAM,OAAA,GAAU,IAAI,SAAA,EAAU;AAC9B,EAAA,IAAI,iBAAA,CAAkB,WAAW,CAAA,IAAK,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,IAAK,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AACxF,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAA,CAAQ,KAAK,WAAA,IAAe,EAAE,KAAK,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AAE9D,IAAA,OAAO,6CAAA,CAA8C,IAAA,CAAK,GAAG,CAAA,GAAI,UAAA,GAAa,MAAA;AAAA,EAChF;AACA,EAAA,OAAO,OAAA;AACT;AAeA,IAAI,QAAA,GAAqC,IAAA;AAQlC,SAAS,yBAAyB,EAAA,EAAoC;AAC3E,EAAA,QAAA,GAAW,EAAA;AACb;AAWA,eAAe,SAAS,QAAA,EAA4C;AAClE,EAAA,MAAM,SAAA,GAAY,OAAQ,QAAA,CAAiC,KAAA,KAAU,UAAA;AACrE,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,SAAA,GAAY,QAAA,CAAS,KAAA,EAAM,GAAI,QAAA;AAC9C,IAAA,IAAI,OAAO,MAAA,CAAO,IAAA,KAAS,UAAA,EAAY,OAAO,IAAA;AAC9C,IAAA,OAAO,MAAM,OAAO,IAAA,EAAK;AAAA,EAC3B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAEA,SAAS,SAAS,IAAA,EAAsB;AACtC,EAAA,MAAM,OAAO,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,GAAG,EAAE,IAAA,EAAK;AAC5C,EAAA,OAAO,IAAA,CAAK,SAAS,iBAAA,GAAoB,CAAA,EAAG,KAAK,KAAA,CAAM,CAAA,EAAG,iBAAiB,CAAC,CAAA,MAAA,CAAA,GAAM,IAAA;AACpF;AAEA,SAAS,KAAA,CACP,WACA,OAAA,EACoB;AACpB,EAAA,MAAM,KAAA,GAAQ,IAAI,kBAAA,CAAmB,SAAS,CAAA;AAC9C,EAAA,IAAI,CAAC,OAAA,EAAS,MAAA,EAAQ,OAAA,CAAQ,KAAA,CAAM,MAAM,OAAO,CAAA;AACjD,EAAA,IAAI;AACF,IAAA,QAAA,GAAW,SAAS,CAAA;AAAA,EACtB,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,KAAA;AACT;AASA,eAAsB,aAAA,CACpB,KAAA,EACA,IAAA,EACA,OAAA,EACmB;AACnB,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,KAAA,EAAsB,IAAI,CAAA;AAEvD,EAAA,MAAM,GAAA,GACJ,OAAO,KAAA,KAAU,QAAA,GACb,KAAA,GACA,KAAA,YAAiB,GAAA,GACf,KAAA,CAAM,QAAA,EAAS,GACb,KAAA,CAAkB,GAAA,IAAO,OAAO,KAAK,CAAA;AAC/C,EAAA,MAAM,MAAA,GAAS,OAAO,IAAA,EAAM,MAAA,IAAW,OAAmB,MAAA,IAAU,KAAK,EAAE,WAAA,EAAY;AAGvF,EAAA,IAAI,kBAAkB,GAAA,CAAI,QAAA,CAAS,MAAM,CAAA,IAAK,MAAA,KAAW,QAAQ,OAAO,QAAA;AAExE,EAAA,MAAM,WAAA,GAAc,QAAA,CAAS,OAAA,EAAS,GAAA,GAAM,cAAc,CAAA,IAAK,EAAA;AAI/D,EAAA,IAAI,iBAAA,CAAkB,WAAW,CAAA,EAAG,OAAO,QAAA;AAI3C,EAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,QAAQ,CAAA;AACpC,EAAA,IAAI,SAAS,IAAA,EAAM;AAIjB,IAAA,IAAI,CAAC,aAAa,OAAO,QAAA;AACzB,IAAA,MAAM,KAAA;AAAA,MACJ;AAAA,QACE,IAAA,EAAM,eAAA;AAAA,QACN,GAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,WAAA;AAAA,QACA,SAAA,EAAW,iBAAA,CAAkB,WAAA,EAAa,EAAE,CAAA;AAAA,QAC5C,UAAA,EAAY;AAAA,OACd;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,GAAY,iBAAA,CAAkB,WAAA,EAAa,IAAI,CAAA;AAIrD,EAAA,IAAI,SAAA,KAAc,MAAA,IAAU,SAAA,KAAc,OAAA,EAAS,OAAO,QAAA;AAE1D,EAAA,MAAM,KAAA;AAAA,IACJ;AAAA,MACE,IAAA,EAAM,eAAA;AAAA,MACN,GAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAQ,QAAA,CAAS,MAAA;AAAA,MACjB,WAAA;AAAA,MACA,SAAA;AAAA,MACA,UAAA,EAAY,SAAS,IAAI;AAAA,KAC3B;AAAA,IACA;AAAA,GACF;AACF;AASA,eAAsB,gBAAA,CACpB,UACA,OAAA,EACY;AACZ,EAAA,MAAM,WAAA,GAAc,QAAA,CAAS,OAAA,EAAS,GAAA,GAAM,cAAc,CAAA,IAAK,EAAA;AAG/D,EAAA,IAAI,KAAA,GAAyB,IAAA;AAC7B,EAAA,IAAI;AACF,IAAA,KAAA,GAAQ,OAAQ,QAAA,CAAiC,KAAA,KAAU,UAAA,GAAa,QAAA,CAAS,OAAM,GAAI,IAAA;AAAA,EAC7F,CAAA,CAAA,MAAQ;AACN,IAAA,KAAA,GAAQ,IAAA;AAAA,EACV;AACA,EAAA,IAAI;AACF,IAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,EAC9B,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAA,GAAO,KAAA,GAAQ,MAAM,QAAA,CAAS,KAAK,CAAA,GAAI,IAAA;AAC7C,IAAA,MAAM,KAAA;AAAA,MACJ;AAAA,QACE,IAAA,EAAM,kBAAA;AAAA,QACN,GAAA,EAAK,SAAS,GAAA,IAAO,EAAA;AAAA,QACrB,MAAA,EAAQ,KAAA;AAAA,QACR,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,WAAA;AAAA;AAAA;AAAA;AAAA,QAIA,SAAA,EAAW,iBAAA,CAAkB,IAAA,EAAM,IAAI,CAAA;AAAA,QACvC,UAAA,EAAY,IAAA,GAAO,QAAA,CAAS,IAAI,CAAA,GAAI;AAAA,OACtC;AAAA,MACA;AAAA,KACF;AAAA,EACF;AACF;;;ACrSA,SAAS,cAAA,CACP,OAAA,EACA,KAAA,EACA,MAAA,EAC6D;AAC7D,EAAA,MAAM,QAAqE,EAAC;AAC5E,EAAA,IAAI,OAAO,UAAU,QAAA,IAAY,KAAA,GAAQ,KAAK,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,GAAS,CAAA,EAAG;AACtF,IAAA,KAAA,CAAM,KAAA,GAAQ,KAAA;AACd,IAAA,KAAA,CAAM,MAAA,GAAS,MAAA;AAAA,EACjB;AACA,EAAA,IAAI,YAAY,MAAA,EAAQ;AACtB,IAAA,KAAA,CAAM,aAAA,GAAgB,MAAA;AAAA,EACxB;AACA,EAAA,OAAO,KAAA;AACT;AAaA,IAAM,WAAA,GAAc,2FAAA;AAGpB,IAAM,WAAA,GAAiE;AAAA,EACrE,KAAA,EAAO,EAAE,MAAA,EAAQ,QAAA,EAAU,OAAO,GAAA,EAAI;AAAA,EACtC,EAAA,EAAI,EAAE,MAAA,EAAQ,KAAA,EAAO,OAAO,GAAA,EAAI;AAAA,EAChC,EAAA,EAAI,EAAE,MAAA,EAAQ,KAAA,EAAO,OAAO,IAAA,EAAK;AAAA,EACjC,EAAA,EAAI,EAAE,MAAA,EAAQ,KAAA,EAAO,OAAO,IAAA;AAC9B,CAAA;AAGA,IAAM,gBAAA,GAAmD;AAAA,EACvD,IAAA,EAAM,CAAC,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA;AAAA,EACvB,IAAA,EAAM,CAAC,OAAA,EAAS,IAAA,EAAM,IAAI,CAAA;AAAA,EAC1B,SAAA,EAAW,CAAC,OAAA,EAAS,IAAI,CAAA;AAAA,EACzB,OAAA,EAAS,CAAC,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA;AAAA,EAC1B,IAAI,EAAC;AAAA;AAAA,EACL,QAAA,EAAU,CAAC,IAAA,EAAM,IAAI;AAAA;AACvB,CAAA;AAGA,IAAM,aAAA,GAA8C;AAAA,EAClD,IAAA,EAAM,OAAA;AAAA,EACN,IAAA,EAAM,0DAAA;AAAA,EACN,SAAA,EAAW,OAAA;AAAA,EACX,OAAA,EAAS,mCAAA;AAAA,EACT,EAAA,EAAI,EAAA;AAAA,EACJ,QAAA,EAAU;AACZ,CAAA;AAqBO,SAAS,uBAAuB,OAAA,EAAwD;AAC7F,EAAA,MAAM,EAAE,KAAK,GAAA,EAAK,OAAA,GAAU,WAAW,KAAA,EAAO,QAAA,EAAU,KAAA,EAAO,MAAA,EAAO,GAAI,OAAA;AAE1E,EAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,OAAA,EAAS,KAAA,EAAO,MAAM,CAAA;AAEnD,EAAA,MAAM,QAAA,GAAkC;AAAA,IACtC,QAAA,EAAU;AAAA,MACR,GAAA;AAAA,MACA,GAAA;AAAA,MACA,OAAA,EAAS,OAAA,KAAY,MAAA,GAAS,OAAA,GAAU,MAAA;AAAA,MACxC,QAAA,EAAU,OAAA,KAAY,MAAA,GAAS,MAAA,GAAS,OAAA;AAAA,MACxC,GAAG;AAAA,KACL;AAAA,IACA,SAAS,EAAC;AAAA,IACV,WAAA,EAAa;AAAA,GACf;AAEA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,WAAW,CAAA;AACnC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAG,QAAA,EAAU,IAAI,CAAA,GAAI,KAAA;AAE3B,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,OAAO,CAAA,IAAK,gBAAA,CAAiB,OAAA;AAC/D,EAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,IAAA,OAAO,QAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAA,GAAc,QAAA,CAAS,GAAA,CAAI,CAAC,GAAA,KAAQ;AACxC,IAAA,MAAM,CAAA,GAAI,YAAY,GAAG,CAAA;AACzB,IAAA,OAAO,CAAA,EAAG,QAAQ,CAAA,EAAG,IAAI,GAAG,CAAA,CAAE,MAAM,CAAA,MAAA,EAAS,CAAA,CAAE,KAAK,CAAA,CAAA,CAAA;AAAA,EACtD,CAAC,CAAA;AAED,EAAA,MAAM,aAAA,GAAgB,KAAA,IAAS,aAAA,CAAc,OAAO,KAAK,aAAA,CAAc,OAAA;AAEvE,EAAA,OAAO;AAAA,IACL,QAAA,EAAU;AAAA,MACR,GAAA,EAAK,GAAG,QAAQ,CAAA,EAAG,IAAI,CAAA,EAAG,OAAA,KAAY,UAAA,GAAa,KAAA,GAAQ,KAAK,CAAA,KAAA,CAAA;AAAA;AAAA,MAChE,GAAA;AAAA,MACA,OAAA,EAAS,OAAA,KAAY,MAAA,GAAS,OAAA,GAAU,MAAA;AAAA,MACxC,QAAA,EAAU,OAAA,KAAY,MAAA,GAAS,MAAA,GAAS,OAAA;AAAA,MACxC,GAAG;AAAA,KACL;AAAA,IACA,OAAA,EAAS;AAAA,MACP;AAAA,QACE,MAAA,EAAQ,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAAA,QAC7B,IAAA,EAAM,YAAA;AAAA,QACN,GAAI,aAAA,GAAgB,EAAE,KAAA,EAAO,aAAA,KAAkB;AAAC;AAClD,KACF;AAAA,IACA,WAAA,EAAa;AAAA,GACf;AACF;AAQO,SAAS,cAAc,GAAA,EAAsB;AAClD,EAAA,OAAO,WAAA,CAAY,KAAK,GAAG,CAAA;AAC7B;;;ACpMA,IAAM,oCAAoC,MAAkE;AAC1G,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,MAAA,CAAO,wBAAwB,WAAA,EAAa;AACtF,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,MAAA,CAAO,mBAAA;AAChB,CAAA;AAEA,IAAM,UAAU,CAAC,KAAA,KAAA,CAAoB,SAAS,SAAA,EAAW,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAE3E,IAAM,iBAAA,GAAoB,CAAC,KAAA,KAA+B;AACxD,EAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,IAAA,EAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,MAAM,GAAG,CAAA;AACzD,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,OAAO,MAAA,GAAS,CAAC,CAAA,GAAI,CAAA,EAAG,GAAG,CAAA;AAClE,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA;AAC9B,EAAA,MAAM,MAAA,GAAS,IAAI,UAAA,CAAW,GAAA,CAAI,MAAM,CAAA;AACxC,EAAA,KAAA,IAAS,QAAQ,CAAA,EAAG,KAAA,GAAQ,GAAA,CAAI,MAAA,EAAQ,SAAS,CAAA,EAAG;AAClD,IAAA,MAAA,CAAO,KAAK,CAAA,GAAI,GAAA,CAAI,UAAA,CAAW,KAAK,CAAA;AAAA,EACtC;AACA,EAAA,OAAO,MAAA,CAAO,MAAA;AAChB,CAAA;AAEA,IAAM,iBAAA,GAAoB,CAAC,KAAA,KAA6C;AACtE,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,CAAM,QAAQ,CAAA,IAAA,KAAQ;AACpB,IAAA,MAAA,IAAU,MAAA,CAAO,aAAa,IAAI,CAAA;AAAA,EACpC,CAAC,CAAA;AACD,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA,CAAE,QAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACvF,CAAA;AAEA,IAAM,iBAAA,GAAoB,CAAC,KAAA,KAAgE;AACzF,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACzB,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA,CAAM,IAAI,CAAA,IAAA,KAAQ;AACvB,IAAA,MAAM,UAAA,GAAa,IAAA;AACnB,IAAA,OAAO;AAAA,MACL,GAAG,UAAA;AAAA,MACH,EAAA,EAAI,OAAO,UAAA,CAAW,EAAA,KAAO,WAAW,iBAAA,CAAkB,UAAA,CAAW,EAAE,CAAA,GAAI,UAAA,CAAW,EAAA;AAAA,MACtF,IAAA,EAAM,WAAW,IAAA,IAAQ;AAAA,KAC3B;AAAA,EACF,CAAC,CAAA;AACH,CAAA;AAEA,IAAM,qBAAA,GAAwB,CAAC,SAAA,KAA2E;AACxG,EAAA,MAAM,OAAO,SAAA,CAAU,IAAA;AACvB,EAAA,OAAO;AAAA,IACL,GAAG,SAAA;AAAA,IACH,SAAA,EAAW,iBAAA,CAAkB,MAAA,CAAO,SAAA,CAAU,SAAS,CAAC,CAAA;AAAA,IACxD,IAAA,EAAM;AAAA,MACJ,GAAI,QAAQ,EAAC;AAAA,MACb,EAAA,EAAI,OAAO,IAAA,EAAM,EAAA,KAAO,WAAW,iBAAA,CAAkB,IAAA,CAAK,EAAE,CAAA,GAAI,IAAA,EAAM;AAAA,KACxE;AAAA,IACA,kBAAA,EAAoB,iBAAA,CAAkB,SAAA,CAAU,kBAAkB;AAAA,GACpE;AACF,CAAA;AAEA,IAAM,oBAAA,GAAuB,CAAC,SAAA,MAA2E;AAAA,EACvG,GAAG,SAAA;AAAA,EACH,SAAA,EAAW,iBAAA,CAAkB,MAAA,CAAO,SAAA,CAAU,SAAS,CAAC,CAAA;AAAA,EACxD,gBAAA,EAAkB,iBAAA,CAAkB,SAAA,CAAU,gBAAgB;AAChE,CAAA,CAAA;AAEA,IAAM,mBAAA,GAAsB,CAAC,UAAA,KAA6D;AACxF,EAAA,MAAM,WAAW,UAAA,CAAW,QAAA;AAC5B,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,IAAI,UAAA,CAAW,EAAA;AAAA,IACf,MAAM,UAAA,CAAW,IAAA;AAAA,IACjB,KAAA,EAAO,iBAAA,CAAkB,UAAA,CAAW,KAAK,CAAA;AAAA,IACzC,yBAAyB,UAAA,CAAW,uBAAA;AAAA,IACpC,sBAAA,EAAwB,WAAW,yBAAA;AAA0B,GAC/D;AAEA,EAAA,IAAI,oBAAoB,gCAAA,EAAkC;AACxD,IAAA,OAAO;AAAA,MACL,GAAG,IAAA;AAAA,MACH,QAAA,EAAU;AAAA,QACR,cAAA,EAAgB,iBAAA,CAAkB,QAAA,CAAS,cAAc,CAAA;AAAA,QACzD,iBAAA,EAAmB,iBAAA,CAAkB,QAAA,CAAS,iBAAiB,CAAA;AAAA,QAC/D,UAAA,EAAY,QAAA,CAAS,aAAA,IAAgB,IAAK;AAAC;AAC7C,KACF;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,GAAY,QAAA;AAClB,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,QAAA,EAAU;AAAA,MACR,cAAA,EAAgB,iBAAA,CAAkB,SAAA,CAAU,cAAc,CAAA;AAAA,MAC1D,iBAAA,EAAmB,iBAAA,CAAkB,SAAA,CAAU,iBAAiB,CAAA;AAAA,MAChE,SAAA,EAAW,iBAAA,CAAkB,SAAA,CAAU,SAAS,CAAA;AAAA,MAChD,UAAA,EAAY,iBAAA,CAAkB,SAAA,CAAU,UAAU;AAAA;AACpD,GACF;AACF,CAAA;AAQA,IAAM,QAAA,GAAW,OAAU,GAAA,EAAa,IAAA,KAA+B;AACrE,EAAA,MAAM,QAAA,GAAW,MAAM,aAAA,CAAc,GAAA,EAAK;AAAA,IACxC,MAAA,EAAQ,MAAA;AAAA,IACR,WAAA,EAAa,SAAA;AAAA,IACb,OAAA,EAAS;AAAA,MACP,cAAA,EAAgB;AAAA,KAClB;AAAA,IACA,MAAM,IAAA,KAAS,MAAA,GAAY,MAAA,GAAY,IAAA,CAAK,UAAU,IAAI;AAAA,GAC3D,CAAA;AACD,EAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,QAAA,CAAS,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EAC/D;AACA,EAAA,OAAO,MAAM,iBAAoB,QAAQ,CAAA;AAC3C,CAAA;AAEO,IAAM,wBAAwB,MACnC,iCAAA,EAAkC,KAAM,MAAA,IACxC,OAAO,SAAA,KAAc,WAAA,IACrB,OAAO,SAAA,CAAU,aAAa,GAAA,KAAQ,UAAA,IACtC,OAAO,SAAA,CAAU,aAAa,MAAA,KAAW;AAEpC,IAAM,mCAAmC,YAA8B;AAC5E,EAAA,MAAM,sBAAsB,iCAAA,EAAkC;AAC9D,EAAA,IAAI,CAAC,qBAAA,EAAsB,IAAK,OAAO,mBAAA,EAAqB,0BAA0B,UAAA,EAAY;AAChG,IAAA,OAAO,KAAA;AAAA,EACT;AACA,EAAA,IAAI;AACF,IAAA,MAAM,YAAA,GAAe,MAAM,mBAAA,CAAoB,qBAAA,EAAsB;AACrE,IAAA,OAAO,aAAa,YAAA,KAAiB,IAAA;AAAA,EACvC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEO,IAAM,sBAAA,GAAyB,OAAO,OAAA,KAAwD;AACnG,EAAA,IAAI,CAAC,uBAAsB,EAAG;AAC5B,IAAA,MAAM,IAAI,MAAM,6CAA6C,CAAA;AAAA,EAC/D;AACA,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,EAAS,UAAU,CAAA;AACxC,EAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAA0B,CAAA,EAAG,IAAI,CAAA,oCAAA,CAAsC,CAAA;AAC9F,EAAA,MAAM,UAAA,GAAa,MAAM,SAAA,CAAU,WAAA,CAAY,MAAA,CAAO;AAAA,IACpD,SAAA,EAAW,qBAAA,CAAsB,QAAA,CAAS,SAAS;AAAA,GACpD,CAAA;AACD,EAAA,IAAI,EAAE,sBAAsB,mBAAA,CAAA,EAAsB;AAChD,IAAA,MAAM,IAAI,MAAM,qCAAqC,CAAA;AAAA,EACvD;AACA,EAAA,MAAM,SAAS,MAAM,QAAA;AAAA,IACnB,GAAG,IAAI,CAAA,mCAAA,CAAA;AAAA,IACP;AAAA,MACE,OAAO,QAAA,CAAS,KAAA;AAAA,MAChB,UAAA,EAAY,oBAAoB,UAAU;AAAA;AAC5C,GACF;AACA,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,MAAM,IAAI,MAAM,8BAA8B,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,OAAO,YAAA,IAAgB,IAAA;AAChC;AAEO,IAAM,0BAAA,GAA6B,OAAO,OAAA,KAA2D;AAC1G,EAAA,IAAI,CAAC,uBAAsB,EAAG;AAC5B,IAAA,MAAM,IAAI,MAAM,6CAA6C,CAAA;AAAA,EAC/D;AACA,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,EAAS,UAAU,CAAA;AACxC,EAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAA0B,CAAA,EAAG,IAAI,CAAA,wCAAA,CAA0C,CAAA;AAClG,EAAA,MAAM,OAAA,GAA4C;AAAA,IAChD,SAAA,EAAW,oBAAA,CAAqB,QAAA,CAAS,SAAS;AAAA,GACpD;AACA,EAAA,IAAI,SAAS,SAAA,EAAW;AACtB,IAAA,OAAA,CAAQ,MAAA,GAAS,WAAA;AAAA,EACnB;AACA,EAAA,MAAM,UAAA,GAAa,MAAM,SAAA,CAAU,WAAA,CAAY,IAAI,OAAO,CAAA;AAC1D,EAAA,IAAI,EAAE,sBAAsB,mBAAA,CAAA,EAAsB;AAChD,IAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,EAClD;AACA,EAAA,OAAO,MAAM,QAAA,CAA2B,CAAA,EAAG,IAAI,CAAA,uCAAA,CAAA,EAA2C;AAAA,IACxF,OAAO,QAAA,CAAS,KAAA;AAAA,IAChB,UAAA,EAAY,oBAAoB,UAAU,CAAA;AAAA,IAC1C,QAAA,EAAU,SAAS,QAAA,IAAY;AAAA,GAChC,CAAA;AACH","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. When BOTH `width` and `height`\n * are known and positive, the resolver emits them as `<img width>`/`<img\n * height>` attributes so the browser reserves layout space and Cumulative\n * Layout Shift (CLS) is eliminated. These are aspect-ratio hints only — CSS\n * (`width:100%; height:auto`) still controls the rendered size. Never fabricate\n * these from a variant's target width: a `height` of 0 means \"unknown\" and\n * MUST be omitted rather than guessed (see the CDN image-map, where older\n * entries carry `height:0`).\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/** 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 variants = CONTEXT_VARIANTS[context] ?? CONTEXT_VARIANTS.content\n if (variants.length === 0) {\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 return {\n imgProps: {\n src: `${basePath}${uuid}${context === 'lightbox' ? '-lg' : '-md'}.webp`, // Lightbox uses lg fallback for max quality\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"]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duffcloudservices/cms-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Shared types and utilities for DCS CMS framework packages",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"//exports": [
|
|
7
|
+
"WORKSPACE consumers resolve SOURCE; PUBLISHED consumers resolve dist (see publishConfig",
|
|
8
|
+
"below, which pnpm substitutes into the packed package.json at publish time). This mirrors",
|
|
9
|
+
"packages/telemetry, which was moved to the same shape by board C-470.",
|
|
10
|
+
"",
|
|
11
|
+
"WHY. dist/ is git-ignored build output, so on a clean checkout it does not exist until",
|
|
12
|
+
"`pnpm run bootstrap` has run. An exports map pointing only at ./dist/* therefore makes this",
|
|
13
|
+
"package UNRESOLVABLE on a fresh tree -- not a type error, an outright resolution failure:",
|
|
14
|
+
"'Failed to resolve entry for package \"@duffcloudservices/cms-core\". The package may have",
|
|
15
|
+
"incorrect main/module/exports specified in its package.json.' Measured on this repo (board",
|
|
16
|
+
"C-475): with dist/ deleted, `pnpm --filter @duffcloudservices/cms run test` went from",
|
|
17
|
+
"32 files / 688 tests passing to 6 files FAILED / 26 passed and only 654 tests counted --",
|
|
18
|
+
"the 34 missing tests died during COLLECTION, which never increments the `Tests` counter.",
|
|
19
|
+
"That is the same silent-green failure mode C-470 found in portal. Pointing the workspace at",
|
|
20
|
+
"src/ removes the build-ordering requirement instead of adding one more step to forget.",
|
|
21
|
+
"",
|
|
22
|
+
"SAFE because every workspace consumer (cms, cms-react, cms-astro, cms-angular) builds with",
|
|
23
|
+
"tsup and type-checks with tsc, both of which compile TS; cms-core stays a normal runtime",
|
|
24
|
+
"`dependencies` entry so tsup keeps it EXTERNAL and the emitted bundles are unchanged.",
|
|
25
|
+
"src/index.ts and src/browser.ts are pure re-exports with no node built-ins. The npm tarball",
|
|
26
|
+
"is unaffected: publishConfig restores the dist-only map, `files` still ships dist only, and",
|
|
27
|
+
"prepublishOnly still builds dist first. Verified by `pnpm pack` inspection, not assumed."
|
|
28
|
+
],
|
|
6
29
|
"exports": {
|
|
7
30
|
".": {
|
|
8
31
|
"types": "./dist/index.d.ts",
|