@duffcloudservices/cms-core 0.4.1 → 0.4.2

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/index.js CHANGED
@@ -314,7 +314,7 @@ async function fetchRuntimeSeo(siteSlug, options = {}) {
314
314
  }
315
315
 
316
316
  // src/responsive-image.ts
317
- var CDN_PATTERN = /^(https?:\/\/files\.[^/]+\/[^/]+\/assets\/(?:[^/]+\/)*)([a-f0-9-]+)\.(\w+)$/;
317
+ var CDN_PATTERN = /^(https?:\/\/files\.[^/]+\/(?:content\/)?[^/]+\/assets\/(?:[^/]+\/)*)([a-f0-9-]+)\.(\w+)$/;
318
318
  var VARIANT_MAP = {
319
319
  thumb: { suffix: "-thumb", width: 150 },
320
320
  sm: { suffix: "-sm", width: 640 },
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"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Content resolution utilities\r\n */\r\n\r\nimport type { ContentConfiguration } from './types'\r\n\r\n/**\r\n * Resolve a text key for a specific page.\r\n * Checks page-specific content first, then falls back to global content.\r\n *\r\n * @param content - The content configuration\r\n * @param page - The page slug\r\n * @param key - The text key to resolve\r\n * @returns The resolved text value, or undefined if not found\r\n */\r\nexport function resolveTextKey(\r\n content: ContentConfiguration,\r\n page: string,\r\n key: string\r\n): string | undefined {\r\n // Check page-specific content first\r\n const pageContent = content.pages?.[page]\r\n if (pageContent && key in pageContent) {\r\n return pageContent[key]\r\n }\r\n\r\n // Fall back to global content\r\n if (content.global && key in content.global) {\r\n return content.global[key]\r\n }\r\n\r\n return undefined\r\n}\r\n\r\n/**\r\n * Get all content for a specific page, merging global and page-specific.\r\n *\r\n * @param content - The content configuration\r\n * @param page - The page slug\r\n * @returns Merged content object (global values overridden by page values)\r\n */\r\nexport function getPageContent(\r\n content: ContentConfiguration,\r\n page: string\r\n): Record<string, string> {\r\n const global = content.global ?? {}\r\n const pageContent = content.pages?.[page] ?? {}\r\n return { ...global, ...pageContent }\r\n}\r\n\r\n/**\r\n * Get only the global content.\r\n *\r\n * @param content - The content configuration\r\n * @returns Global content object\r\n */\r\nexport function getGlobalContent(\r\n content: ContentConfiguration\r\n): Record<string, string> {\r\n return content.global ?? {}\r\n}\r\n","/**\r\n * SEO resolution utilities\r\n */\r\n\r\nimport type {\r\n SeoConfiguration,\r\n ResolvedSeo,\r\n SeoOpenGraphConfig,\r\n SeoTwitterConfig,\r\n} from './types'\r\n\r\n/**\r\n * Resolve SEO configuration for a specific page.\r\n * Merges global defaults with page-specific overrides.\r\n *\r\n * @param seo - The SEO configuration\r\n * @param page - The page slug\r\n * @returns Resolved SEO object with all values filled in\r\n */\r\nexport function resolveSeoForPage(\r\n seo: SeoConfiguration,\r\n page: string\r\n): ResolvedSeo {\r\n const global = seo.global ?? {}\r\n const pageSeo = seo.pages?.[page] ?? {}\r\n\r\n // Resolve title with template\r\n let title = pageSeo.title ?? global.defaultTitle ?? ''\r\n if (title && global.titleTemplate && !pageSeo.noTitleTemplate) {\r\n title = global.titleTemplate.replace('%s', title)\r\n }\r\n\r\n const description = pageSeo.description ?? global.defaultDescription ?? ''\r\n\r\n // Resolve Open Graph\r\n const ogImage =\r\n pageSeo.openGraph?.image ?? global.images?.ogDefault ?? undefined\r\n const openGraph = resolveOpenGraph(pageSeo.openGraph, ogImage, title, description)\r\n\r\n // Resolve Twitter Card\r\n const twitterImage =\r\n pageSeo.twitter?.image ?? global.images?.twitterDefault ?? ogImage\r\n const twitter = resolveTwitterCard(\r\n pageSeo.twitter,\r\n twitterImage,\r\n title,\r\n description,\r\n global.social?.twitter\r\n )\r\n\r\n return {\r\n title,\r\n description,\r\n image: ogImage,\r\n siteName: global.siteName,\r\n siteUrl: global.siteUrl,\r\n locale: global.locale,\r\n canonical: pageSeo.canonical,\r\n robots: pageSeo.robots ?? global.robots,\r\n noIndex: pageSeo.robots?.includes('noindex'),\r\n openGraph,\r\n twitter,\r\n schemas: pageSeo.schemas ?? global.schemas,\r\n alternates: pageSeo.alternates,\r\n }\r\n}\r\n\r\n/**\r\n * Resolve Open Graph configuration\r\n */\r\nfunction resolveOpenGraph(\r\n pageOg: SeoOpenGraphConfig | undefined,\r\n ogImage: string | undefined,\r\n title: string,\r\n description: string\r\n): SeoOpenGraphConfig | undefined {\r\n if (pageOg) {\r\n return {\r\n ...pageOg,\r\n title: pageOg.title ?? title,\r\n description: pageOg.description ?? description,\r\n image: ogImage,\r\n }\r\n }\r\n\r\n if (ogImage) {\r\n return {\r\n title,\r\n description,\r\n image: ogImage,\r\n type: 'website',\r\n }\r\n }\r\n\r\n return undefined\r\n}\r\n\r\n/**\r\n * Resolve Twitter Card configuration\r\n */\r\nfunction resolveTwitterCard(\r\n pageTwitter: SeoTwitterConfig | undefined,\r\n twitterImage: string | undefined,\r\n title: string,\r\n description: string,\r\n globalTwitterHandle: string | undefined\r\n): SeoTwitterConfig | undefined {\r\n if (pageTwitter) {\r\n return {\r\n card: pageTwitter.card ?? 'summary_large_image',\r\n site: pageTwitter.site ?? globalTwitterHandle,\r\n ...pageTwitter,\r\n title: pageTwitter.title ?? title,\r\n description: pageTwitter.description ?? description,\r\n image: twitterImage,\r\n }\r\n }\r\n\r\n if (globalTwitterHandle || twitterImage) {\r\n return {\r\n card: 'summary_large_image',\r\n site: globalTwitterHandle,\r\n title,\r\n description,\r\n image: twitterImage,\r\n }\r\n }\r\n\r\n return undefined\r\n}\r\n\r\n/**\r\n * Meta tag representation for framework-agnostic usage\r\n */\r\nexport interface MetaTag {\r\n name?: string\r\n property?: string\r\n content: string\r\n}\r\n\r\n/**\r\n * Helper to add a meta tag if content exists\r\n */\r\nfunction addTag(\r\n tags: MetaTag[],\r\n content: string | undefined,\r\n attr: { name?: string; property?: string }\r\n): void {\r\n if (content) {\r\n tags.push({ ...attr, content })\r\n }\r\n}\r\n\r\n/**\r\n * Build Open Graph meta tags\r\n */\r\nfunction buildOpenGraphTags(\r\n tags: MetaTag[],\r\n seo: ResolvedSeo\r\n): void {\r\n const og = seo.openGraph\r\n if (!og) return\r\n\r\n addTag(tags, og.title, { property: 'og:title' })\r\n addTag(tags, og.description, { property: 'og:description' })\r\n addTag(tags, og.image, { property: 'og:image' })\r\n addTag(tags, og.imageAlt, { property: 'og:image:alt' })\r\n addTag(tags, og.imageWidth?.toString(), { property: 'og:image:width' })\r\n addTag(tags, og.imageHeight?.toString(), { property: 'og:image:height' })\r\n addTag(tags, og.type, { property: 'og:type' })\r\n addTag(tags, og.url, { property: 'og:url' })\r\n addTag(tags, seo.siteName, { property: 'og:site_name' })\r\n addTag(tags, seo.locale, { property: 'og:locale' })\r\n}\r\n\r\n/**\r\n * Build Twitter Card meta tags\r\n */\r\nfunction buildTwitterTags(\r\n tags: MetaTag[],\r\n seo: ResolvedSeo\r\n): void {\r\n const tw = seo.twitter\r\n if (!tw) return\r\n\r\n addTag(tags, tw.card, { name: 'twitter:card' })\r\n addTag(tags, tw.site ? `@${tw.site}` : undefined, { name: 'twitter:site' })\r\n addTag(tags, tw.creator ? `@${tw.creator}` : undefined, { name: 'twitter:creator' })\r\n addTag(tags, tw.title, { name: 'twitter:title' })\r\n addTag(tags, tw.description, { name: 'twitter:description' })\r\n addTag(tags, tw.image, { name: 'twitter:image' })\r\n addTag(tags, tw.imageAlt, { name: 'twitter:image:alt' })\r\n}\r\n\r\n/**\r\n * Build an array of meta tags from resolved SEO.\r\n * Useful for frameworks that need to manually set meta tags.\r\n *\r\n * @param seo - Resolved SEO object\r\n * @returns Array of meta tag objects\r\n */\r\nexport function buildMetaTags(seo: ResolvedSeo): MetaTag[] {\r\n const tags: MetaTag[] = []\r\n\r\n // Basic meta\r\n addTag(tags, seo.description, { name: 'description' })\r\n addTag(tags, seo.robots, { name: 'robots' })\r\n\r\n // Open Graph\r\n buildOpenGraphTags(tags, seo)\r\n\r\n // Twitter Card\r\n buildTwitterTags(tags, seo)\r\n\r\n return tags\r\n}\r\n","/**\r\n * YAML file loaders for DCS configuration files\r\n */\r\n\r\nimport fs from 'node:fs'\r\nimport path from 'node:path'\r\nimport yaml from 'js-yaml'\r\nimport type {\r\n ContentConfiguration,\r\n SeoConfiguration,\r\n PagesConfiguration,\r\n UnifiedConfiguration,\r\n} from './types'\r\n\r\n/**\r\n * Load and parse .dcs/content.yaml\r\n *\r\n * @param filePath - Path to content.yaml (absolute or relative to cwd)\r\n * @returns Parsed content configuration\r\n * @throws Error if file not found or parse fails\r\n */\r\nexport async function loadContentYaml(\r\n filePath: string\r\n): Promise<ContentConfiguration> {\r\n const absolutePath = path.isAbsolute(filePath)\r\n ? filePath\r\n : path.resolve(process.cwd(), filePath)\r\n\r\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\r\n const content = yaml.load(fileContent) as ContentConfiguration\r\n\r\n // Ensure required fields exist\r\n if (!content.version) {\r\n content.version = 1\r\n }\r\n if (!content.global) {\r\n content.global = {}\r\n }\r\n if (!content.pages) {\r\n content.pages = {}\r\n }\r\n\r\n return content\r\n}\r\n\r\n/**\r\n * Load and parse .dcs/seo.yaml\r\n *\r\n * @param filePath - Path to seo.yaml (absolute or relative to cwd)\r\n * @returns Parsed SEO configuration\r\n * @throws Error if file not found or parse fails\r\n */\r\nexport async function loadSeoYaml(filePath: string): Promise<SeoConfiguration> {\r\n const absolutePath = path.isAbsolute(filePath)\r\n ? filePath\r\n : path.resolve(process.cwd(), filePath)\r\n\r\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\r\n const seo = yaml.load(fileContent) as SeoConfiguration\r\n\r\n // Ensure required fields exist\r\n if (!seo.version) {\r\n seo.version = 1\r\n }\r\n\r\n return seo\r\n}\r\n\r\n/**\r\n * Load and parse .dcs/pages.yaml\r\n *\r\n * @param filePath - Path to pages.yaml (absolute or relative to cwd)\r\n * @returns Parsed pages configuration\r\n * @throws Error if file not found or parse fails\r\n */\r\nexport async function loadPagesYaml(\r\n filePath: string\r\n): Promise<PagesConfiguration> {\r\n const absolutePath = path.isAbsolute(filePath)\r\n ? filePath\r\n : path.resolve(process.cwd(), filePath)\r\n\r\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\r\n const pages = yaml.load(fileContent) as PagesConfiguration\r\n\r\n // Ensure required fields exist\r\n if (!pages.version) {\r\n pages.version = 3\r\n }\r\n if (!pages.pages) {\r\n pages.pages = []\r\n }\r\n\r\n return pages\r\n}\r\n\r\n/**\r\n * Try to find a DCS config file in common locations\r\n *\r\n * @param filename - The config file name (e.g., 'content.yaml')\r\n * @param projectRoot - The project root directory\r\n * @returns The found path, or undefined if not found\r\n */\r\nexport function findDcsConfigFile(\r\n filename: string,\r\n projectRoot: string\r\n): string | undefined {\r\n const possiblePaths = [\r\n path.resolve(projectRoot, '.dcs', filename),\r\n path.resolve(projectRoot, '..', '.dcs', filename), // For VitePress docs folder\r\n path.resolve(process.cwd(), '.dcs', filename),\r\n ]\r\n\r\n for (const testPath of possiblePaths) {\r\n if (fs.existsSync(testPath)) {\r\n return testPath\r\n }\r\n }\r\n\r\n return undefined\r\n}\r\n\r\n// ─── Unified Config Loader ──────────────────────────────────────\r\n\r\n/**\r\n * Result of loading DCS configuration, whether from unified config.yaml\r\n * or individual split files.\r\n */\r\nexport interface DcsConfigResult {\r\n /** Resolved content configuration */\r\n content: ContentConfiguration\r\n /** Resolved SEO configuration */\r\n seo: SeoConfiguration\r\n /** Resolved pages configuration */\r\n pages: PagesConfiguration\r\n /** Whether the configuration was loaded from a unified config.yaml */\r\n isUnified: boolean\r\n /** The source file path(s) that were loaded */\r\n sources: string[]\r\n}\r\n\r\n/**\r\n * Load DCS configuration from either a unified `.dcs/config.yaml` (version 2)\r\n * or fall back to individual split files (content.yaml, seo.yaml, pages.yaml).\r\n *\r\n * The unified format is preferred when `.dcs/config.yaml` exists and has `version: 2`.\r\n * This provides a single source of truth for all site configuration.\r\n *\r\n * @param projectRoot - The project root directory containing `.dcs/`\r\n * @returns Resolved configuration from all sources\r\n *\r\n * @example\r\n * ```typescript\r\n * const config = await loadConfigYaml('/path/to/site')\r\n * console.log(config.content.pages.home)\r\n * console.log(config.seo.global?.siteName)\r\n * console.log(config.pages.pages)\r\n * console.log(config.isUnified) // true if loaded from config.yaml\r\n * ```\r\n */\r\nexport async function loadConfigYaml(projectRoot: string): Promise<DcsConfigResult> {\r\n // Try unified config.yaml first\r\n const unifiedPath = findDcsConfigFile('config.yaml', projectRoot)\r\n if (unifiedPath) {\r\n try {\r\n const fileContent = fs.readFileSync(unifiedPath, 'utf8')\r\n const raw = yaml.load(fileContent) as Record<string, unknown>\r\n\r\n // Only treat as unified if version === 2\r\n if (raw && raw.version === 2) {\r\n const unified = raw as unknown as UnifiedConfiguration\r\n return {\r\n content: unified.content ?? { version: 1, global: {}, pages: {} },\r\n seo: unified.seo ?? { version: 1 },\r\n pages: {\r\n version: 3,\r\n siteSlug: unified.siteSlug ?? unified.site?.slug ?? '',\r\n pages: unified.pages ?? [],\r\n },\r\n isUnified: true,\r\n sources: [unifiedPath],\r\n }\r\n }\r\n } catch {\r\n // config.yaml exists but failed to parse — fall through to split files\r\n }\r\n }\r\n\r\n // Fall back to individual split files\r\n const sources: string[] = []\r\n\r\n let content: ContentConfiguration = { version: 1, global: {}, pages: {} }\r\n const contentPath = findDcsConfigFile('content.yaml', projectRoot)\r\n if (contentPath) {\r\n content = await loadContentYaml(contentPath)\r\n sources.push(contentPath)\r\n }\r\n\r\n let seo: SeoConfiguration = { version: 1 }\r\n const seoPath = findDcsConfigFile('seo.yaml', projectRoot)\r\n if (seoPath) {\r\n seo = await loadSeoYaml(seoPath)\r\n sources.push(seoPath)\r\n }\r\n\r\n let pages: PagesConfiguration = { version: 3, siteSlug: '', pages: [] }\r\n const pagesPath = findDcsConfigFile('pages.yaml', projectRoot)\r\n if (pagesPath) {\r\n pages = await loadPagesYaml(pagesPath)\r\n sources.push(pagesPath)\r\n }\r\n\r\n return {\r\n content,\r\n seo,\r\n pages,\r\n isUnified: false,\r\n sources,\r\n }\r\n}\r\n","/**\r\n * Runtime content fetching for premium tier customers\r\n */\r\n\r\nimport type { ContentConfiguration, SeoConfiguration } from './types'\r\n\r\n/**\r\n * Options for runtime fetch operations\r\n */\r\nexport interface FetchOptions {\r\n /** Base URL for the DCS API */\r\n apiBaseUrl?: string\r\n /** Timeout in milliseconds (default: 5000) */\r\n timeout?: number\r\n /** Custom headers to include */\r\n headers?: Record<string, string>\r\n}\r\n\r\nconst DEFAULT_API_URL = 'https://api.duffcloudservices.com'\r\nconst DEFAULT_TIMEOUT = 5000\r\n\r\n/**\r\n * Fetch runtime content from the DCS API.\r\n * This is a premium tier feature - returns 403 for non-premium sites.\r\n *\r\n * @param siteSlug - The site's slug identifier\r\n * @param options - Fetch options\r\n * @returns Content configuration or null if fetch fails\r\n */\r\nexport async function fetchRuntimeContent(\r\n siteSlug: string,\r\n options: FetchOptions = {}\r\n): Promise<ContentConfiguration | null> {\r\n const { apiBaseUrl = DEFAULT_API_URL, timeout = DEFAULT_TIMEOUT } = options\r\n\r\n const controller = new AbortController()\r\n const timeoutId = setTimeout(() => controller.abort(), timeout)\r\n\r\n try {\r\n const response = await fetch(\r\n `${apiBaseUrl}/portal/sites/${siteSlug}/content/runtime`,\r\n {\r\n method: 'GET',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...options.headers,\r\n },\r\n signal: controller.signal,\r\n }\r\n )\r\n\r\n if (!response.ok) {\r\n if (response.status === 403) {\r\n console.warn(\r\n '[DCS] Runtime content requires premium tier. Using build-time content.'\r\n )\r\n }\r\n return null\r\n }\r\n\r\n return (await response.json()) as ContentConfiguration\r\n } catch (error) {\r\n if (error instanceof Error && error.name === 'AbortError') {\r\n console.warn('[DCS] Runtime content fetch timed out')\r\n } else {\r\n console.warn('[DCS] Runtime content fetch failed:', error)\r\n }\r\n return null\r\n } finally {\r\n clearTimeout(timeoutId)\r\n }\r\n}\r\n\r\n/**\r\n * Fetch runtime SEO configuration from the DCS API.\r\n * This is a premium tier feature - returns 403 for non-premium sites.\r\n *\r\n * @param siteSlug - The site's slug identifier\r\n * @param options - Fetch options\r\n * @returns SEO configuration or null if fetch fails\r\n */\r\nexport async function fetchRuntimeSeo(\r\n siteSlug: string,\r\n options: FetchOptions = {}\r\n): Promise<SeoConfiguration | null> {\r\n const { apiBaseUrl = DEFAULT_API_URL, timeout = DEFAULT_TIMEOUT } = options\r\n\r\n const controller = new AbortController()\r\n const timeoutId = setTimeout(() => controller.abort(), timeout)\r\n\r\n try {\r\n const response = await fetch(\r\n `${apiBaseUrl}/portal/sites/${siteSlug}/seo/runtime`,\r\n {\r\n method: 'GET',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...options.headers,\r\n },\r\n signal: controller.signal,\r\n }\r\n )\r\n\r\n if (!response.ok) {\r\n if (response.status === 403) {\r\n console.warn(\r\n '[DCS] Runtime SEO requires premium tier. Using build-time SEO.'\r\n )\r\n }\r\n return null\r\n }\r\n\r\n return (await response.json()) as SeoConfiguration\r\n } catch (error) {\r\n if (error instanceof Error && error.name === 'AbortError') {\r\n console.warn('[DCS] Runtime SEO fetch timed out')\r\n } else {\r\n console.warn('[DCS] Runtime SEO fetch failed:', error)\r\n }\r\n return null\r\n } finally {\r\n clearTimeout(timeoutId)\r\n }\r\n}\r\n","/**\r\n * Pure functions for resolving responsive image variants from DCS CDN URLs.\r\n *\r\n * The DCS imaging pipeline generates WebP variants during upload:\r\n * - thumb (150px) — grid thumbnails\r\n * - sm (640px) — mobile displays\r\n * - md (1024px) — tablet / blog body\r\n * - lg (1920px) — desktop / hero sections\r\n * - og (1200×630) — social sharing (fixed aspect)\r\n *\r\n * This module provides framework-agnostic resolution logic that\r\n * framework packages (@duffcloudservices/cms, cms-react, etc.) wrap\r\n * in their respective APIs.\r\n *\r\n * @module responsive-image\r\n */\r\n\r\n/** Sizing context that controls which variants are included in the srcset. */\r\nexport type ImageContext = 'hero' | 'card' | 'thumbnail' | 'content' | 'og'\r\n\r\n/** Options for resolving a responsive image. */\r\nexport interface ResponsiveImageOptions {\r\n /** Source URL — original CDN URL or local path. */\r\n src: string\r\n /** Alt text for accessibility. */\r\n alt: string\r\n /** Sizing context — determines which variants to include. */\r\n context?: ImageContext\r\n /** Optional explicit width hints for the browser `sizes` attribute. */\r\n sizes?: string\r\n /** Skip variant resolution and return the original URL only. */\r\n original?: boolean\r\n}\r\n\r\n/** A single `<source>` entry for a `<picture>` element. */\r\nexport interface ResponsiveSource {\r\n srcset: string\r\n type: string\r\n sizes?: string\r\n}\r\n\r\n/** Resolved image properties for rendering. */\r\nexport interface ResponsiveImageResult {\r\n /** Props spreadable onto an `<img>` element. */\r\n imgProps: {\r\n src: string\r\n alt: string\r\n loading: 'lazy' | 'eager'\r\n decoding: 'async' | 'auto'\r\n }\r\n /** WebP `<source>` entries for a `<picture>` element. */\r\n sources: ResponsiveSource[]\r\n /** Whether responsive variants were detected. */\r\n hasVariants: boolean\r\n}\r\n\r\n/**\r\n * Regex that matches DCS CDN asset URLs and captures the base path, UUID, and extension.\r\n *\r\n * Group 1: base path including trailing slash (e.g. `https://files.duffcloudservices.com/kept/assets/hero/`)\r\n * Group 2: UUID (e.g. `abc-123`)\r\n * Group 3: file extension (e.g. `jpg`)\r\n */\r\nconst CDN_PATTERN = /^(https?:\\/\\/files\\.[^/]+\\/[^/]+\\/assets\\/(?:[^/]+\\/)*)([a-f0-9-]+)\\.(\\w+)$/\r\n\r\n/** Variant definitions mapping suffix → pixel width. */\r\nconst VARIANT_MAP: Record<string, { suffix: string; width: number }> = {\r\n thumb: { suffix: '-thumb', width: 150 },\r\n sm: { suffix: '-sm', width: 640 },\r\n md: { suffix: '-md', width: 1024 },\r\n lg: { suffix: '-lg', width: 1920 },\r\n}\r\n\r\n/** Which variants each context includes (smallest → largest). */\r\nconst CONTEXT_VARIANTS: Record<ImageContext, string[]> = {\r\n hero: ['sm', 'md', 'lg'],\r\n card: ['thumb', 'sm', 'md'],\r\n thumbnail: ['thumb', 'sm'],\r\n content: ['sm', 'md', 'lg'],\r\n og: [], // OG uses the dedicated -og variant, not srcset\r\n}\r\n\r\n/** Default `sizes` attribute per context. */\r\nconst DEFAULT_SIZES: Record<ImageContext, string> = {\r\n hero: '100vw',\r\n card: '(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw',\r\n thumbnail: '150px',\r\n content: '(max-width: 1024px) 100vw, 1024px',\r\n og: '',\r\n}\r\n\r\n/**\r\n * Resolves responsive image metadata from a DCS CDN URL.\r\n *\r\n * If the URL matches the CDN asset pattern, a srcset of WebP variants\r\n * is generated based on the sizing `context`. Non-CDN URLs are returned\r\n * as-is with no variants.\r\n *\r\n * @example\r\n * ```ts\r\n * const result = resolveResponsiveImage({\r\n * src: 'https://files.duffcloudservices.com/kept/assets/hero/abc-123.jpg',\r\n * alt: 'Hero image',\r\n * context: 'hero',\r\n * })\r\n * // result.hasVariants === true\r\n * // result.sources[0].srcset contains sm, md, lg WebP sizes\r\n * // result.imgProps.loading === 'eager' (hero context)\r\n * ```\r\n */\r\nexport function resolveResponsiveImage(options: ResponsiveImageOptions): ResponsiveImageResult {\r\n const { src, alt, context = 'content', sizes, original } = options\r\n\r\n const fallback: ResponsiveImageResult = {\r\n imgProps: {\r\n src,\r\n alt,\r\n loading: context === 'hero' ? 'eager' : 'lazy',\r\n decoding: context === 'hero' ? 'auto' : 'async',\r\n },\r\n sources: [],\r\n hasVariants: false,\r\n }\r\n\r\n if (original) {\r\n return fallback\r\n }\r\n\r\n const match = src.match(CDN_PATTERN)\r\n if (!match) {\r\n return fallback\r\n }\r\n\r\n const [, basePath, uuid] = match\r\n\r\n const variants = CONTEXT_VARIANTS[context] ?? CONTEXT_VARIANTS.content\r\n if (variants.length === 0) {\r\n return fallback\r\n }\r\n\r\n const srcsetParts = variants.map((key) => {\r\n const v = VARIANT_MAP[key]\r\n return `${basePath}${uuid}${v.suffix}.webp ${v.width}w`\r\n })\r\n\r\n const resolvedSizes = sizes ?? DEFAULT_SIZES[context] ?? DEFAULT_SIZES.content\r\n\r\n return {\r\n imgProps: {\r\n src: `${basePath}${uuid}-md.webp`, // Fallback to md variant\r\n alt,\r\n loading: context === 'hero' ? 'eager' : 'lazy',\r\n decoding: context === 'hero' ? 'auto' : 'async',\r\n },\r\n sources: [\r\n {\r\n srcset: srcsetParts.join(', '),\r\n type: 'image/webp',\r\n ...(resolvedSizes ? { sizes: resolvedSizes } : {}),\r\n },\r\n ],\r\n hasVariants: true,\r\n }\r\n}\r\n\r\n/**\r\n * Tests whether a URL matches the DCS CDN asset pattern.\r\n *\r\n * Useful when rendering a mix of CDN-hosted and local images\r\n * to decide whether responsive treatment is applicable.\r\n */\r\nexport function isCdnAssetUrl(url: string): boolean {\r\n return CDN_PATTERN.test(url)\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/content.ts","../src/seo.ts","../src/loaders.ts","../src/fetch.ts","../src/responsive-image.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Content resolution utilities\r\n */\r\n\r\nimport type { ContentConfiguration } from './types'\r\n\r\n/**\r\n * Resolve a text key for a specific page.\r\n * Checks page-specific content first, then falls back to global content.\r\n *\r\n * @param content - The content configuration\r\n * @param page - The page slug\r\n * @param key - The text key to resolve\r\n * @returns The resolved text value, or undefined if not found\r\n */\r\nexport function resolveTextKey(\r\n content: ContentConfiguration,\r\n page: string,\r\n key: string\r\n): string | undefined {\r\n // Check page-specific content first\r\n const pageContent = content.pages?.[page]\r\n if (pageContent && key in pageContent) {\r\n return pageContent[key]\r\n }\r\n\r\n // Fall back to global content\r\n if (content.global && key in content.global) {\r\n return content.global[key]\r\n }\r\n\r\n return undefined\r\n}\r\n\r\n/**\r\n * Get all content for a specific page, merging global and page-specific.\r\n *\r\n * @param content - The content configuration\r\n * @param page - The page slug\r\n * @returns Merged content object (global values overridden by page values)\r\n */\r\nexport function getPageContent(\r\n content: ContentConfiguration,\r\n page: string\r\n): Record<string, string> {\r\n const global = content.global ?? {}\r\n const pageContent = content.pages?.[page] ?? {}\r\n return { ...global, ...pageContent }\r\n}\r\n\r\n/**\r\n * Get only the global content.\r\n *\r\n * @param content - The content configuration\r\n * @returns Global content object\r\n */\r\nexport function getGlobalContent(\r\n content: ContentConfiguration\r\n): Record<string, string> {\r\n return content.global ?? {}\r\n}\r\n","/**\r\n * SEO resolution utilities\r\n */\r\n\r\nimport type {\r\n SeoConfiguration,\r\n ResolvedSeo,\r\n SeoOpenGraphConfig,\r\n SeoTwitterConfig,\r\n} from './types'\r\n\r\n/**\r\n * Resolve SEO configuration for a specific page.\r\n * Merges global defaults with page-specific overrides.\r\n *\r\n * @param seo - The SEO configuration\r\n * @param page - The page slug\r\n * @returns Resolved SEO object with all values filled in\r\n */\r\nexport function resolveSeoForPage(\r\n seo: SeoConfiguration,\r\n page: string\r\n): ResolvedSeo {\r\n const global = seo.global ?? {}\r\n const pageSeo = seo.pages?.[page] ?? {}\r\n\r\n // Resolve title with template\r\n let title = pageSeo.title ?? global.defaultTitle ?? ''\r\n if (title && global.titleTemplate && !pageSeo.noTitleTemplate) {\r\n title = global.titleTemplate.replace('%s', title)\r\n }\r\n\r\n const description = pageSeo.description ?? global.defaultDescription ?? ''\r\n\r\n // Resolve Open Graph\r\n const ogImage =\r\n pageSeo.openGraph?.image ?? global.images?.ogDefault ?? undefined\r\n const openGraph = resolveOpenGraph(pageSeo.openGraph, ogImage, title, description)\r\n\r\n // Resolve Twitter Card\r\n const twitterImage =\r\n pageSeo.twitter?.image ?? global.images?.twitterDefault ?? ogImage\r\n const twitter = resolveTwitterCard(\r\n pageSeo.twitter,\r\n twitterImage,\r\n title,\r\n description,\r\n global.social?.twitter\r\n )\r\n\r\n return {\r\n title,\r\n description,\r\n image: ogImage,\r\n siteName: global.siteName,\r\n siteUrl: global.siteUrl,\r\n locale: global.locale,\r\n canonical: pageSeo.canonical,\r\n robots: pageSeo.robots ?? global.robots,\r\n noIndex: pageSeo.robots?.includes('noindex'),\r\n openGraph,\r\n twitter,\r\n schemas: pageSeo.schemas ?? global.schemas,\r\n alternates: pageSeo.alternates,\r\n }\r\n}\r\n\r\n/**\r\n * Resolve Open Graph configuration\r\n */\r\nfunction resolveOpenGraph(\r\n pageOg: SeoOpenGraphConfig | undefined,\r\n ogImage: string | undefined,\r\n title: string,\r\n description: string\r\n): SeoOpenGraphConfig | undefined {\r\n if (pageOg) {\r\n return {\r\n ...pageOg,\r\n title: pageOg.title ?? title,\r\n description: pageOg.description ?? description,\r\n image: ogImage,\r\n }\r\n }\r\n\r\n if (ogImage) {\r\n return {\r\n title,\r\n description,\r\n image: ogImage,\r\n type: 'website',\r\n }\r\n }\r\n\r\n return undefined\r\n}\r\n\r\n/**\r\n * Resolve Twitter Card configuration\r\n */\r\nfunction resolveTwitterCard(\r\n pageTwitter: SeoTwitterConfig | undefined,\r\n twitterImage: string | undefined,\r\n title: string,\r\n description: string,\r\n globalTwitterHandle: string | undefined\r\n): SeoTwitterConfig | undefined {\r\n if (pageTwitter) {\r\n return {\r\n card: pageTwitter.card ?? 'summary_large_image',\r\n site: pageTwitter.site ?? globalTwitterHandle,\r\n ...pageTwitter,\r\n title: pageTwitter.title ?? title,\r\n description: pageTwitter.description ?? description,\r\n image: twitterImage,\r\n }\r\n }\r\n\r\n if (globalTwitterHandle || twitterImage) {\r\n return {\r\n card: 'summary_large_image',\r\n site: globalTwitterHandle,\r\n title,\r\n description,\r\n image: twitterImage,\r\n }\r\n }\r\n\r\n return undefined\r\n}\r\n\r\n/**\r\n * Meta tag representation for framework-agnostic usage\r\n */\r\nexport interface MetaTag {\r\n name?: string\r\n property?: string\r\n content: string\r\n}\r\n\r\n/**\r\n * Helper to add a meta tag if content exists\r\n */\r\nfunction addTag(\r\n tags: MetaTag[],\r\n content: string | undefined,\r\n attr: { name?: string; property?: string }\r\n): void {\r\n if (content) {\r\n tags.push({ ...attr, content })\r\n }\r\n}\r\n\r\n/**\r\n * Build Open Graph meta tags\r\n */\r\nfunction buildOpenGraphTags(\r\n tags: MetaTag[],\r\n seo: ResolvedSeo\r\n): void {\r\n const og = seo.openGraph\r\n if (!og) return\r\n\r\n addTag(tags, og.title, { property: 'og:title' })\r\n addTag(tags, og.description, { property: 'og:description' })\r\n addTag(tags, og.image, { property: 'og:image' })\r\n addTag(tags, og.imageAlt, { property: 'og:image:alt' })\r\n addTag(tags, og.imageWidth?.toString(), { property: 'og:image:width' })\r\n addTag(tags, og.imageHeight?.toString(), { property: 'og:image:height' })\r\n addTag(tags, og.type, { property: 'og:type' })\r\n addTag(tags, og.url, { property: 'og:url' })\r\n addTag(tags, seo.siteName, { property: 'og:site_name' })\r\n addTag(tags, seo.locale, { property: 'og:locale' })\r\n}\r\n\r\n/**\r\n * Build Twitter Card meta tags\r\n */\r\nfunction buildTwitterTags(\r\n tags: MetaTag[],\r\n seo: ResolvedSeo\r\n): void {\r\n const tw = seo.twitter\r\n if (!tw) return\r\n\r\n addTag(tags, tw.card, { name: 'twitter:card' })\r\n addTag(tags, tw.site ? `@${tw.site}` : undefined, { name: 'twitter:site' })\r\n addTag(tags, tw.creator ? `@${tw.creator}` : undefined, { name: 'twitter:creator' })\r\n addTag(tags, tw.title, { name: 'twitter:title' })\r\n addTag(tags, tw.description, { name: 'twitter:description' })\r\n addTag(tags, tw.image, { name: 'twitter:image' })\r\n addTag(tags, tw.imageAlt, { name: 'twitter:image:alt' })\r\n}\r\n\r\n/**\r\n * Build an array of meta tags from resolved SEO.\r\n * Useful for frameworks that need to manually set meta tags.\r\n *\r\n * @param seo - Resolved SEO object\r\n * @returns Array of meta tag objects\r\n */\r\nexport function buildMetaTags(seo: ResolvedSeo): MetaTag[] {\r\n const tags: MetaTag[] = []\r\n\r\n // Basic meta\r\n addTag(tags, seo.description, { name: 'description' })\r\n addTag(tags, seo.robots, { name: 'robots' })\r\n\r\n // Open Graph\r\n buildOpenGraphTags(tags, seo)\r\n\r\n // Twitter Card\r\n buildTwitterTags(tags, seo)\r\n\r\n return tags\r\n}\r\n","/**\r\n * YAML file loaders for DCS configuration files\r\n */\r\n\r\nimport fs from 'node:fs'\r\nimport path from 'node:path'\r\nimport yaml from 'js-yaml'\r\nimport type {\r\n ContentConfiguration,\r\n SeoConfiguration,\r\n PagesConfiguration,\r\n UnifiedConfiguration,\r\n} from './types'\r\n\r\n/**\r\n * Load and parse .dcs/content.yaml\r\n *\r\n * @param filePath - Path to content.yaml (absolute or relative to cwd)\r\n * @returns Parsed content configuration\r\n * @throws Error if file not found or parse fails\r\n */\r\nexport async function loadContentYaml(\r\n filePath: string\r\n): Promise<ContentConfiguration> {\r\n const absolutePath = path.isAbsolute(filePath)\r\n ? filePath\r\n : path.resolve(process.cwd(), filePath)\r\n\r\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\r\n const content = yaml.load(fileContent) as ContentConfiguration\r\n\r\n // Ensure required fields exist\r\n if (!content.version) {\r\n content.version = 1\r\n }\r\n if (!content.global) {\r\n content.global = {}\r\n }\r\n if (!content.pages) {\r\n content.pages = {}\r\n }\r\n\r\n return content\r\n}\r\n\r\n/**\r\n * Load and parse .dcs/seo.yaml\r\n *\r\n * @param filePath - Path to seo.yaml (absolute or relative to cwd)\r\n * @returns Parsed SEO configuration\r\n * @throws Error if file not found or parse fails\r\n */\r\nexport async function loadSeoYaml(filePath: string): Promise<SeoConfiguration> {\r\n const absolutePath = path.isAbsolute(filePath)\r\n ? filePath\r\n : path.resolve(process.cwd(), filePath)\r\n\r\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\r\n const seo = yaml.load(fileContent) as SeoConfiguration\r\n\r\n // Ensure required fields exist\r\n if (!seo.version) {\r\n seo.version = 1\r\n }\r\n\r\n return seo\r\n}\r\n\r\n/**\r\n * Load and parse .dcs/pages.yaml\r\n *\r\n * @param filePath - Path to pages.yaml (absolute or relative to cwd)\r\n * @returns Parsed pages configuration\r\n * @throws Error if file not found or parse fails\r\n */\r\nexport async function loadPagesYaml(\r\n filePath: string\r\n): Promise<PagesConfiguration> {\r\n const absolutePath = path.isAbsolute(filePath)\r\n ? filePath\r\n : path.resolve(process.cwd(), filePath)\r\n\r\n const fileContent = fs.readFileSync(absolutePath, 'utf8')\r\n const pages = yaml.load(fileContent) as PagesConfiguration\r\n\r\n // Ensure required fields exist\r\n if (!pages.version) {\r\n pages.version = 3\r\n }\r\n if (!pages.pages) {\r\n pages.pages = []\r\n }\r\n\r\n return pages\r\n}\r\n\r\n/**\r\n * Try to find a DCS config file in common locations\r\n *\r\n * @param filename - The config file name (e.g., 'content.yaml')\r\n * @param projectRoot - The project root directory\r\n * @returns The found path, or undefined if not found\r\n */\r\nexport function findDcsConfigFile(\r\n filename: string,\r\n projectRoot: string\r\n): string | undefined {\r\n const possiblePaths = [\r\n path.resolve(projectRoot, '.dcs', filename),\r\n path.resolve(projectRoot, '..', '.dcs', filename), // For VitePress docs folder\r\n path.resolve(process.cwd(), '.dcs', filename),\r\n ]\r\n\r\n for (const testPath of possiblePaths) {\r\n if (fs.existsSync(testPath)) {\r\n return testPath\r\n }\r\n }\r\n\r\n return undefined\r\n}\r\n\r\n// ─── Unified Config Loader ──────────────────────────────────────\r\n\r\n/**\r\n * Result of loading DCS configuration, whether from unified config.yaml\r\n * or individual split files.\r\n */\r\nexport interface DcsConfigResult {\r\n /** Resolved content configuration */\r\n content: ContentConfiguration\r\n /** Resolved SEO configuration */\r\n seo: SeoConfiguration\r\n /** Resolved pages configuration */\r\n pages: PagesConfiguration\r\n /** Whether the configuration was loaded from a unified config.yaml */\r\n isUnified: boolean\r\n /** The source file path(s) that were loaded */\r\n sources: string[]\r\n}\r\n\r\n/**\r\n * Load DCS configuration from either a unified `.dcs/config.yaml` (version 2)\r\n * or fall back to individual split files (content.yaml, seo.yaml, pages.yaml).\r\n *\r\n * The unified format is preferred when `.dcs/config.yaml` exists and has `version: 2`.\r\n * This provides a single source of truth for all site configuration.\r\n *\r\n * @param projectRoot - The project root directory containing `.dcs/`\r\n * @returns Resolved configuration from all sources\r\n *\r\n * @example\r\n * ```typescript\r\n * const config = await loadConfigYaml('/path/to/site')\r\n * console.log(config.content.pages.home)\r\n * console.log(config.seo.global?.siteName)\r\n * console.log(config.pages.pages)\r\n * console.log(config.isUnified) // true if loaded from config.yaml\r\n * ```\r\n */\r\nexport async function loadConfigYaml(projectRoot: string): Promise<DcsConfigResult> {\r\n // Try unified config.yaml first\r\n const unifiedPath = findDcsConfigFile('config.yaml', projectRoot)\r\n if (unifiedPath) {\r\n try {\r\n const fileContent = fs.readFileSync(unifiedPath, 'utf8')\r\n const raw = yaml.load(fileContent) as Record<string, unknown>\r\n\r\n // Only treat as unified if version === 2\r\n if (raw && raw.version === 2) {\r\n const unified = raw as unknown as UnifiedConfiguration\r\n return {\r\n content: unified.content ?? { version: 1, global: {}, pages: {} },\r\n seo: unified.seo ?? { version: 1 },\r\n pages: {\r\n version: 3,\r\n siteSlug: unified.siteSlug ?? unified.site?.slug ?? '',\r\n pages: unified.pages ?? [],\r\n },\r\n isUnified: true,\r\n sources: [unifiedPath],\r\n }\r\n }\r\n } catch {\r\n // config.yaml exists but failed to parse — fall through to split files\r\n }\r\n }\r\n\r\n // Fall back to individual split files\r\n const sources: string[] = []\r\n\r\n let content: ContentConfiguration = { version: 1, global: {}, pages: {} }\r\n const contentPath = findDcsConfigFile('content.yaml', projectRoot)\r\n if (contentPath) {\r\n content = await loadContentYaml(contentPath)\r\n sources.push(contentPath)\r\n }\r\n\r\n let seo: SeoConfiguration = { version: 1 }\r\n const seoPath = findDcsConfigFile('seo.yaml', projectRoot)\r\n if (seoPath) {\r\n seo = await loadSeoYaml(seoPath)\r\n sources.push(seoPath)\r\n }\r\n\r\n let pages: PagesConfiguration = { version: 3, siteSlug: '', pages: [] }\r\n const pagesPath = findDcsConfigFile('pages.yaml', projectRoot)\r\n if (pagesPath) {\r\n pages = await loadPagesYaml(pagesPath)\r\n sources.push(pagesPath)\r\n }\r\n\r\n return {\r\n content,\r\n seo,\r\n pages,\r\n isUnified: false,\r\n sources,\r\n }\r\n}\r\n","/**\r\n * Runtime content fetching for premium tier customers\r\n */\r\n\r\nimport type { ContentConfiguration, SeoConfiguration } from './types'\r\n\r\n/**\r\n * Options for runtime fetch operations\r\n */\r\nexport interface FetchOptions {\r\n /** Base URL for the DCS API */\r\n apiBaseUrl?: string\r\n /** Timeout in milliseconds (default: 5000) */\r\n timeout?: number\r\n /** Custom headers to include */\r\n headers?: Record<string, string>\r\n}\r\n\r\nconst DEFAULT_API_URL = 'https://api.duffcloudservices.com'\r\nconst DEFAULT_TIMEOUT = 5000\r\n\r\n/**\r\n * Fetch runtime content from the DCS API.\r\n * This is a premium tier feature - returns 403 for non-premium sites.\r\n *\r\n * @param siteSlug - The site's slug identifier\r\n * @param options - Fetch options\r\n * @returns Content configuration or null if fetch fails\r\n */\r\nexport async function fetchRuntimeContent(\r\n siteSlug: string,\r\n options: FetchOptions = {}\r\n): Promise<ContentConfiguration | null> {\r\n const { apiBaseUrl = DEFAULT_API_URL, timeout = DEFAULT_TIMEOUT } = options\r\n\r\n const controller = new AbortController()\r\n const timeoutId = setTimeout(() => controller.abort(), timeout)\r\n\r\n try {\r\n const response = await fetch(\r\n `${apiBaseUrl}/portal/sites/${siteSlug}/content/runtime`,\r\n {\r\n method: 'GET',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...options.headers,\r\n },\r\n signal: controller.signal,\r\n }\r\n )\r\n\r\n if (!response.ok) {\r\n if (response.status === 403) {\r\n console.warn(\r\n '[DCS] Runtime content requires premium tier. Using build-time content.'\r\n )\r\n }\r\n return null\r\n }\r\n\r\n return (await response.json()) as ContentConfiguration\r\n } catch (error) {\r\n if (error instanceof Error && error.name === 'AbortError') {\r\n console.warn('[DCS] Runtime content fetch timed out')\r\n } else {\r\n console.warn('[DCS] Runtime content fetch failed:', error)\r\n }\r\n return null\r\n } finally {\r\n clearTimeout(timeoutId)\r\n }\r\n}\r\n\r\n/**\r\n * Fetch runtime SEO configuration from the DCS API.\r\n * This is a premium tier feature - returns 403 for non-premium sites.\r\n *\r\n * @param siteSlug - The site's slug identifier\r\n * @param options - Fetch options\r\n * @returns SEO configuration or null if fetch fails\r\n */\r\nexport async function fetchRuntimeSeo(\r\n siteSlug: string,\r\n options: FetchOptions = {}\r\n): Promise<SeoConfiguration | null> {\r\n const { apiBaseUrl = DEFAULT_API_URL, timeout = DEFAULT_TIMEOUT } = options\r\n\r\n const controller = new AbortController()\r\n const timeoutId = setTimeout(() => controller.abort(), timeout)\r\n\r\n try {\r\n const response = await fetch(\r\n `${apiBaseUrl}/portal/sites/${siteSlug}/seo/runtime`,\r\n {\r\n method: 'GET',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n ...options.headers,\r\n },\r\n signal: controller.signal,\r\n }\r\n )\r\n\r\n if (!response.ok) {\r\n if (response.status === 403) {\r\n console.warn(\r\n '[DCS] Runtime SEO requires premium tier. Using build-time SEO.'\r\n )\r\n }\r\n return null\r\n }\r\n\r\n return (await response.json()) as SeoConfiguration\r\n } catch (error) {\r\n if (error instanceof Error && error.name === 'AbortError') {\r\n console.warn('[DCS] Runtime SEO fetch timed out')\r\n } else {\r\n console.warn('[DCS] Runtime SEO fetch failed:', error)\r\n }\r\n return null\r\n } finally {\r\n clearTimeout(timeoutId)\r\n }\r\n}\r\n","/**\r\n * Pure functions for resolving responsive image variants from DCS CDN URLs.\r\n *\r\n * The DCS imaging pipeline generates WebP variants during upload:\r\n * - thumb (150px) — grid thumbnails\r\n * - sm (640px) — mobile displays\r\n * - md (1024px) — tablet / blog body\r\n * - lg (1920px) — desktop / hero sections\r\n * - og (1200×630) — social sharing (fixed aspect)\r\n *\r\n * This module provides framework-agnostic resolution logic that\r\n * framework packages (@duffcloudservices/cms, cms-react, etc.) wrap\r\n * in their respective APIs.\r\n *\r\n * @module responsive-image\r\n */\r\n\r\n/** Sizing context that controls which variants are included in the srcset. */\r\nexport type ImageContext = 'hero' | 'card' | 'thumbnail' | 'content' | 'og'\r\n\r\n/** Options for resolving a responsive image. */\r\nexport interface ResponsiveImageOptions {\r\n /** Source URL — original CDN URL or local path. */\r\n src: string\r\n /** Alt text for accessibility. */\r\n alt: string\r\n /** Sizing context — determines which variants to include. */\r\n context?: ImageContext\r\n /** Optional explicit width hints for the browser `sizes` attribute. */\r\n sizes?: string\r\n /** Skip variant resolution and return the original URL only. */\r\n original?: boolean\r\n}\r\n\r\n/** A single `<source>` entry for a `<picture>` element. */\r\nexport interface ResponsiveSource {\r\n srcset: string\r\n type: string\r\n sizes?: string\r\n}\r\n\r\n/** Resolved image properties for rendering. */\r\nexport interface ResponsiveImageResult {\r\n /** Props spreadable onto an `<img>` element. */\r\n imgProps: {\r\n src: string\r\n alt: string\r\n loading: 'lazy' | 'eager'\r\n decoding: 'async' | 'auto'\r\n }\r\n /** WebP `<source>` entries for a `<picture>` element. */\r\n sources: ResponsiveSource[]\r\n /** Whether responsive variants were detected. */\r\n hasVariants: boolean\r\n}\r\n\r\n/**\r\n * Regex that matches DCS CDN asset URLs and captures the base path, UUID, and extension.\r\n *\r\n * Supports both URL formats:\r\n * - `https://files.duffcloudservices.com/{slug}/assets/{uuid}.ext` (branding container)\r\n * - `https://files.duffcloudservices.com/content/{slug}/assets/{uuid}.ext` (content container)\r\n *\r\n * Group 1: base path including trailing slash (e.g. `https://files.duffcloudservices.com/content/kept/assets/`)\r\n * Group 2: UUID (e.g. `abc-123`)\r\n * Group 3: file extension (e.g. `jpg`)\r\n */\r\nconst CDN_PATTERN = /^(https?:\\/\\/files\\.[^/]+\\/(?:content\\/)?[^/]+\\/assets\\/(?:[^/]+\\/)*)([a-f0-9-]+)\\.(\\w+)$/\r\n\r\n/** Variant definitions mapping suffix → pixel width. */\r\nconst VARIANT_MAP: Record<string, { suffix: string; width: number }> = {\r\n thumb: { suffix: '-thumb', width: 150 },\r\n sm: { suffix: '-sm', width: 640 },\r\n md: { suffix: '-md', width: 1024 },\r\n lg: { suffix: '-lg', width: 1920 },\r\n}\r\n\r\n/** Which variants each context includes (smallest → largest). */\r\nconst CONTEXT_VARIANTS: Record<ImageContext, string[]> = {\r\n hero: ['sm', 'md', 'lg'],\r\n card: ['thumb', 'sm', 'md'],\r\n thumbnail: ['thumb', 'sm'],\r\n content: ['sm', 'md', 'lg'],\r\n og: [], // OG uses the dedicated -og variant, not srcset\r\n}\r\n\r\n/** Default `sizes` attribute per context. */\r\nconst DEFAULT_SIZES: Record<ImageContext, string> = {\r\n hero: '100vw',\r\n card: '(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw',\r\n thumbnail: '150px',\r\n content: '(max-width: 1024px) 100vw, 1024px',\r\n og: '',\r\n}\r\n\r\n/**\r\n * Resolves responsive image metadata from a DCS CDN URL.\r\n *\r\n * If the URL matches the CDN asset pattern, a srcset of WebP variants\r\n * is generated based on the sizing `context`. Non-CDN URLs are returned\r\n * as-is with no variants.\r\n *\r\n * @example\r\n * ```ts\r\n * const result = resolveResponsiveImage({\r\n * src: 'https://files.duffcloudservices.com/kept/assets/hero/abc-123.jpg',\r\n * alt: 'Hero image',\r\n * context: 'hero',\r\n * })\r\n * // result.hasVariants === true\r\n * // result.sources[0].srcset contains sm, md, lg WebP sizes\r\n * // result.imgProps.loading === 'eager' (hero context)\r\n * ```\r\n */\r\nexport function resolveResponsiveImage(options: ResponsiveImageOptions): ResponsiveImageResult {\r\n const { src, alt, context = 'content', sizes, original } = options\r\n\r\n const fallback: ResponsiveImageResult = {\r\n imgProps: {\r\n src,\r\n alt,\r\n loading: context === 'hero' ? 'eager' : 'lazy',\r\n decoding: context === 'hero' ? 'auto' : 'async',\r\n },\r\n sources: [],\r\n hasVariants: false,\r\n }\r\n\r\n if (original) {\r\n return fallback\r\n }\r\n\r\n const match = src.match(CDN_PATTERN)\r\n if (!match) {\r\n return fallback\r\n }\r\n\r\n const [, basePath, uuid] = match\r\n\r\n const variants = CONTEXT_VARIANTS[context] ?? CONTEXT_VARIANTS.content\r\n if (variants.length === 0) {\r\n return fallback\r\n }\r\n\r\n const srcsetParts = variants.map((key) => {\r\n const v = VARIANT_MAP[key]\r\n return `${basePath}${uuid}${v.suffix}.webp ${v.width}w`\r\n })\r\n\r\n const resolvedSizes = sizes ?? DEFAULT_SIZES[context] ?? DEFAULT_SIZES.content\r\n\r\n return {\r\n imgProps: {\r\n src: `${basePath}${uuid}-md.webp`, // Fallback to md variant\r\n alt,\r\n loading: context === 'hero' ? 'eager' : 'lazy',\r\n decoding: context === 'hero' ? 'auto' : 'async',\r\n },\r\n sources: [\r\n {\r\n srcset: srcsetParts.join(', '),\r\n type: 'image/webp',\r\n ...(resolvedSizes ? { sizes: resolvedSizes } : {}),\r\n },\r\n ],\r\n hasVariants: true,\r\n }\r\n}\r\n\r\n/**\r\n * Tests whether a URL matches the DCS CDN asset pattern.\r\n *\r\n * Useful when rendering a mix of CDN-hosted and local images\r\n * to decide whether responsive treatment is applicable.\r\n */\r\nexport function isCdnAssetUrl(url: string): boolean {\r\n return CDN_PATTERN.test(url)\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duffcloudservices/cms-core",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Shared types and utilities for DCS CMS framework packages",
5
5
  "type": "module",
6
6
  "exports": {