@duffcloudservices/cms 0.12.0 → 0.13.1

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.
Files changed (40) hide show
  1. package/README.md +244 -8
  2. package/dist/chunk-A5F4C72F.js +500 -0
  3. package/dist/chunk-A5F4C72F.js.map +1 -0
  4. package/dist/{chunk-F3EIWEZD.js → chunk-HVSF23P7.js} +971 -73
  5. package/dist/chunk-HVSF23P7.js.map +1 -0
  6. package/dist/editor/editorBridge.d.ts +13 -1
  7. package/dist/editor/editorBridge.js +75 -5
  8. package/dist/editor/editorBridge.js.map +1 -1
  9. package/dist/headHonesty-OzxvLuwd.d.ts +222 -0
  10. package/dist/index.d.ts +365 -22
  11. package/dist/index.js +421 -21
  12. package/dist/index.js.map +1 -1
  13. package/dist/installSeoHead-kWQwObez.d.ts +627 -0
  14. package/dist/plugins/index.d.ts +90 -6
  15. package/dist/plugins/index.js +530 -49
  16. package/dist/plugins/index.js.map +1 -1
  17. package/dist/seo/index.d.ts +763 -4
  18. package/dist/seo/index.js +2 -2
  19. package/dist/{vitepressTransform-DfmABXmK.d.ts → vitepressTransform-JG_zlaux.d.ts} +99 -6
  20. package/package.json +17 -6
  21. package/src/components/DcsCallButton.test.ts +58 -0
  22. package/src/components/DcsCallButton.vue +19 -4
  23. package/src/components/LiteMediaEmbed.vue +3 -3
  24. package/src/components/ManagedImage.test.ts +34 -0
  25. package/src/components/ManagedImage.vue +5 -0
  26. package/src/components/PreviewRibbon.vue +25 -6
  27. package/src/components/raw-source-imports.test.ts +44 -0
  28. package/src/composables/useConversionTracking.test.ts +492 -0
  29. package/src/composables/useConversionTracking.ts +770 -0
  30. package/src/composables/useReleaseNotes.ts +7 -1
  31. package/src/composables/useSEO.applyHead.test.ts +150 -0
  32. package/src/composables/useSEO.ts +63 -17
  33. package/src/composables/useSiteVersion.ts +4 -1
  34. package/src/composables/useSiteVisitorSession.test.ts +56 -0
  35. package/src/composables/useSiteVisitorSession.ts +39 -3
  36. package/src/composables/useTextContent.ts +9 -1
  37. package/dist/chunk-DAYLLSEE.js +0 -3
  38. package/dist/chunk-DAYLLSEE.js.map +0 -1
  39. package/dist/chunk-F3EIWEZD.js.map +0 -1
  40. package/dist/spliceHeadHtml-CsBEucGy.d.ts +0 -254
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/composables/useTextContent.ts","../src/composables/useSEO.ts","../src/composables/useReleaseNotes.ts","../src/composables/useSiteVersion.ts","../src/composables/useMediaCarousel.ts","../src/composables/useResponsiveImage.ts","../src/composables/useReviewContent.ts","../src/composables/useSiteVisitorSession.ts"],"names":["computed","getEnvVar","ref","onMounted","CACHE_TTL"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * useTextContent Composable\n *\n * Provides text content management with build-time injection support and optional\n * runtime API overrides for DCS-managed customer sites.\n *\n * Content resolution order:\n * 1. Runtime API overrides (premium tier only, if mode is 'runtime')\n * 2. Build-time content from .dcs/content.yaml (injected via dcsContentPlugin)\n * 3. Hardcoded defaults passed to the composable\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useTextContent } from '@duffcloudservices/cms'\n *\n * const { t, isLoading, error } = useTextContent({\n * pageSlug: 'home',\n * defaults: {\n * 'hero.title': 'Welcome to Our Site',\n * 'hero.subtitle': 'Build something amazing',\n * 'cta.primary': 'Get Started'\n * }\n * })\n * </script>\n *\n * <template>\n * <h1>{{ t('hero.title') }}</h1>\n * <p>{{ t('hero.subtitle') }}</p>\n * <button>{{ t('cta.primary') }}</button>\n * </template>\n * ```\n */\n\nimport { ref, computed, readonly, onMounted, type Ref } from 'vue'\nimport type { DcsContentFile, TextContentConfig, TextContentReturn } from '../types/content'\n\n// Declare the global injected by dcsContentPlugin\ndeclare const __DCS_CONTENT__: DcsContentFile | undefined\n\n// Simple in-memory cache for runtime fetches\nconst fetchCache = new Map<string, { data: Record<string, string>; expiresAt: number }>()\n\n/**\n * Safely get build-time content configuration.\n * Returns undefined if not available (no content.yaml or plugin not configured).\n */\nfunction getBuildTimeContent(): DcsContentFile | undefined {\n try {\n if (typeof __DCS_CONTENT__ !== 'undefined' && __DCS_CONTENT__ !== null) {\n return __DCS_CONTENT__\n }\n } catch {\n // __DCS_CONTENT__ not defined - that's fine, use defaults\n }\n return undefined\n}\n\n/**\n * Get build-time content for a specific page, merging global and page-specific content.\n */\nfunction getBuildTimePageContent(pageSlug: string): Record<string, string> {\n const content = getBuildTimeContent()\n if (!content) return {}\n\n const global = content.global ?? {}\n const page = content.pages?.[pageSlug] ?? {}\n\n return { ...global, ...page }\n}\n\n/**\n * Get environment variable value, handling both Vite and process.env patterns.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n // Vite pattern\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n\n try {\n // Node.js pattern\n if (typeof process !== 'undefined' && process.env) {\n const value = process.env[key]\n if (value !== undefined) return value\n }\n } catch {\n // process not available\n }\n\n return defaultValue\n}\n\n/**\n * useTextContent composable for DCS-managed text content.\n *\n * @param config - Configuration object\n * @returns Text content helpers and state\n */\nexport function useTextContent(config: TextContentConfig): TextContentReturn {\n const {\n pageSlug,\n defaults,\n fetchOnMount = true,\n cacheKey,\n cacheTtl = 60000,\n } = config\n\n // Get configuration from environment.\n // @deprecated VITE_SITE_SLUG: the site is now resolved server-side from the\n // request Host or the dedicated Container App's DCS_SITE_SLUG. It is still read\n // here for cache-key continuity and source compatibility, but is no longer used\n // to build the request URL.\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', '')\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\n const textOverrideMode = getEnvVar('VITE_TEXT_OVERRIDE_MODE', 'commit')\n const mode: 'commit' | 'runtime' = textOverrideMode === 'runtime' ? 'runtime' : 'commit'\n\n // Get build-time content immediately (synchronous)\n const buildTimeContent = getBuildTimePageContent(pageSlug)\n const hasBuildTimeContent = Object.keys(buildTimeContent).length > 0\n\n // State - initialize with build-time content\n const overrides = ref<Record<string, string>>({ ...buildTimeContent })\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n // Computed merged texts (defaults + overrides)\n const texts = computed(() => ({ ...defaults, ...overrides.value }))\n\n /**\n * Get text by key with optional fallback.\n * Resolution order: overrides → defaults → fallback → key\n */\n function t(key: string, fallback?: string): string {\n return overrides.value[key] ?? defaults[key] ?? fallback ?? key\n }\n\n /**\n * Check if a key has an override (from build-time or runtime).\n */\n function hasOverride(key: string): boolean {\n return key in overrides.value\n }\n\n /**\n * Get an array of objects from indexed content keys.\n * Useful for lists where keys follow the pattern: arrayKey.index.property\n * Example: features.1.title, features.1.description, features.2.title, etc.\n *\n * @param arrayKey - The base key prefix (e.g., 'features', 'items')\n * @returns Array of objects with properties extracted from matching keys, sorted by index\n *\n * @example\n * ```ts\n * // Given content keys:\n * // positions.1.title = \"Software Engineer\"\n * // positions.1.description = \"Build cool stuff\"\n * // positions.2.title = \"Designer\"\n * // positions.2.description = \"Design cool stuff\"\n *\n * const positions = getArray('positions')\n * // Returns:\n * // [\n * // { _index: 1, title: \"Software Engineer\", description: \"Build cool stuff\" },\n * // { _index: 2, title: \"Designer\", description: \"Design cool stuff\" }\n * // ]\n * ```\n */\n function getArray(arrayKey: string): Array<Record<string, unknown> & { _index: number }> {\n const items: Record<number, Record<string, unknown> & { _index: number }> = {}\n const source = { ...defaults, ...overrides.value }\n\n Object.keys(source).forEach((key) => {\n if (key.startsWith(`${arrayKey}.`)) {\n const parts = key.split('.')\n // Format: arrayKey.index.property (or arrayKey.index.nested.property)\n // Example: features.1.title -> index=1, prop=title\n if (parts.length >= 3) {\n const index = Number.parseInt(parts[1], 10)\n const prop = parts.slice(2).join('.')\n\n if (!Number.isNaN(index)) {\n if (!items[index]) items[index] = { _index: index }\n items[index][prop] = source[key]\n }\n }\n }\n })\n\n return Object.values(items).sort((a, b) => a._index - b._index)\n }\n\n /**\n * Fetch runtime overrides from the API.\n * Only runs if mode is 'runtime' and API is configured.\n */\n async function fetchOverrides(): Promise<void> {\n // Skip API fetch in commit mode - use build-time content only\n if (mode !== 'runtime') {\n return\n }\n\n // Skip if API not configured. The site is resolved server-side (Host /\n // DCS_SITE_SLUG), so VITE_SITE_SLUG is no longer required for routing —\n // only the API base URL is needed.\n if (!apiBaseUrl) {\n console.warn(\n '[@duffcloudservices/cms] Runtime mode enabled but VITE_API_BASE_URL not set'\n )\n return\n }\n\n // Check cache first\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\n const cached = fetchCache.get(effectiveCacheKey)\n if (cached && cached.expiresAt > Date.now()) {\n overrides.value = { ...buildTimeContent, ...cached.data }\n return\n }\n\n isLoading.value = true\n error.value = null\n\n try {\n // Site is resolved server-side from the request Host or the dedicated\n // Container App's DCS_SITE_SLUG; the slug is no longer encoded in the path.\n const url = `${apiBaseUrl}/api/v1/pages/${pageSlug}/text`\n const response = await fetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n // No overrides for this page - that's fine\n return\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const data = await response.json()\n const apiOverrides = data.overrides ?? data.texts ?? {}\n\n // Cache the result\n fetchCache.set(effectiveCacheKey, {\n data: apiOverrides,\n expiresAt: Date.now() + cacheTtl,\n })\n\n // Merge: build-time content is baseline, API overrides on top\n overrides.value = { ...buildTimeContent, ...apiOverrides }\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to load text overrides'\n console.error('[@duffcloudservices/cms] Failed to fetch text overrides:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n /**\n * Manually refresh overrides.\n * In commit mode, resets to build-time content.\n * In runtime mode, fetches fresh data from API.\n */\n async function refresh(): Promise<void> {\n if (mode !== 'runtime') {\n // In commit mode, just reset to build-time content\n overrides.value = { ...buildTimeContent }\n return\n }\n\n // Clear cache for this key\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\n fetchCache.delete(effectiveCacheKey)\n\n await fetchOverrides()\n }\n\n // Fetch on mount if enabled and in runtime mode (browser only)\n if (fetchOnMount && mode === 'runtime' && globalThis.window !== undefined) {\n onMounted(() => {\n fetchOverrides()\n })\n }\n\n return {\n t,\n getArray,\n texts,\n overrides,\n isLoading: readonly(isLoading) as Ref<boolean>,\n error: readonly(error) as Ref<string | null>,\n refresh,\n hasOverride,\n hasBuildTimeContent,\n mode,\n }\n}\n","/**\n * useSEO Composable\n *\n * Provides SEO configuration with build-time injection support from .dcs/seo.yaml.\n * Generates meta tags, Open Graph, Twitter Cards, and JSON-LD structured data.\n *\n * The actual tag resolution lives in the framework-agnostic `../seo/headTags`\n * module so that the build-time static-HTML emitter (`dcsSeoPlugin`) produces\n * byte-identical output. This composable is a thin Vue/unhead wrapper over it.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useSEO } from '@duffcloudservices/cms'\n *\n * const { applyHead, getSchema, config } = useSEO('home')\n *\n * // Apply all meta tags\n * applyHead()\n *\n * // Or customize before applying\n * applyHead({\n * title: 'Custom Override Title',\n * schemas: [...getSchema(), customSchema]\n * })\n * </script>\n * ```\n */\n\nimport { computed, type ComputedRef } from 'vue'\nimport { useHead } from '@unhead/vue'\nimport type {\n SeoConfiguration,\n GlobalSeoConfig,\n ResolvedPageSeo,\n UseSeoReturn,\n HeadOverrides,\n} from '../types/seo'\nimport { buildHeadTags, resolvePageSeo, generateJsonLd } from '../seo/headTags'\n\n// Declare the global injected by dcsSeoPlugin\ndeclare const __DCS_SEO__: SeoConfiguration | undefined\n\n/**\n * Safely get build-time SEO configuration.\n * Returns undefined if not available (no seo.yaml or plugin not configured).\n */\nfunction getBuildTimeSeo(): SeoConfiguration | undefined {\n try {\n if (typeof __DCS_SEO__ !== 'undefined' && __DCS_SEO__ !== null) {\n return __DCS_SEO__\n }\n } catch {\n // __DCS_SEO__ not defined - that's fine, use defaults\n }\n return undefined\n}\n\n/**\n * useSEO composable for DCS-managed SEO configuration.\n *\n * @param pageSlug - Page slug matching entry in seo.yaml\n * @param pagePath - Optional page path for canonical URL generation\n * @returns SEO helpers and state\n */\nexport function useSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\n const seoConfig = getBuildTimeSeo()\n const hasBuildTimeSeo = seoConfig !== undefined\n\n // Computed resolved config\n const config: ComputedRef<ResolvedPageSeo> = computed(() =>\n resolvePageSeo(pageSlug, pagePath, seoConfig)\n )\n\n /**\n * Get JSON-LD schema objects for the page\n */\n function getSchema(): object[] {\n return generateJsonLd(config.value.schemas, seoConfig?.global ?? {})\n }\n\n /**\n * Get canonical URL for the page\n */\n function getCanonical(): string {\n return config.value.canonical\n }\n\n /**\n * Apply all meta tags via useHead.\n *\n * Delegates to the shared `buildHeadTags` resolver so the emitted tags match\n * the build-time static-HTML emitter exactly. Keywords are intentionally not\n * emitted at runtime (historical behaviour), so `includeKeywords` is omitted.\n */\n function applyHead(overrides?: HeadOverrides): void {\n const { title, meta, link, script } = buildHeadTags(pageSlug, pagePath, seoConfig, {\n title: overrides?.title,\n description: overrides?.description,\n keywords: overrides?.keywords,\n schemas: overrides?.schemas,\n meta: overrides?.meta,\n })\n\n // Apply via useHead. The shared resolver returns framework-agnostic tag\n // shapes (HeadMetaTag/HeadLinkTag/HeadScriptTag); unhead's input types are\n // structurally compatible but add an open-ended `data-*` index signature,\n // so we hand them over via useHead's Head input type. Runtime is identical.\n useHead({\n title,\n meta,\n link,\n script,\n } as unknown as Parameters<typeof useHead>[0])\n }\n\n return {\n config,\n applyHead,\n getSchema,\n getCanonical,\n hasBuildTimeSeo,\n }\n}\n\n/**\n * Create a typed useSEO function with site-specific defaults.\n * Useful for creating a site-wide wrapper.\n *\n * @example\n * ```ts\n * // composables/useSiteSeo.ts\n * import { createSiteSEO } from '@duffcloudservices/cms'\n *\n * export const useSiteSeo = createSiteSEO({\n * siteName: 'My Site',\n * siteUrl: 'https://example.com'\n * })\n * ```\n */\nexport function createSiteSEO(\n _siteDefaults: Partial<GlobalSeoConfig>\n): (pageSlug: string, pagePath?: string) => UseSeoReturn {\n return function siteUseSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\n // Note: siteDefaults would be used if we needed to override at runtime\n // but build-time injection handles this via dcsSeoPlugin\n return useSEO(pageSlug, pagePath)\n }\n}\n","/**\n * useReleaseNotes Composable\n *\n * Fetches and displays versioned release notes from the DCS Portal API.\n * Supports fetching specific versions or the latest release.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useReleaseNotes } from '@duffcloudservices/cms'\n * import { useRoute } from 'vue-router'\n *\n * const route = useRoute()\n * const version = route.params.version as string || 'latest'\n *\n * const { releaseNote, isLoading, error } = useReleaseNotes(version)\n * </script>\n *\n * <template>\n * <div v-if=\"isLoading\">Loading...</div>\n * <div v-else-if=\"error\">{{ error }}</div>\n * <article v-else-if=\"releaseNote\">\n * <h1>{{ releaseNote.title }}</h1>\n * <p>{{ releaseNote.summary }}</p>\n * <div v-html=\"renderedMarkdown\" />\n * </article>\n * </template>\n * ```\n */\n\nimport { ref, onMounted } from 'vue'\nimport type { ReleaseNote, ReleaseNotesReturn } from '../types/release-notes'\n\n/**\n * Get environment variable value.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n return defaultValue\n}\n\n// Simple cache for release notes\nconst releaseNotesCache = new Map<string, { data: ReleaseNote; expiresAt: number }>()\nconst CACHE_TTL = 5 * 60 * 1000 // 5 minutes\n\n/**\n * useReleaseNotes composable for fetching release notes from the DCS API.\n *\n * @param version - Semantic version (e.g., \"1.2.0\") or \"latest\"\n * @param options - Optional configuration\n * @returns Release notes data and state\n */\nexport function useReleaseNotes(\n version: string,\n options: { fetchOnMount?: boolean } = {}\n): ReleaseNotesReturn {\n const { fetchOnMount = true } = options\n\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\n // @deprecated VITE_SITE_SLUG: the site is now resolved server-side from the\n // request Host or the dedicated Container App's DCS_SITE_SLUG. It is retained\n // only for cache-key continuity and source compatibility, not for routing.\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\n\n const releaseNote = ref<ReleaseNote | null>(null)\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n async function fetchReleaseNotes(): Promise<void> {\n // Check cache. The slug is no longer required for routing (server resolves\n // the site from Host / DCS_SITE_SLUG); it is only part of the cache key.\n const cacheKey = `${siteSlug}:${version}`\n const cached = releaseNotesCache.get(cacheKey)\n if (cached && cached.expiresAt > Date.now()) {\n releaseNote.value = cached.data\n return\n }\n\n isLoading.value = true\n error.value = null\n\n try {\n const url = `${apiBaseUrl}/api/v1/release-notes/${version}`\n const response = await fetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n error.value = `Release notes for version ${version} not found`\n return\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const data = await response.json()\n\n // Normalize the response\n const note: ReleaseNote = {\n version: data.version,\n title: data.title,\n summary: data.summary || '',\n notesMarkdown: data.notesMarkdown || data.notes || '',\n changeCount: data.changeCount || 0,\n releaseDate: data.releaseDate || data.releasedAt || '',\n }\n\n // Cache the result\n releaseNotesCache.set(cacheKey, {\n data: note,\n expiresAt: Date.now() + CACHE_TTL,\n })\n\n releaseNote.value = note\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to load release notes'\n console.error('[@duffcloudservices/cms] Failed to fetch release notes:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n async function refresh(): Promise<void> {\n // Clear cache\n const cacheKey = `${siteSlug}:${version}`\n releaseNotesCache.delete(cacheKey)\n await fetchReleaseNotes()\n }\n\n // Fetch on mount if enabled\n if (fetchOnMount && typeof window !== 'undefined') {\n onMounted(() => {\n fetchReleaseNotes()\n })\n }\n\n return {\n releaseNote,\n isLoading,\n error,\n refresh,\n }\n}\n","/**\n * useSiteVersion Composable\n *\n * Gets the current site version for footer badges and version displays.\n * Fetches the latest release version from the DCS Portal API.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useSiteVersion } from '@duffcloudservices/cms'\n *\n * const { version, releaseNotesUrl } = useSiteVersion()\n * </script>\n *\n * <template>\n * <footer>\n * <a v-if=\"version\" :href=\"releaseNotesUrl\" class=\"version-badge\">\n * v{{ version }}\n * </a>\n * </footer>\n * </template>\n * ```\n */\n\nimport { ref, computed, onMounted } from 'vue'\nimport type { SiteVersionReturn } from '../types/release-notes'\n\n/**\n * Get environment variable value.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n return defaultValue\n}\n\n// Cache for site version\nlet versionCache: { version: string; expiresAt: number } | null = null\nconst CACHE_TTL = 10 * 60 * 1000 // 10 minutes\n\n/**\n * useSiteVersion composable for displaying the current site version.\n *\n * @param options - Optional configuration\n * @returns Site version data and computed URL\n */\nexport function useSiteVersion(options: { fetchOnMount?: boolean } = {}): SiteVersionReturn {\n const { fetchOnMount = true } = options\n\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\n // @deprecated VITE_SITE_SLUG: the site is now resolved server-side from the\n // request Host or the dedicated Container App's DCS_SITE_SLUG. It is no longer\n // used for routing; retained only for source compatibility.\n void getEnvVar('VITE_SITE_SLUG', '')\n\n const version = ref<string | null>(null)\n const isLoading = ref(false)\n\n const releaseNotesUrl = computed(() => {\n if (!version.value) return '/releaseNotes/latest'\n return `/releaseNotes/${version.value}`\n })\n\n async function fetchVersion(): Promise<void> {\n // Check cache\n if (versionCache && versionCache.expiresAt > Date.now()) {\n version.value = versionCache.version\n return\n }\n\n isLoading.value = true\n\n try {\n // Fetch the latest release notes to get the version\n const url = `${apiBaseUrl}/api/v1/release-notes/latest`\n const response = await fetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n // No release notes yet - that's fine\n return\n }\n\n const data = await response.json()\n const latestVersion = data.version\n\n // Cache the result\n versionCache = {\n version: latestVersion,\n expiresAt: Date.now() + CACHE_TTL,\n }\n\n version.value = latestVersion\n } catch (e) {\n console.error('[@duffcloudservices/cms] Failed to fetch site version:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n // Fetch on mount if enabled\n if (fetchOnMount && typeof window !== 'undefined') {\n onMounted(() => {\n fetchVersion()\n })\n }\n\n return {\n version,\n isLoading,\n releaseNotesUrl,\n }\n}\n","/**\n * useMediaCarousel Composable\n *\n * Extracts media carousel items from text content keys following the pattern:\n * `{prefix}.{N}.url`, `{prefix}.{N}.type`, `{prefix}.{N}.alt`\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useTextContent, useMediaCarousel } from '@duffcloudservices/cms'\n *\n * const { t } = useTextContent({\n * pageSlug: 'bio-mackenzie',\n * defaults: {\n * 'hero.media-carousel.0.url': '/images/staff/mackenzie.webp',\n * 'hero.media-carousel.0.type': 'image',\n * 'hero.media-carousel.0.alt': 'Mackenzie Kowalick',\n * 'hero.media-carousel.1.url': '/videos/intro.mp4',\n * 'hero.media-carousel.1.type': 'video',\n * 'hero.media-carousel.1.alt': 'Introduction video',\n * }\n * })\n *\n * const { items } = useMediaCarousel({\n * prefix: 'hero.media-carousel',\n * t,\n * defaults: [\n * { url: '/images/staff/mackenzie.webp', type: 'image', alt: 'Mackenzie Kowalick' }\n * ]\n * })\n * </script>\n *\n * <template>\n * <MediaCarousel :items=\"items\" />\n * </template>\n * ```\n */\n\nimport { computed, type ComputedRef } from 'vue'\nimport { isCdnAssetUrl } from '@duffcloudservices/cms-core'\n\n/**\n * Media carousel item representing an image, video, or embed\n */\nexport interface MediaCarouselItem {\n /** URL to the image, video file, or embed URL */\n url: string\n /**\n * Type of media: 'image', 'video' (direct file), 'youtube', or 'instagram'.\n * Render `youtube`/`instagram` items through the click-to-load facade\n * `LiteMediaEmbed` (`@duffcloudservices/cms/lite-media-embed`) so the heavy\n * player iframe loads only on user interaction — never eagerly.\n */\n type: 'image' | 'video' | 'youtube' | 'instagram'\n /** Accessibility alt text */\n alt?: string\n /**\n * Whether this image has responsive CDN variants available.\n * Automatically set to `true` when the URL matches the DCS CDN asset pattern.\n * Components rendering the carousel should use `<ResponsiveImage>` when this is `true`.\n */\n responsive?: boolean\n}\n\n/**\n * Configuration for useMediaCarousel composable\n */\nexport interface UseMediaCarouselConfig {\n /** Key prefix for carousel items (e.g., 'hero.media-carousel') */\n prefix: string\n /** The t() function from useTextContent */\n t: (key: string, fallback?: string) => string\n /** Default items to use if no content keys are found */\n defaults?: MediaCarouselItem[]\n /** Maximum number of items to look for (default: 10) */\n maxItems?: number\n}\n\n/**\n * Return type for useMediaCarousel composable\n */\nexport interface UseMediaCarouselReturn {\n /** Computed array of media carousel items */\n items: ComputedRef<MediaCarouselItem[]>\n /** Whether any items were found from content keys */\n hasItems: ComputedRef<boolean>\n /** Number of items in the carousel */\n count: ComputedRef<number>\n}\n\n/**\n * Extract media carousel items from text content keys.\n *\n * Looks for keys in the format:\n * - `{prefix}.{N}.url` - Required URL for the media\n * - `{prefix}.{N}.type` - Type: 'image' or 'video' (defaults to 'image')\n * - `{prefix}.{N}.alt` - Alt text for accessibility\n *\n * Items are sorted by index (0, 1, etc.) and only included if they have a valid URL.\n *\n * @param config - Configuration object\n * @returns Media carousel helpers and state\n */\nexport function useMediaCarousel(config: UseMediaCarouselConfig): UseMediaCarouselReturn {\n const {\n prefix,\n t,\n defaults = [],\n maxItems = 10,\n } = config\n\n const items = computed<MediaCarouselItem[]>(() => {\n const result: MediaCarouselItem[] = []\n\n // Look for items from 0 to maxItems\n for (let i = 0; i < maxItems; i++) {\n const urlKey = `${prefix}.${i}.url`\n const typeKey = `${prefix}.${i}.type`\n const altKey = `${prefix}.${i}.alt`\n\n // Use a sentinel value to detect if the key exists\n const url = t(urlKey, '')\n \n // Skip if no URL (key doesn't exist or is empty)\n if (!url || url === urlKey) {\n continue\n }\n\n const typeValue = t(typeKey, 'image')\n // Parse type value - support image, video, youtube, instagram\n let type: 'image' | 'video' | 'youtube' | 'instagram' = 'image'\n if (typeValue === 'video') type = 'video'\n else if (typeValue === 'youtube') type = 'youtube'\n else if (typeValue === 'instagram') type = 'instagram'\n const alt = t(altKey, '')\n\n result.push({\n url,\n type,\n alt: alt && alt !== altKey ? alt : undefined,\n // Flag CDN-hosted images as responsive so carousel components\n // can render them with <ResponsiveImage> automatically\n responsive: type === 'image' && isCdnAssetUrl(url),\n })\n }\n\n // If no items found from content keys, use defaults\n if (result.length === 0) {\n return defaults\n }\n\n return result\n })\n\n const hasItems = computed(() => items.value.length > 0)\n const count = computed(() => items.value.length)\n\n return {\n items,\n hasItems,\n count,\n }\n}\n","/**\n * Vue 3 composable that resolves responsive image variants for DCS CDN-hosted assets.\n *\n * Wraps the framework-agnostic `resolveResponsiveImage` from `@duffcloudservices/cms-core`\n * with reactive Vue refs so it can be used directly in `<script setup>` blocks.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useResponsiveImage } from '@duffcloudservices/cms'\n *\n * const hero = useResponsiveImage({\n * src: 'https://files.duffcloudservices.com/kept/assets/hero/abc-123.jpg',\n * alt: 'Hero image',\n * context: 'hero',\n * })\n * </script>\n *\n * <template>\n * <picture v-if=\"hero.hasVariants\">\n * <source\n * v-for=\"source in hero.sources\"\n * :key=\"source.type\"\n * :srcset=\"source.srcset\"\n * :type=\"source.type\"\n * :sizes=\"source.sizes\"\n * />\n * <img v-bind=\"hero.imgProps\" class=\"w-full h-full object-cover\" />\n * </picture>\n * <img v-else v-bind=\"hero.imgProps\" class=\"w-full h-full object-cover\" />\n * </template>\n * ```\n */\n\nimport { computed, type MaybeRefOrGetter, toValue } from 'vue'\nimport {\n resolveResponsiveImage,\n type ImageContext,\n type ResponsiveImageResult,\n} from '@duffcloudservices/cms-core'\n\nexport interface UseResponsiveImageOptions {\n /** Source URL — can be a reactive ref, getter, or plain string. */\n src: MaybeRefOrGetter<string>\n /** Alt text — can be a reactive ref, getter, or plain string. */\n alt: MaybeRefOrGetter<string>\n /** Sizing context — determines which variants to include. */\n context?: MaybeRefOrGetter<ImageContext | undefined>\n /** Optional `sizes` attribute override. */\n sizes?: MaybeRefOrGetter<string | undefined>\n /** Skip variant resolution and use the original URL only. */\n original?: MaybeRefOrGetter<boolean | undefined>\n /** Intrinsic width — emitted as a layout-shift hint when paired with `height`. */\n width?: MaybeRefOrGetter<number | undefined>\n /** Intrinsic height — emitted as a layout-shift hint when paired with `width`. */\n height?: MaybeRefOrGetter<number | undefined>\n}\n\n/**\n * Reactively resolves responsive image metadata for a DCS CDN URL.\n *\n * The returned object is a computed ref that recomputes whenever any\n * of the input refs change. Spread `imgProps` onto an `<img>` or\n * combine with `sources` inside a `<picture>` element.\n */\nexport function useResponsiveImage(options: UseResponsiveImageOptions): ResponsiveImageResult {\n const result = computed(() =>\n resolveResponsiveImage({\n src: toValue(options.src),\n alt: toValue(options.alt),\n context: toValue(options.context),\n sizes: toValue(options.sizes),\n original: toValue(options.original) ?? undefined,\n width: toValue(options.width) ?? undefined,\n height: toValue(options.height) ?? undefined,\n }),\n )\n\n // Return a reactive proxy that delegates to the computed\n return {\n get imgProps() {\n return result.value.imgProps\n },\n get sources() {\n return result.value.sources\n },\n get hasVariants() {\n return result.value.hasVariants\n },\n }\n}\n","/**\n * Composable for reading curated review selections from DCS content.\n * Reviews are stored in content.yaml by the visual editor's ReviewPickerSheet.\n */\nimport { computed, onMounted, onUnmounted, shallowRef, type ComputedRef } from 'vue'\n\nexport interface ReviewItem {\n id: string\n platform: 'google' | 'meta' | string\n rating: number\n authorName: string\n authorPhotoUrl?: string\n text?: string\n date?: string\n replyText?: string\n locationName?: string\n sourceLocationName?: string\n sourceUrl?: string\n}\n\nfunction withoutAuthorPhotos(items: ReviewItem[]): ReviewItem[] {\n return items.map(item => ({\n ...item,\n authorPhotoUrl: undefined,\n }))\n}\n\n/**\n * True only in a Vite dev build. Vite statically replaces `import.meta.env.DEV`\n * with a boolean literal (`true` in dev, `false` in production). The\n * `typeof import.meta` guard + try/catch keep this safe if the package is ever\n * SSR-externalized (where `import.meta.env` is undefined at the Node runtime and\n * accessing it would throw) — in that case we deliberately return `false` so the\n * production-safe branch is taken.\n */\nfunction isDevBuild(): boolean {\n try {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.env.DEV === true\n }\n } catch {\n // import.meta.env unavailable (externalized SSR / non-Vite host) → prod-safe.\n }\n return false\n}\n\n/**\n * Honesty guard: consumer-supplied `defaults` are a DEV-ONLY affordance so a\n * freshly-scaffolded site shows sample reviews while it is being wired up. In a\n * production build they must NEVER render — an SSR production pass emitting\n * placeholder reviews is exactly the fabricated-social-proof / hydration bug\n * this guards against (the content define can be missing server-side if the cms\n * package is SSR-externalized). Production therefore falls back to an honest\n * empty state; real reviews always come from `.dcs/content.yaml`.\n */\nfunction fallbackReviews(defaults: ReviewItem[]): ReviewItem[] {\n return isDevBuild() ? withoutAuthorPhotos(defaults) : []\n}\n\nexport interface UseReviewContentConfig {\n /** The section key matching the data-dcs-reviews attribute value */\n sectionKey: string\n /** Page slug for page-specific content lookup (defaults to current page) */\n pageSlug?: string\n /** Fallback reviews when no content is available */\n defaults?: ReviewItem[]\n}\n\nexport interface UseReviewContentReturn {\n /** The curated review items from content */\n reviews: ComputedRef<ReviewItem[]>\n /** Whether any reviews are available */\n hasReviews: ComputedRef<boolean>\n /** Number of reviews */\n count: ComputedRef<number>\n}\n\nconst previewReviewOverrides = shallowRef<Record<string, ReviewItem[]>>({})\nlet activePreviewReviewConsumers = 0\n\n// Declare the global content variable injected by dcsContentPlugin\ndeclare const __DCS_CONTENT__: {\n global?: Record<string, unknown>\n pages?: Record<string, Record<string, unknown>>\n} | undefined\n\nfunction normalizeReviewItem(item: Record<string, unknown>): ReviewItem | null {\n const id = String(item.id ?? '').trim()\n if (!id) {\n return null\n }\n\n const rawRating = Number(item.rating ?? 5)\n const rating = Number.isFinite(rawRating)\n ? Math.min(5, Math.max(1, Math.round(rawRating)))\n : 5\n\n return {\n id,\n platform: String(item.platform ?? 'google'),\n rating,\n authorName: String(item.authorName ?? 'Anonymous'),\n authorPhotoUrl: undefined,\n text: item.text ? String(item.text) : undefined,\n date: item.date ? String(item.date) : undefined,\n replyText: item.replyText ? String(item.replyText) : undefined,\n locationName: item.locationName ? String(item.locationName) : undefined,\n sourceLocationName: item.sourceLocationName ? String(item.sourceLocationName) : undefined,\n sourceUrl: item.sourceUrl ? String(item.sourceUrl) : undefined,\n }\n}\n\nfunction normalizeReviewList(value: unknown): ReviewItem[] {\n if (!Array.isArray(value)) {\n return []\n }\n\n return value\n .filter((item): item is Record<string, unknown> => item != null && typeof item === 'object')\n .map(normalizeReviewItem)\n .filter((item): item is ReviewItem => item != null)\n}\n\nfunction handlePreviewReviewUpdate(event: Event) {\n const detail = event instanceof CustomEvent && event.detail != null && typeof event.detail === 'object'\n ? event.detail as { key?: unknown; reviews?: unknown }\n : null\n const key = typeof detail?.key === 'string' ? detail.key.trim() : ''\n if (!key) {\n return\n }\n\n const next = { ...previewReviewOverrides.value }\n if (Array.isArray(detail?.reviews)) {\n next[key] = normalizeReviewList(detail.reviews)\n } else {\n delete next[key]\n }\n previewReviewOverrides.value = next\n}\n\nexport function useReviewContent(config: UseReviewContentConfig): UseReviewContentReturn {\n const { sectionKey, pageSlug, defaults = [] } = config\n\n onMounted(() => {\n activePreviewReviewConsumers += 1\n if (activePreviewReviewConsumers === 1) {\n window.addEventListener('dcs:reviews-updated', handlePreviewReviewUpdate)\n }\n })\n\n onUnmounted(() => {\n activePreviewReviewConsumers = Math.max(0, activePreviewReviewConsumers - 1)\n if (activePreviewReviewConsumers === 0) {\n window.removeEventListener('dcs:reviews-updated', handlePreviewReviewUpdate)\n }\n })\n\n const reviews = computed<ReviewItem[]>(() => {\n if (Object.prototype.hasOwnProperty.call(previewReviewOverrides.value, sectionKey)) {\n return previewReviewOverrides.value[sectionKey] ?? []\n }\n\n if (typeof __DCS_CONTENT__ === 'undefined' || __DCS_CONTENT__ == null) {\n return fallbackReviews(defaults)\n }\n\n let reviewData: unknown = null\n\n if (pageSlug && __DCS_CONTENT__.pages?.[pageSlug]) {\n reviewData = __DCS_CONTENT__.pages[pageSlug][`reviews.${sectionKey}.items`]\n ?? __DCS_CONTENT__.pages[pageSlug][`reviews.${sectionKey}`]\n }\n\n if (!reviewData && __DCS_CONTENT__.global) {\n reviewData = __DCS_CONTENT__.global[`reviews.${sectionKey}.items`]\n ?? __DCS_CONTENT__.global[`reviews.${sectionKey}`]\n }\n\n if (!reviewData || !Array.isArray(reviewData)) {\n return fallbackReviews(defaults)\n }\n\n return normalizeReviewList(reviewData)\n })\n\n const hasReviews = computed(() => reviews.value.length > 0)\n const count = computed(() => reviews.value.length)\n\n return { reviews, hasReviews, count }\n}\n","/**\n * Site-visitor session probe client.\n *\n * Customer sites poll \"who is signed in?\" on load to render the right auth UI.\n * Signed-out is the EXPECTED state for the vast majority of visits, so this\n * client treats it as normal and never logs:\n *\n * - New servers answer an anonymous probe with `200 { \"visitor\": null }`.\n * - Old servers (pre-fix) answer an anonymous probe with `401`.\n *\n * Sites bump `@duffcloudservices/cms` independently of server deploys, so this\n * client must accept BOTH shapes and stay silent on the anonymous path — a\n * `console.error`/`console.warn` on every signed-out page load is exactly the\n * console-noise this fixes. A genuine signed-in visitor is returned as a\n * normalized `SiteVisitor`; anything else resolves to `null`.\n */\nimport { ref, computed, onMounted, type Ref, type ComputedRef } from 'vue'\n\n/** A signed-in site visitor. */\nexport interface SiteVisitor {\n id?: string\n email: string\n name: string\n picture?: string\n createdAt?: string\n}\n\nexport interface SiteVisitorSessionResult {\n /** The signed-in visitor, or `null` when signed out. */\n visitor: SiteVisitor | null\n /** Convenience flag — `true` iff a visitor is present. */\n authenticated: boolean\n}\n\nexport interface FetchSiteVisitorSessionOptions {\n /** API base (default `/api/v1`, same-origin via Front Door). */\n apiBaseUrl?: string\n /** Optional AbortSignal to cancel an in-flight probe. */\n signal?: AbortSignal\n}\n\nconst SIGNED_OUT: SiteVisitorSessionResult = { visitor: null, authenticated: false }\n\nconst apiBase = (value?: string): string => (value ?? '/api/v1').replace(/\\/$/u, '')\n\n/**\n * Normalize the various session-response shapes into a `SiteVisitor | null`.\n * Accepts the primary/legacy `{ visitor: {...} | null }` envelope and the\n * contracts-spec `{ authenticated, user: {...} }` shape defensively.\n */\nfunction extractVisitor(data: unknown): SiteVisitor | null {\n if (!data || typeof data !== 'object') {\n return null\n }\n const obj = data as Record<string, unknown>\n const raw = obj.visitor ?? obj.user\n if (!raw || typeof raw !== 'object') {\n return null\n }\n const v = raw as Record<string, unknown>\n const email = typeof v.email === 'string' ? v.email : ''\n if (!email) {\n return null\n }\n return {\n id: typeof v.id === 'string' ? v.id : undefined,\n email,\n name: typeof v.name === 'string' ? v.name : email,\n picture: typeof v.picture === 'string' ? v.picture : undefined,\n createdAt: typeof v.createdAt === 'string' ? v.createdAt : undefined,\n }\n}\n\n/**\n * Fetch the current site-visitor session. Never throws and never logs for the\n * anonymous case: any network error, non-OK status (incl. a legacy `401`), or\n * `{ visitor: null }` body resolves to a signed-out result.\n */\nexport async function fetchSiteVisitorSession(\n options: FetchSiteVisitorSessionOptions = {},\n): Promise<SiteVisitorSessionResult> {\n const base = apiBase(options.apiBaseUrl)\n\n let response: Response\n try {\n response = await fetch(`${base}/site-auth/session`, {\n method: 'GET',\n credentials: 'include',\n headers: { Accept: 'application/json' },\n signal: options.signal,\n })\n } catch {\n // Network error / aborted / CORS — signed-out is the safe probe assumption.\n return SIGNED_OUT\n }\n\n // Old servers reply 401 to an anonymous probe; new servers reply 200-null.\n // Treat any non-OK status as signed-out WITHOUT logging.\n if (!response.ok) {\n return SIGNED_OUT\n }\n\n let data: unknown\n try {\n data = await response.json()\n } catch {\n return SIGNED_OUT\n }\n\n const visitor = extractVisitor(data)\n return { visitor, authenticated: visitor !== null }\n}\n\nexport interface UseSiteVisitorSessionOptions extends FetchSiteVisitorSessionOptions {\n /** Probe automatically on mount (browser only). Default `true`. */\n fetchOnMount?: boolean\n}\n\nexport interface UseSiteVisitorSessionReturn {\n /** The signed-in visitor, or `null` when signed out / not yet loaded. */\n visitor: Ref<SiteVisitor | null>\n /** `true` iff a visitor is present. */\n isAuthenticated: ComputedRef<boolean>\n /** `true` while a probe is in flight. */\n isLoading: Ref<boolean>\n /** Re-run the probe. */\n refresh: () => Promise<void>\n}\n\n/**\n * Vue composable wrapper around {@link fetchSiteVisitorSession}. Reactive\n * `visitor` / `isAuthenticated` / `isLoading`, and probes on mount by default.\n */\nexport function useSiteVisitorSession(\n options: UseSiteVisitorSessionOptions = {},\n): UseSiteVisitorSessionReturn {\n const { fetchOnMount = true, ...fetchOptions } = options\n\n const visitor = ref<SiteVisitor | null>(null)\n const isLoading = ref(false)\n const isAuthenticated = computed(() => visitor.value !== null)\n\n async function refresh(): Promise<void> {\n isLoading.value = true\n try {\n const result = await fetchSiteVisitorSession(fetchOptions)\n visitor.value = result.visitor\n } finally {\n isLoading.value = false\n }\n }\n\n if (fetchOnMount && typeof window !== 'undefined') {\n onMounted(() => {\n void refresh()\n })\n }\n\n return { visitor, isAuthenticated, isLoading, refresh }\n}\n"]}
1
+ {"version":3,"sources":["../src/composables/useConversionTracking.ts","../src/conversionAutoInstall.ts","../src/composables/useTextContent.ts","../src/composables/useSEO.ts","../src/composables/useReleaseNotes.ts","../src/composables/useSiteVersion.ts","../src/composables/useMediaCarousel.ts","../src/composables/useResponsiveImage.ts","../src/composables/useReviewContent.ts","../src/composables/useSiteVisitorSession.ts","../src/index.ts"],"names":["onMounted","computed","getEnvVar","ref","platformFetch","CACHE_TTL"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Conversion-click capture for DCS customer sites.\n *\n * WHY THIS EXISTS\n * ---------------\n * The money-moment on most DCS sites happens on somebody else's domain: KEPT books\n * physical therapy on StrideThera and gym slots on Momence, Just Posh books on Vagaro,\n * and every site has a `tel:` number. We do not own those transactions — but we can own\n * the *number*, and \"your site sent N people to booking last month\" is the single most\n * renewal-relevant sentence we can put in front of a paying owner.\n *\n * Hand-wiring a `@click` handler onto every booking button does not survive contact with\n * a redesign. Evidence, from the first monthly owner reports (2026-07):\n *\n * - KEPT's `BookPTAppointmentClicked` event fired for the last time on 2026-06-03. The\n * header \"Book Online\" link in the `isClassicExperience` branch never got a handler,\n * so the site's single most prominent booking CTA has been invisible ever since —\n * silently, with nothing failing.\n * - Six other KEPT event names went dark in the same window.\n * - Just Posh, which uses *delegated* capture (one document-level listener, classified\n * by href), recorded 95 booking clicks across 11 pages in the same 30 days without a\n * single per-button code change.\n *\n * This module is the delegated approach, generalised for the fleet: one capture-phase\n * click listener, an allow-list of booking hosts, and a classification of every outbound\n * link into `booking` / `phone` / `email` / `social` / `external` / `internal` / `button`.\n *\n * WHY IT WAS NOT ENOUGH (C-326, 2026-07-26)\n * -----------------------------------------\n * Everything above shipped, was exported from the package index, and was consumed by\n * EXACTLY ZERO fleet sites. Two independent same-day reviews proved the cost: Bryan's\n * Handyman ships five `tel:` CTAs — the site's only conversion action — and fires nothing;\n * Kim Duff Homes' App Insights holds 6 pageViews in 365 days. A capability that every site\n * must opt into is a capability every site forgets.\n *\n * So the opt-in is gone. `../conversionAutoInstall` installs this tracker as a side effect\n * of importing `@duffcloudservices/cms` — which every fleet site already does — and finds\n * its own sink (GA4's `window.gtag`, or a telemetry transport that calls the published\n * `window.__dcsConversionAttach`). Nothing in a site repo changes.\n *\n * THREE PROPERTIES THAT MUST SURVIVE ANY EDIT HERE\n * -----------------------------------------------\n * 1. NO PII IN THE PAYLOAD. `tel:` / `sms:` / `mailto:` targets are redacted to a scheme\n * plus a short digest ({@link redactHref}) — never the raw number or address.\n * 2. NO DOUBLE COUNTING. Exactly one delegated listener may be live per document, and a\n * click on a form's submit control is deliberately NOT counted (the `submit` event is).\n * This is the C-288/C-291 discipline: 52% of one site's page views were duplicates\n * because two trackers counted the same navigation, so the shared layer refuses rather\n * than risks it.\n * 3. IT NEVER THROWS INTO A USER'S CLICK.\n *\n * INTERLOCK — MUST SURVIVE APP INSIGHTS SDK DEFERRAL (fleet P3)\n * ------------------------------------------------------------\n * A planned fleet change defers (or lazily loads, or drops) the App Insights browser SDK\n * to buy page-speed budget. Conversion capture MUST NOT be a casualty of that: an owner\n * report that silently reports zero bookings is worse than no report.\n *\n * Therefore this module:\n * 1. never imports the App Insights SDK, and has no dependency on it;\n * 2. starts listening immediately, before any telemetry transport exists;\n * 3. buffers captured events in a bounded FIFO while no sink is attached;\n * 4. drains that buffer the moment a sink attaches — including a sink attached\n * minutes later by a lazy loader via {@link attachConversionSink}.\n *\n * A deferred SDK therefore costs at most a delayed flush, never a lost conversion. If you\n * change this file, keep that property and keep the test that asserts it\n * (`useConversionTracking.test.ts` → \"survives a deferred sink\").\n */\nimport { getCurrentInstance, onBeforeUnmount, onMounted } from 'vue'\n\n/** How a clicked element was classified. */\nexport type ConversionInteractionType =\n | 'booking'\n | 'phone'\n | 'email'\n | 'form_submit'\n | 'social'\n | 'external'\n | 'internal'\n | 'button'\n\n/**\n * The interaction types that ARE the money moment — the ones an owner report counts.\n *\n * Everything else (`social`, `external`, `internal`, `button`) is navigation telemetry and\n * is still captured, but it must never be summed into \"conversions\". Keeping the set here,\n * rather than in each report's query, means one edit changes every consumer.\n */\nexport const CONVERSION_INTERACTION_TYPES: readonly ConversionInteractionType[] = [\n 'booking',\n 'phone',\n 'email',\n 'form_submit',\n]\n\n/** Whether an interaction type counts as a conversion. */\nexport function isConversionType(type: ConversionInteractionType): boolean {\n return CONVERSION_INTERACTION_TYPES.includes(type)\n}\n\n/** A captured conversion event, in App Insights `trackEvent` shape. */\nexport interface ConversionEvent {\n /** Event name — `site_interaction` by default (see {@link ConversionTrackingOptions.eventName}). */\n name: string\n /** Flat string properties; App Insights `customDimensions`. */\n properties: {\n interaction_type: ConversionInteractionType\n /**\n * `'true'` when {@link isConversionType} holds. A string, not a boolean, because App\n * Insights `customDimensions` and GA4 event params are both string maps — so the\n * owner-report query is one predicate (`is_conversion == \"true\"`) instead of an\n * interaction-type IN-list that every new report has to remember to keep in sync.\n */\n is_conversion: string\n /** Visible label / aria-label of the clicked element, truncated. */\n label: string\n /**\n * Destination, query string and fragment stripped, and REDACTED for contact schemes:\n * a `tel:` / `sms:` / `mailto:` href becomes `tel:#<digest>` — never the raw number or\n * address. See {@link redactHref}. Empty for buttons and form submits.\n */\n href: string\n /** URL scheme of the destination including the colon (`tel:`, `https:`), or `''`. */\n href_scheme: string\n /** Host of the destination, or `''` for buttons, form submits and contact schemes. */\n href_host: string\n /**\n * Short digest of the contact target (the phone number / email address), or `''` for\n * everything else. Lets a report say \"CTA A got 12 taps, CTA B got 3\" without ever\n * storing the contact string itself.\n */\n href_hash: string\n /** Path of the page the click happened on. */\n page_path: string\n /** Host of the page the click happened on. */\n host: string\n /** Schema version, so a report can tell old rows from new ones. */\n capture_version: string\n }\n}\n\n/** A telemetry transport. Typically `(e) => telemetry.trackEvent(e)`. */\nexport type ConversionSink = (event: ConversionEvent) => void\n\nexport interface ConversionTrackingOptions {\n /**\n * Where to send events. Optional on purpose — omit it when the telemetry SDK is\n * deferred, and call {@link attachConversionSink} once it has loaded. Events captured\n * in the meantime are buffered, not dropped.\n */\n sink?: ConversionSink\n /**\n * Fire-and-forget transports that receive every event AS WELL AS `sink`, and that do NOT\n * count as \"a sink exists\" for buffering purposes.\n *\n * This distinction is load-bearing. GA4's `gtag` is a mirror: the deploy injects it as a\n * synchronous inline snippet, so it is either there when the click happens or the hit is\n * genuinely unavailable — there is nothing to wait for. App Insights is a `sink`: it boots\n * on an idle callback minutes later, which is exactly what the buffer exists to survive.\n * Treating GA4 as a `sink` would have satisfied the \"do we have somewhere to send this?\"\n * test on every site and quietly disabled the deferred-SDK interlock.\n */\n mirrors?: ConversionSink[]\n /**\n * Custom event name. Defaults to `site_interaction` — the name Just Posh has been\n * emitting since 2026-04, so its history stays one continuous series. Pass\n * `'dcs_conversion'` on a site with no existing history if you prefer the canonical name.\n */\n eventName?: string\n /**\n * Extra hostnames to treat as booking destinations, on top of {@link DEFAULT_BOOKING_HOSTS}.\n * Matched on host suffix, so `vagaro.com` also matches `www.vagaro.com`.\n */\n bookingHosts?: string[]\n /**\n * Same-origin paths that mean \"booking\" (e.g. a self-hosted `/book`). Matched as a\n * prefix on the pathname.\n */\n bookingPaths?: string[]\n /** Extra social hostnames on top of {@link DEFAULT_SOCIAL_HOSTS}. */\n socialHosts?: string[]\n /** Max events held while no sink is attached. Default 50 — bounded so a bot cannot grow it. */\n bufferLimit?: number\n /** Drop untrusted (script-dispatched) clicks. Default `false`. */\n requireTrusted?: boolean\n /** Document to bind to. Defaults to the ambient `document`. Injected in tests. */\n target?: Document\n /**\n * Also capture managed-form submissions as `form_submit`. Default `true`.\n *\n * A form submit is a conversion on every DCS site that has a form, and it is the one\n * affordance a click listener alone cannot see honestly: clicking \"Send\" on a form that\n * then fails validation is not a lead. So the `submit` event — not the click — is\n * authoritative, and clicks on submit controls are deliberately dropped to keep the two\n * from counting the same action twice.\n */\n captureFormSubmits?: boolean\n /**\n * Honour the visitor's Do Not Track signal. Default `true`.\n *\n * With DNT on, `start()` binds nothing at all — no listener, no buffer, no event. This\n * is a measurement rail, not a consent platform: if a site ever grows a real consent\n * banner, gate {@link installConversionCapture} on it rather than weakening this.\n */\n respectDoNotTrack?: boolean\n /**\n * Bind a second delegated listener even though one is already live on this document.\n *\n * Off by default and it should stay off. Two delegated listeners on one document count\n * every click twice — the same defect class C-288 measured as 52% duplicate page views\n * on a live customer site, which the portal then reported to the owner as traffic. If\n * you are reaching for this, you almost certainly want `stop()` on the existing tracker.\n */\n force?: boolean\n}\n\n/**\n * Schema version stamped on every captured event. Bump on a breaking property change.\n *\n * `2` (C-326): `href` is redacted for `tel:`/`sms:`/`mailto:`, and `is_conversion`,\n * `href_scheme` + `href_hash` were added. Version `1` rows carry raw contact hrefs and no\n * conversion flag, so a report spanning the boundary must branch on this.\n */\nexport const CONVERSION_CAPTURE_VERSION = '2'\n\n/**\n * Booking/scheduling vendors seen across the DCS fleet plus the common SMB schedulers.\n * Host-suffix matched. Add per-site extras via `bookingHosts` rather than editing this.\n */\nexport const DEFAULT_BOOKING_HOSTS: readonly string[] = [\n 'stridethera.com', // KEPT — physical therapy\n 'momence.com', // KEPT — gym / recovery\n 'vagaro.com', // Just Posh\n 'acuityscheduling.com',\n 'booksy.com',\n 'calendly.com',\n 'fresha.com',\n 'janeapp.com',\n 'mindbodyonline.com',\n 'schedulicity.com',\n 'setmore.com',\n 'simplepractice.com',\n 'square.site',\n 'squareup.com',\n]\n\n/** Social destinations. Host-suffix matched. */\nexport const DEFAULT_SOCIAL_HOSTS: readonly string[] = [\n 'facebook.com',\n 'instagram.com',\n 'linkedin.com',\n 'pinterest.com',\n 'threads.net',\n 'tiktok.com',\n 'x.com',\n 'youtube.com',\n]\n\nconst LABEL_MAX_LENGTH = 120\nconst DEFAULT_BUFFER_LIMIT = 50\n/** Identical events inside this window are collapsed (guards double-fired handlers). */\nconst DEDUPE_WINDOW_MS = 400\n\nfunction hostMatches(host: string, patterns: readonly string[]): boolean {\n const h = host.toLowerCase()\n return patterns.some((p) => {\n const needle = p.toLowerCase()\n return h === needle || h.endsWith(`.${needle}`)\n })\n}\n\n/** Strip query + fragment. A booking URL can carry a name or email in its query string. */\nfunction sanitizeUrl(raw: string): string {\n const trimmed = raw.trim()\n if (!trimmed) return ''\n const cut = trimmed.split('#')[0].split('?')[0]\n return cut.slice(0, 300)\n}\n\n/** Schemes whose target is a contact string and must never be emitted verbatim. */\nconst CONTACT_SCHEMES = ['tel:', 'sms:', 'mailto:'] as const\n\n/**\n * FNV-1a (32-bit), base36. Synchronous on purpose: this runs inside a click handler, where\n * `crypto.subtle` — the only real hash a browser offers — is async and would force the\n * event to be built after the navigation has already started.\n *\n * BE HONEST ABOUT WHAT THIS IS. It is not anonymisation. A site publishes two or three\n * phone numbers, so anybody holding the site could brute-force the digest back in\n * milliseconds. What it buys is real but narrow: the contact string never lands in an\n * analytics store (GA4 forbids PII in event params outright), a support screenshot of the\n * events table cannot leak a customer's mailbox, and the value is still stable enough to\n * answer \"which CTA did they tap\". Do not describe it as anything more than that.\n */\nexport function hashTarget(value: string): string {\n let h = 0x811c9dc5\n for (let i = 0; i < value.length; i += 1) {\n h ^= value.charCodeAt(i)\n h = Math.imul(h, 0x01000193) >>> 0\n }\n return h.toString(36)\n}\n\n/** The scheme of an href, including the colon (`tel:`, `https:`), or `''`. */\nexport function hrefScheme(href: string, pageHost: string): string {\n const raw = href.trim()\n if (!raw) return ''\n const lower = raw.toLowerCase()\n const contact = CONTACT_SCHEMES.find((s) => lower.startsWith(s))\n if (contact) return contact\n if (lower.startsWith('#')) return ''\n try {\n return new URL(raw, `https://${pageHost || 'localhost'}/`).protocol\n } catch {\n return ''\n }\n}\n\n/**\n * Split an href into the parts that are safe to emit.\n *\n * For `tel:` / `sms:` / `mailto:` the target is a person's or business's contact string, so\n * it is replaced by `<scheme>#<digest>` and the digest is also surfaced on its own. For\n * everything else the href passes through {@link sanitizeUrl} unchanged — a booking URL's\n * path is the useful part and its query (which CAN carry a name or email) is already gone.\n */\nexport function redactHref(\n href: string,\n pageHost: string,\n): { href: string; scheme: string; hash: string } {\n const raw = href.trim()\n if (!raw) return { href: '', scheme: '', hash: '' }\n\n const lower = raw.toLowerCase()\n const contact = CONTACT_SCHEMES.find((s) => lower.startsWith(s))\n if (contact) {\n // Normalise before hashing so `tel:+1 (248) 385-2926` and `tel:+12483852926` — the\n // same CTA written two ways in one codebase — do not split into two rows.\n const target = raw.slice(contact.length).split('?')[0].trim().toLowerCase()\n const normalized =\n contact === 'mailto:' ? target : target.replace(/[^0-9+]/gu, '')\n const hash = normalized ? hashTarget(normalized) : ''\n return { href: `${contact}#${hash}`, scheme: contact, hash }\n }\n\n return { href: sanitizeUrl(raw), scheme: hrefScheme(raw, pageHost), hash: '' }\n}\n\n/**\n * Whether this element is the control that submits a form.\n *\n * Clicks on these are dropped so the `submit` event can be the single source of truth —\n * see {@link ConversionTrackingOptions.captureFormSubmits}. Note the HTML default: a\n * `<button>` inside a form with no `type` IS a submit button, which is exactly how every\n * `<DcsForm>` renders its action.\n */\nexport function isSubmitControl(el: Element): boolean {\n const tag = el.tagName.toLowerCase()\n if (tag !== 'button' && tag !== 'input') return false\n const type = (el.getAttribute('type') ?? '').toLowerCase()\n if (type === 'submit') return true\n if (type) return false\n return tag === 'button' && !!el.closest('form')\n}\n\n/** Read the visitor's Do Not Track signal across the three places browsers have put it. */\nexport function doNotTrackEnabled(): boolean {\n if (typeof navigator === 'undefined' && typeof window === 'undefined') return false\n const nav =\n typeof navigator !== 'undefined'\n ? (navigator as Navigator & { msDoNotTrack?: string })\n : undefined\n const win =\n typeof window !== 'undefined' ? (window as Window & { doNotTrack?: string }) : undefined\n const signal = nav?.doNotTrack ?? win?.doNotTrack ?? nav?.msDoNotTrack\n return signal === '1' || signal === 'yes'\n}\n\nfunction labelFor(element: Element): string {\n const aria = element.getAttribute('aria-label')\n const source =\n (aria && aria.trim()) ||\n (element.textContent ?? '').replace(/\\s+/gu, ' ').trim() ||\n element.getAttribute('title') ||\n element.getAttribute('href') ||\n element.tagName.toLowerCase()\n return source.slice(0, LABEL_MAX_LENGTH)\n}\n\n/**\n * Classify a link.\n *\n * ORDER IS LOAD-BEARING and is asserted by tests. Protocol wins first (a `tel:` href has\n * no host to match on), then booking — *before* the same-origin and social checks — so a\n * self-hosted `/book` path and a booking vendor that also runs a social profile are both\n * attributed to revenue rather than to `internal` / `social`.\n */\nexport function classifyHref(\n href: string,\n opts: {\n pageHost: string\n bookingHosts: readonly string[]\n bookingPaths: readonly string[]\n socialHosts: readonly string[]\n },\n): ConversionInteractionType {\n const raw = href.trim()\n if (!raw) return 'button'\n\n const lower = raw.toLowerCase()\n if (lower.startsWith('tel:') || lower.startsWith('sms:')) return 'phone'\n if (lower.startsWith('mailto:')) return 'email'\n // Non-navigational anchors (`#`, `javascript:`) behave like buttons.\n if (lower.startsWith('#') || lower.startsWith('javascript:')) return 'button'\n\n let url: URL\n try {\n url = new URL(raw, `https://${opts.pageHost || 'localhost'}/`)\n } catch {\n return 'internal'\n }\n\n if (hostMatches(url.hostname, opts.bookingHosts)) return 'booking'\n\n const sameOrigin =\n !!opts.pageHost && url.hostname.toLowerCase() === opts.pageHost.toLowerCase()\n if (sameOrigin && opts.bookingPaths.some((p) => url.pathname.startsWith(p))) {\n return 'booking'\n }\n if (hostMatches(url.hostname, opts.socialHosts)) return 'social'\n if (sameOrigin) return 'internal'\n if (url.protocol === 'http:' || url.protocol === 'https:') return 'external'\n return 'external'\n}\n\n/** The object returned by {@link createConversionTracker}. */\nexport interface ConversionTracker {\n /** Bind the capture-phase click listener. Idempotent. */\n start(): void\n /** Unbind and clear the buffer. Idempotent. */\n stop(): void\n /** Attach (or replace) the sink and immediately drain anything buffered. */\n attachSink(sink: ConversionSink): void\n /** Events currently held because no sink is attached. Read-only copy. */\n buffered(): ConversionEvent[]\n /** Capture a click target directly. Exposed for tests and manual instrumentation. */\n capture(element: Element): ConversionEvent | null\n}\n\n/**\n * Module-level sink registry.\n *\n * A lazy telemetry loader usually does not hold a reference to the tracker — it just\n * finished `loadAppInsights()` and wants to start receiving. This lets it say so from\n * anywhere, which is the ergonomic that makes the SDK-deferral interlock actually get used.\n */\nconst registeredTrackers = new Set<ConversionTracker>()\n\n/**\n * Documents that already have a delegated listener bound.\n *\n * THE DOUBLE-COUNT RAIL. Two capture-phase listeners on one document emit two events per\n * click, and the resulting number looks entirely plausible — which is why C-288 shipped\n * 52% duplicate page views to a customer's owner report before anyone noticed. Since\n * C-326 the tracker auto-installs, so the realistic collision is a site that ALSO follows\n * the old docs and mounts `useConversionTracking()` in `App.vue`. That second `start()`\n * refuses, loudly, instead of silently doubling the site's conversion count.\n */\nconst boundDocuments = new WeakSet<Document>()\nlet warnedDoubleBind = false\n\n/**\n * Attach a sink to every live tracker and drain their buffers.\n *\n * Call this from a deferred/lazy App Insights loader once the SDK is ready:\n *\n * ```ts\n * const { ApplicationInsights } = await import('@microsoft/applicationinsights-web')\n * const ai = new ApplicationInsights({ config })\n * ai.loadAppInsights()\n * attachConversionSink((e) => ai.trackEvent({ name: e.name, properties: e.properties }))\n * ```\n *\n * @returns how many trackers received the sink.\n */\nexport function attachConversionSink(sink: ConversionSink): number {\n let count = 0\n for (const tracker of registeredTrackers) {\n tracker.attachSink(sink)\n count += 1\n }\n return count\n}\n\n/**\n * Framework-free conversion tracker. Vue callers usually want\n * {@link useConversionTracking} instead.\n */\nexport function createConversionTracker(\n options: ConversionTrackingOptions = {},\n): ConversionTracker {\n const eventName = options.eventName ?? 'site_interaction'\n const bookingHosts = [...DEFAULT_BOOKING_HOSTS, ...(options.bookingHosts ?? [])]\n const socialHosts = [...DEFAULT_SOCIAL_HOSTS, ...(options.socialHosts ?? [])]\n const bookingPaths = options.bookingPaths ?? []\n const bufferLimit = Math.max(0, options.bufferLimit ?? DEFAULT_BUFFER_LIMIT)\n const requireTrusted = options.requireTrusted ?? false\n const captureFormSubmits = options.captureFormSubmits ?? true\n const respectDoNotTrack = options.respectDoNotTrack ?? true\n const force = options.force ?? false\n\n const mirrors = options.mirrors ?? []\n let sink: ConversionSink | null = options.sink ?? null\n let buffer: ConversionEvent[] = []\n let listening = false\n let lastKey = ''\n let lastAt = 0\n\n const doc = (): Document | null =>\n options.target ?? (typeof document !== 'undefined' ? document : null)\n\n function pageHost(): string {\n return typeof window !== 'undefined' ? window.location.hostname : ''\n }\n\n function pagePath(): string {\n return typeof window !== 'undefined' ? window.location.pathname : ''\n }\n\n function emit(event: ConversionEvent): void {\n for (const mirror of mirrors) {\n try {\n mirror(event)\n } catch {\n // A broken transport must never break the site's click handling.\n }\n }\n\n if (sink) {\n try {\n sink(event)\n } catch {\n // Same contract as the mirrors.\n }\n return\n }\n // No sink yet (deferred SDK). Buffer, bounded, oldest-out. Mirrors above already\n // received this event, so a GA4-only site loses nothing by never attaching a sink.\n if (bufferLimit === 0) return\n buffer.push(event)\n if (buffer.length > bufferLimit) buffer.shift()\n }\n\n /** Assemble an event from an already-classified actionable element. */\n function compose(\n actionable: Element,\n interactionType: ConversionInteractionType,\n rawHref: string,\n ): ConversionEvent {\n const host = pageHost()\n const { href, scheme, hash } = redactHref(rawHref, host)\n\n let hrefHost = ''\n // Contact schemes have no host, and asking for one would mean parsing the very string\n // that was just redacted.\n if (href && !hash) {\n try {\n hrefHost = new URL(href, `https://${host || 'localhost'}/`).hostname\n } catch {\n hrefHost = ''\n }\n }\n\n return {\n name: eventName,\n properties: {\n interaction_type: interactionType,\n is_conversion: isConversionType(interactionType) ? 'true' : 'false',\n label: labelFor(actionable),\n href,\n href_scheme: scheme,\n href_host: hrefHost,\n href_hash: hash,\n page_path: pagePath(),\n host,\n capture_version: CONVERSION_CAPTURE_VERSION,\n },\n }\n }\n\n function build(element: Element): ConversionEvent | null {\n const anchor = element.closest('a')\n const actionable: Element | null = anchor ?? element.closest('button')\n if (!actionable) return null\n\n // A click on a form's submit control is NOT the event — the `submit` that follows is\n // (and only follows if validation passed). Dropping it here is what keeps a single\n // \"Send\" tap from being counted as both a `button` click and a `form_submit`.\n if (captureFormSubmits && !anchor && isSubmitControl(actionable)) return null\n\n const rawHref = anchor?.getAttribute('href') ?? ''\n const interactionType = anchor\n ? classifyHref(rawHref, { pageHost: pageHost(), bookingHosts, bookingPaths, socialHosts })\n : 'button'\n\n // Resolve relative hrefs through the anchor's own `.href`, but keep contact schemes on\n // the raw attribute — jsdom and browsers both normalise `tel:` inconsistently.\n const resolved =\n anchor && !CONTACT_SCHEMES.some((s) => rawHref.trim().toLowerCase().startsWith(s))\n ? (anchor as HTMLAnchorElement).href || rawHref\n : rawHref\n\n return compose(actionable, interactionType, resolved)\n }\n\n /** Build the event for a managed/bespoke form that actually submitted. */\n function buildSubmit(form: Element): ConversionEvent {\n // Prefer the submit control's own label (\"Request a Consultation\") over the form's\n // full text content, which on a real intake form is every field label concatenated.\n const control =\n form.querySelector('button[type=\"submit\"], input[type=\"submit\"]') ??\n form.querySelector('button') ??\n form\n return compose(control, 'form_submit', '')\n }\n\n /** Shared dedupe gate: collapses an identical event fired twice in quick succession. */\n function emitDeduped(event: ConversionEvent): ConversionEvent | null {\n const key = `${event.properties.interaction_type}|${event.properties.href}|${event.properties.label}`\n const now = Date.now()\n if (key === lastKey && now - lastAt < DEDUPE_WINDOW_MS) return null\n lastKey = key\n lastAt = now\n\n emit(event)\n return event\n }\n\n function capture(element: Element): ConversionEvent | null {\n const event = build(element)\n if (!event) return null\n return emitDeduped(event)\n }\n\n const onClick = (event: Event): void => {\n if (requireTrusted && !(event as MouseEvent).isTrusted) return\n const target = event.target\n if (!target || !(target as Element).closest) return\n try {\n capture(target as Element)\n } catch {\n // Never let telemetry throw inside a user's click.\n }\n }\n\n const onSubmit = (event: Event): void => {\n if (requireTrusted && !event.isTrusted) return\n const form = event.target\n if (!form || !(form as Element).tagName) return\n try {\n emitDeduped(buildSubmit(form as Element))\n } catch {\n // Never let telemetry throw inside a user's submit.\n }\n }\n\n const tracker: ConversionTracker = {\n start() {\n if (listening) return\n const d = doc()\n if (!d) return\n\n if (respectDoNotTrack && doNotTrackEnabled()) {\n // Bind nothing. Not even the buffer — a visitor who asked not to be tracked should\n // not have their clicks sitting in memory waiting for a sink to show up.\n return\n }\n\n if (boundDocuments.has(d) && !force) {\n if (!warnedDoubleBind) {\n warnedDoubleBind = true\n console.warn(\n '[dcs-conversion] start() REFUSED: a delegated conversion listener is already ' +\n 'bound to this document. Binding a second one counts every click twice — the ' +\n 'defect class that put 52% duplicate page views in a live owner report ' +\n '(C-288). Since C-326 the tracker auto-installs with @duffcloudservices/cms, ' +\n 'so a manual useConversionTracking() in App.vue is no longer needed; remove ' +\n 'it, or pass { force: true } if you have genuinely torn the first one down.',\n )\n }\n return\n }\n\n // Capture phase: fires even when a handler downstream calls stopPropagation().\n d.addEventListener('click', onClick, { capture: true })\n if (captureFormSubmits) d.addEventListener('submit', onSubmit, { capture: true })\n listening = true\n boundDocuments.add(d)\n registeredTrackers.add(tracker)\n },\n stop() {\n const d = doc()\n if (d && listening) {\n d.removeEventListener('click', onClick, { capture: true })\n if (captureFormSubmits) d.removeEventListener('submit', onSubmit, { capture: true })\n boundDocuments.delete(d)\n }\n listening = false\n buffer = []\n registeredTrackers.delete(tracker)\n },\n attachSink(next: ConversionSink) {\n sink = next\n const pending = buffer\n buffer = []\n for (const event of pending) {\n try {\n next(event)\n } catch {\n // Same contract as emit(): a broken transport is not the site's problem.\n }\n }\n },\n buffered() {\n return [...buffer]\n },\n capture,\n }\n\n return tracker\n}\n\n/**\n * Vue composable: start conversion capture for the lifetime of the calling component.\n *\n * YOU PROBABLY DO NOT NEED THIS ANY MORE. Since C-326, importing `@duffcloudservices/cms`\n * auto-installs a tracker (`installConversionCapture`), so every fleet site captures\n * conversions with no site-repo code at all. Calling this on top of that is a no-op with a\n * loud console warning — `start()` refuses to bind a second listener to a document that\n * already has one, because two listeners double every conversion count.\n *\n * It remains exported for the cases the auto-install deliberately does not cover: a site\n * that needs per-site `bookingHosts`/`bookingPaths`, or a non-cms consumer wiring capture\n * by hand. In the first case, prefer configuring the auto-installer:\n *\n * ```ts\n * import { installConversionCapture } from '@duffcloudservices/cms'\n * installConversionCapture({ bookingHosts: ['stridethera.com'], bookingPaths: ['/book'] })\n * ```\n *\n * Returns the tracker so a caller can attach a sink or inspect the buffer. Safe on the\n * server: with no `document`, `start()` is a no-op.\n */\nexport function useConversionTracking(\n options: ConversionTrackingOptions = {},\n): ConversionTracker {\n const tracker = createConversionTracker(options)\n\n // Inside a component: bind to its lifecycle. Outside one (plain-JS site entry, a\n // module-level call), start immediately — SSR/prerender is covered because `start()`\n // no-ops without a `document`.\n if (getCurrentInstance()) {\n onMounted(() => tracker.start())\n onBeforeUnmount(() => tracker.stop())\n } else {\n tracker.start()\n }\n\n return tracker\n}\n","/**\n * Fleet-wide conversion capture, installed without asking the site.\n *\n * WHY THIS FILE EXISTS\n * --------------------\n * `useConversionTracking` shipped in @duffcloudservices/cms, was exported from the package\n * index, was documented with an example, and was adopted by ZERO of the eleven fleet sites.\n * Two independent reviews on 2026-07-26 measured what that cost (C-326):\n *\n * - Bryan's Handyman Solutions exists to generate phone calls. It ships five `tel:` CTAs\n * — hero, header, mobile icon, sticky call bar, footer — and every one of them fires\n * nothing. `trackEvent` appears in the bundle exactly once: as the composable's own\n * unused export. Its GA4 property was never created either, so the site has no\n * measurement of any kind for the only action it is built to produce.\n * - Kim Duff Homes, a flagship account, has 6 pageViews in App Insights across 365 days.\n *\n * The lesson is not \"write better docs\". A capability the site must remember to switch on\n * is a capability that is off. So capture is a SIDE EFFECT of importing the package every\n * fleet site already depends on, and it finds its own transport.\n *\n * HOW A SITE'S EVENTS GET OUT — three sinks, in the order they are usually available\n * ---------------------------------------------------------------------------------\n * 1. GA4 (`window.gtag`). The deploy workflow injects the gtag snippet into every built\n * HTML file when `.dcs/site.yaml` carries a `google_analytics_id`. That means a site\n * with GA4 configured captures conversions with LITERALLY no code change — which is\n * precisely why the empty-`google_analytics_id` launch gate is the other half of C-326.\n * 2. `window.__dcsConversionAttach(sink)` — a one-line, import-free contract published by\n * this module for any telemetry transport that boots later. `@duffcloudservices/telemetry`\n * calls it automatically inside `initialize()`; a site with a bespoke App Insights boot\n * adds the single line. Events captured before that arrive are buffered, not lost.\n * 3. `attachConversionSink(sink)` — the direct API, for callers holding the import.\n *\n * WHERE IT DELIBERATELY DOES NOTHING\n * ----------------------------------\n * - No `document` (SSR, VitePress build, the Playwright prerender pass).\n * - Inside an iframe — the DCS visual editor and preview surfaces frame the site, and\n * an editor clicking around a customer's page is not a lead.\n * - Do Not Track is on.\n * - The page opted out via `window.__dcsConversionOptOut = true` or\n * `<html data-dcs-analytics=\"off\">`.\n *\n * In every one of those cases it binds no listener and buffers nothing.\n */\nimport {\n attachConversionSink,\n createConversionTracker,\n doNotTrackEnabled,\n type ConversionEvent,\n type ConversionSink,\n type ConversionTracker,\n type ConversionTrackingOptions,\n} from './composables/useConversionTracking'\n\n/** Globals this module reads or publishes. Declared once so the reads stay typed. */\ntype ConversionWindow = Window & {\n gtag?: (command: string, ...args: unknown[]) => void\n __dcsConversionOptOut?: boolean\n __dcsConversionAttach?: (sink: ConversionSink) => number\n __dcsConversionInstalled?: boolean\n}\n\n/** Options for {@link installConversionCapture}. */\nexport interface InstallConversionCaptureOptions\n extends Omit<ConversionTrackingOptions, 'sink' | 'mirrors' | 'force'> {\n /**\n * Emit through GA4's `window.gtag` when it is present. Default `true`.\n *\n * Looked up at emit time, not install time: the gtag snippet is `async`, so on a fast\n * connection the site's JS can install this before Google's script has defined `gtag`.\n */\n gtag?: boolean\n}\n\n/**\n * The module-level singleton.\n *\n * Idempotence matters more here than anywhere else in the package: this runs on import, and\n * a site whose bundler produces two copies of the module (a pnpm-hoisting accident, a\n * duplicated dependency) would otherwise install two listeners and double every conversion.\n * The `window.__dcsConversionInstalled` flag closes that across module instances; the local\n * `installed` closes it within one.\n */\nlet installed: ConversionTracker | null = null\n\nfunction conversionWindow(): ConversionWindow | null {\n return typeof window !== 'undefined' ? (window as ConversionWindow) : null\n}\n\n/** Whether the page has explicitly asked not to be measured. */\nexport function conversionOptedOut(): boolean {\n const w = conversionWindow()\n if (w?.__dcsConversionOptOut === true) return true\n if (typeof document !== 'undefined') {\n const flag = document.documentElement?.getAttribute('data-dcs-analytics')\n if (flag === 'off' || flag === 'false') return true\n }\n return false\n}\n\n/**\n * Whether this document is being rendered inside a frame.\n *\n * The DCS portal embeds customer sites in an iframe for the visual editor and preview\n * (`dcsEditorPlugin` keys off the same `window.parent !== window` test). Counting an\n * editor's clicks as customer conversions would inflate exactly the number the owner\n * report is supposed to make trustworthy.\n */\nexport function inFrame(): boolean {\n const w = conversionWindow()\n if (!w) return false\n try {\n return w.parent !== w || w.top !== w.self\n } catch {\n // Cross-origin framing throws on access — which is itself proof we are framed.\n return true\n }\n}\n\n/** Reason the installer declined, or `null` when it installed. */\nexport type ConversionInstallSkipReason =\n | 'no-document'\n | 'already-installed'\n | 'in-frame'\n | 'do-not-track'\n | 'opted-out'\n\n/** Result of an {@link installConversionCapture} call. */\nexport interface ConversionInstallResult {\n installed: boolean\n reason: ConversionInstallSkipReason | null\n tracker: ConversionTracker | null\n}\n\n/**\n * Send an event to GA4 if the page has one.\n *\n * GA4 event params are a flat string map and must not carry PII — which the event's\n * properties already satisfy, because `redactHref` replaced any contact string with a\n * digest before the event was ever built.\n */\nfunction gtagSink(event: ConversionEvent): void {\n const w = conversionWindow()\n if (typeof w?.gtag !== 'function') return\n w.gtag('event', event.name, { ...event.properties })\n}\n\n/**\n * Install fleet conversion capture. Idempotent; safe to call from anywhere, any number of\n * times, on the server or in a frame.\n *\n * Called for its side effect from the package index, so a site gets this by importing\n * `@duffcloudservices/cms` at all. Call it directly only to pass per-site configuration\n * (extra `bookingHosts`, a self-hosted `bookingPaths`) — do that BEFORE the first click,\n * i.e. at app entry, since the first call wins.\n */\nexport function installConversionCapture(\n options: InstallConversionCaptureOptions = {},\n): ConversionInstallResult {\n const skip = (reason: ConversionInstallSkipReason): ConversionInstallResult => ({\n installed: false,\n reason,\n tracker: null,\n })\n\n if (typeof document === 'undefined') return skip('no-document')\n\n const w = conversionWindow()\n if (installed || w?.__dcsConversionInstalled === true) return skip('already-installed')\n if (inFrame()) return skip('in-frame')\n if ((options.respectDoNotTrack ?? true) && doNotTrackEnabled()) return skip('do-not-track')\n if (conversionOptedOut()) return skip('opted-out')\n\n const useGtag = options.gtag ?? true\n\n // GA4 goes in as a MIRROR, never as the sink. `gtagSink` re-reads `window.gtag` on every\n // event (so a snippet that lands after install still gets the hit), and because it is a\n // mirror it does not satisfy the tracker's \"a sink exists\" test — which keeps the\n // deferred-App-Insights buffer armed on GA4 sites instead of silently disabling it.\n const tracker = createConversionTracker({\n ...options,\n sink: undefined,\n mirrors: useGtag ? [gtagSink] : [],\n })\n tracker.start()\n\n installed = tracker\n if (w) {\n w.__dcsConversionInstalled = true\n // The import-free contract. The sink lands as the tracker's PRIMARY, which drains the\n // buffer; GA4 keeps receiving separately as a mirror. Two stores, one event in each —\n // not a double count within either.\n w.__dcsConversionAttach = (sink: ConversionSink): number => attachConversionSink(sink)\n }\n\n return { installed: true, reason: null, tracker }\n}\n\n/** The installed tracker, or `null`. Exposed for tests and diagnostics. */\nexport function installedConversionTracker(): ConversionTracker | null {\n return installed\n}\n\n/**\n * Tear the auto-installed tracker down and clear the global flags.\n *\n * Exists for tests and for a site that genuinely needs to re-install with different\n * options. Not part of the normal runtime path.\n */\nexport function resetConversionCapture(): void {\n installed?.stop()\n installed = null\n const w = conversionWindow()\n if (w) {\n delete w.__dcsConversionInstalled\n delete w.__dcsConversionAttach\n }\n}\n","/**\n * useTextContent Composable\n *\n * Provides text content management with build-time injection support and optional\n * runtime API overrides for DCS-managed customer sites.\n *\n * Content resolution order:\n * 1. Runtime API overrides (premium tier only, if mode is 'runtime')\n * 2. Build-time content from .dcs/content.yaml (injected via dcsContentPlugin)\n * 3. Hardcoded defaults passed to the composable\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useTextContent } from '@duffcloudservices/cms'\n *\n * const { t, isLoading, error } = useTextContent({\n * pageSlug: 'home',\n * defaults: {\n * 'hero.title': 'Welcome to Our Site',\n * 'hero.subtitle': 'Build something amazing',\n * 'cta.primary': 'Get Started'\n * }\n * })\n * </script>\n *\n * <template>\n * <h1>{{ t('hero.title') }}</h1>\n * <p>{{ t('hero.subtitle') }}</p>\n * <button>{{ t('cta.primary') }}</button>\n * </template>\n * ```\n */\n\nimport { platformFetch } from '@duffcloudservices/cms-core'\nimport { ref, computed, readonly, onMounted, type Ref } from 'vue'\nimport type { DcsContentFile, TextContentConfig, TextContentReturn } from '../types/content'\n\n// Declare the global injected by dcsContentPlugin\ndeclare const __DCS_CONTENT__: DcsContentFile | undefined\n\n// Simple in-memory cache for runtime fetches\nconst fetchCache = new Map<string, { data: Record<string, string>; expiresAt: number }>()\n\n/**\n * Safely get build-time content configuration.\n * Returns undefined if not available (no content.yaml or plugin not configured).\n */\nfunction getBuildTimeContent(): DcsContentFile | undefined {\n try {\n if (typeof __DCS_CONTENT__ !== 'undefined' && __DCS_CONTENT__ !== null) {\n return __DCS_CONTENT__\n }\n } catch {\n // __DCS_CONTENT__ not defined - that's fine, use defaults\n }\n return undefined\n}\n\n/**\n * Get build-time content for a specific page, merging global and page-specific content.\n */\nfunction getBuildTimePageContent(pageSlug: string): Record<string, string> {\n const content = getBuildTimeContent()\n if (!content) return {}\n\n const global = content.global ?? {}\n const page = content.pages?.[pageSlug] ?? {}\n\n return { ...global, ...page }\n}\n\n/**\n * Get environment variable value, handling both Vite and process.env patterns.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n // Vite pattern\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n\n try {\n // Node.js pattern\n if (typeof process !== 'undefined' && process.env) {\n const value = process.env[key]\n if (value !== undefined) return value\n }\n } catch {\n // process not available\n }\n\n return defaultValue\n}\n\n/**\n * useTextContent composable for DCS-managed text content.\n *\n * @param config - Configuration object\n * @returns Text content helpers and state\n */\nexport function useTextContent(config: TextContentConfig): TextContentReturn {\n const {\n pageSlug,\n defaults,\n fetchOnMount = true,\n cacheKey,\n cacheTtl = 60000,\n } = config\n\n // Get configuration from environment.\n // @deprecated VITE_SITE_SLUG: the site is now resolved server-side from the\n // request Host or the dedicated Container App's DCS_SITE_SLUG. It is still read\n // here for cache-key continuity and source compatibility, but is no longer used\n // to build the request URL.\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', '')\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\n const textOverrideMode = getEnvVar('VITE_TEXT_OVERRIDE_MODE', 'commit')\n const mode: 'commit' | 'runtime' = textOverrideMode === 'runtime' ? 'runtime' : 'commit'\n\n // Get build-time content immediately (synchronous)\n const buildTimeContent = getBuildTimePageContent(pageSlug)\n const hasBuildTimeContent = Object.keys(buildTimeContent).length > 0\n\n // State - initialize with build-time content\n const overrides = ref<Record<string, string>>({ ...buildTimeContent })\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n // Computed merged texts (defaults + overrides)\n const texts = computed(() => ({ ...defaults, ...overrides.value }))\n\n /**\n * Get text by key with optional fallback.\n * Resolution order: overrides → defaults → fallback → key\n */\n function t(key: string, fallback?: string): string {\n return overrides.value[key] ?? defaults[key] ?? fallback ?? key\n }\n\n /**\n * Check if a key has an override (from build-time or runtime).\n */\n function hasOverride(key: string): boolean {\n return key in overrides.value\n }\n\n /**\n * Get an array of objects from indexed content keys.\n * Useful for lists where keys follow the pattern: arrayKey.index.property\n * Example: features.1.title, features.1.description, features.2.title, etc.\n *\n * @param arrayKey - The base key prefix (e.g., 'features', 'items')\n * @returns Array of objects with properties extracted from matching keys, sorted by index\n *\n * @example\n * ```ts\n * // Given content keys:\n * // positions.1.title = \"Software Engineer\"\n * // positions.1.description = \"Build cool stuff\"\n * // positions.2.title = \"Designer\"\n * // positions.2.description = \"Design cool stuff\"\n *\n * const positions = getArray('positions')\n * // Returns:\n * // [\n * // { _index: 1, title: \"Software Engineer\", description: \"Build cool stuff\" },\n * // { _index: 2, title: \"Designer\", description: \"Design cool stuff\" }\n * // ]\n * ```\n */\n function getArray(arrayKey: string): Array<Record<string, unknown> & { _index: number }> {\n const items: Record<number, Record<string, unknown> & { _index: number }> = {}\n const source = { ...defaults, ...overrides.value }\n\n Object.keys(source).forEach((key) => {\n if (key.startsWith(`${arrayKey}.`)) {\n const parts = key.split('.')\n // Format: arrayKey.index.property (or arrayKey.index.nested.property)\n // Example: features.1.title -> index=1, prop=title\n if (parts.length >= 3) {\n const index = Number.parseInt(parts[1], 10)\n const prop = parts.slice(2).join('.')\n\n if (!Number.isNaN(index)) {\n if (!items[index]) items[index] = { _index: index }\n items[index][prop] = source[key]\n }\n }\n }\n })\n\n return Object.values(items).sort((a, b) => a._index - b._index)\n }\n\n /**\n * Fetch runtime overrides from the API.\n * Only runs if mode is 'runtime' and API is configured.\n */\n async function fetchOverrides(): Promise<void> {\n // Skip API fetch in commit mode - use build-time content only\n if (mode !== 'runtime') {\n return\n }\n\n // Skip if API not configured. The site is resolved server-side (Host /\n // DCS_SITE_SLUG), so VITE_SITE_SLUG is no longer required for routing —\n // only the API base URL is needed.\n if (!apiBaseUrl) {\n console.warn(\n '[@duffcloudservices/cms] Runtime mode enabled but VITE_API_BASE_URL not set'\n )\n return\n }\n\n // Check cache first\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\n const cached = fetchCache.get(effectiveCacheKey)\n if (cached && cached.expiresAt > Date.now()) {\n overrides.value = { ...buildTimeContent, ...cached.data }\n return\n }\n\n isLoading.value = true\n error.value = null\n\n try {\n // Site is resolved server-side from the request Host or the dedicated\n // Container App's DCS_SITE_SLUG; the slug is no longer encoded in the path.\n const url = `${apiBaseUrl}/api/v1/pages/${pageSlug}/text`\n // C-298 layer 2. `VITE_API_BASE_URL` defaults to '' here, so an unset base in\n // `runtime` mode produces a RELATIVE call — the exact shape of C-261. The sweep also\n // proved the configured base (api.duffcloudservices.com) is tenant-unresolvable for\n // this host-resolved route (400 \"Unable to resolve site from request\"), which arrives\n // as JSON and is therefore left to the `!response.ok` branch below, unchanged. What\n // platformFetch adds is that an HTML shell can no longer be mistaken for \"no\n // overrides for this page\".\n const response = await platformFetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n // No overrides for this page - that's fine\n return\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const data = await response.json()\n const apiOverrides = data.overrides ?? data.texts ?? {}\n\n // Cache the result\n fetchCache.set(effectiveCacheKey, {\n data: apiOverrides,\n expiresAt: Date.now() + cacheTtl,\n })\n\n // Merge: build-time content is baseline, API overrides on top\n overrides.value = { ...buildTimeContent, ...apiOverrides }\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to load text overrides'\n console.error('[@duffcloudservices/cms] Failed to fetch text overrides:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n /**\n * Manually refresh overrides.\n * In commit mode, resets to build-time content.\n * In runtime mode, fetches fresh data from API.\n */\n async function refresh(): Promise<void> {\n if (mode !== 'runtime') {\n // In commit mode, just reset to build-time content\n overrides.value = { ...buildTimeContent }\n return\n }\n\n // Clear cache for this key\n const effectiveCacheKey = cacheKey ?? `${siteSlug}:${pageSlug}`\n fetchCache.delete(effectiveCacheKey)\n\n await fetchOverrides()\n }\n\n // Fetch on mount if enabled and in runtime mode (browser only)\n if (fetchOnMount && mode === 'runtime' && globalThis.window !== undefined) {\n onMounted(() => {\n fetchOverrides()\n })\n }\n\n return {\n t,\n getArray,\n texts,\n overrides,\n isLoading: readonly(isLoading) as Ref<boolean>,\n error: readonly(error) as Ref<string | null>,\n refresh,\n hasOverride,\n hasBuildTimeContent,\n mode,\n }\n}\n","/**\n * useSEO Composable\n *\n * Provides SEO configuration with build-time injection support from .dcs/seo.yaml.\n * Generates meta tags, Open Graph, Twitter Cards, and JSON-LD structured data.\n *\n * The actual tag resolution lives in the framework-agnostic `../seo/headTags`\n * module so that the build-time static-HTML emitter (`dcsSeoPlugin`) produces\n * byte-identical output. This composable is a thin Vue/unhead wrapper over it.\n *\n * THE HEAD-AUTHORITY CONTRACT (C-356). `.dcs/seo.yaml` is the ONLY writer of\n * the managed head fields. `applyHead()` RE-ASSERTS the baked head at runtime;\n * it never AUTHORS one, so it takes no arguments. Full text + reasoning:\n * `.docs/plans/dynamic-site-resolution/README.md` § \"The head-authority\n * contract (C-356)\".\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useSEO } from '@duffcloudservices/cms'\n *\n * const { applyHead, getSchema, config } = useSEO('home')\n *\n * // Re-assert the baked head for this route. No arguments — ever.\n * applyHead()\n * </script>\n * ```\n *\n * Need a different title? Change it in `.dcs/seo.yaml` (or in the portal SEO\n * editor, which writes it). A value hardcoded here is a SECOND writer, and a\n * second writer is a divergence by construction — whether or not today's two\n * values happen to agree.\n */\n\nimport { computed, type ComputedRef } from 'vue'\nimport { useHead } from '@unhead/vue'\nimport type {\n SeoConfiguration,\n GlobalSeoConfig,\n ResolvedPageSeo,\n UseSeoReturn,\n} from '../types/seo'\nimport { buildHeadTags, resolvePageSeo, generateJsonLd } from '../seo/headTags'\n\n// Declare the global injected by dcsSeoPlugin\ndeclare const __DCS_SEO__: SeoConfiguration | undefined\n\n/**\n * Safely get build-time SEO configuration.\n * Returns undefined if not available (no seo.yaml or plugin not configured).\n */\nfunction getBuildTimeSeo(): SeoConfiguration | undefined {\n try {\n if (typeof __DCS_SEO__ !== 'undefined' && __DCS_SEO__ !== null) {\n return __DCS_SEO__\n }\n } catch {\n // __DCS_SEO__ not defined - that's fine, use defaults\n }\n return undefined\n}\n\n/**\n * The C-356 refusal. Exported so tests can assert the exact message and so a\n * host app can spot it in a log.\n *\n * IT DOES NOT THROW, ANYWHERE. The build-time gates are the ones that must be\n * unmissable — the `() => void` type (caught by every site's `type-check`, made\n * pre-deploy-mandatory by C-303/C-316) and `assertHeadContract` in the source\n * audit, which fails the build outright. By the time control reaches this\n * function the page is already rendering for a real visitor, and throwing there\n * would take a paying customer's page down over a metadata mistake. The\n * asymmetry is the point: the worst outcome of a refused override is that the\n * page serves the OWNER-APPROVED value with a console error next to it.\n */\nexport const HEAD_OVERRIDE_REFUSED_MESSAGE =\n '[dcs-seo] applyHead() was called WITH AN ARGUMENT and the argument was IGNORED. ' +\n '.dcs/seo.yaml is the only writer of title/description/keywords/canonical/OG/Twitter/' +\n 'JSON-LD (head-authority contract, C-356). A hardcoded value here overwrites the ' +\n 'owner-approved baked head for Google and every human while non-JS crawlers keep ' +\n 'receiving the approved one. Move the value into .dcs/seo.yaml and call applyHead() ' +\n 'with no arguments.'\n\nfunction reportHeadOverrideRefused(pageSlug: string, refused: unknown): void {\n const keys =\n refused && typeof refused === 'object' ? Object.keys(refused as object).join(', ') : String(refused)\n console.error(\n `${HEAD_OVERRIDE_REFUSED_MESSAGE}\\n page slug: ${pageSlug}\\n ignored keys: ${keys}`\n )\n}\n\n/**\n * useSEO composable for DCS-managed SEO configuration.\n *\n * @param pageSlug - Page slug matching entry in seo.yaml\n * @param pagePath - Optional page path for canonical URL generation\n * @returns SEO helpers and state\n */\nexport function useSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\n const seoConfig = getBuildTimeSeo()\n const hasBuildTimeSeo = seoConfig !== undefined\n\n // Computed resolved config\n const config: ComputedRef<ResolvedPageSeo> = computed(() =>\n resolvePageSeo(pageSlug, pagePath, seoConfig)\n )\n\n /**\n * Get JSON-LD schema objects for the page\n */\n function getSchema(): object[] {\n return generateJsonLd(config.value.schemas, seoConfig?.global ?? {})\n }\n\n /**\n * Get canonical URL for the page\n */\n function getCanonical(): string {\n return config.value.canonical\n }\n\n /**\n * RE-ASSERT the baked head for this route. Takes no arguments.\n *\n * Delegates to the shared `buildHeadTags` resolver so the emitted tags match\n * the build-time static-HTML emitter exactly. Keywords are intentionally not\n * emitted at runtime (historical behaviour), so `includeKeywords` is omitted.\n *\n * ENFORCEMENT (C-356). The parameter is gone from the signature, so TypeScript\n * consumers fail `type-check`. But a `.vue` file compiled without type\n * checking, an `as any`, or a JS site can still reach this function with an\n * argument, and THAT is the shape that shipped 93 divergences on KEPT. So the\n * override is also refused at runtime: it is dropped, `.dcs/seo.yaml` still\n * wins, and the violation is reported. A violating site therefore degrades to\n * CORRECT SEO plus a loud message — never to a silently destroyed head.\n */\n // Declared with a rest parameter so a runtime argument is CAPTURED, then\n // exposed through `UseSeoReturn` as `() => void` so a compile-time argument is\n // REJECTED. Both halves are needed: the type stops the honest caller, the\n // runtime stops the one who is not type-checked.\n function applyHeadImpl(...refused: unknown[]): void {\n if (refused.length > 0 && refused[0] !== undefined && refused[0] !== null) {\n reportHeadOverrideRefused(pageSlug, refused[0])\n }\n\n const { title, meta, link, script } = buildHeadTags(pageSlug, pagePath, seoConfig)\n\n // Apply via useHead. The shared resolver returns framework-agnostic tag\n // shapes (HeadMetaTag/HeadLinkTag/HeadScriptTag); unhead's input types are\n // structurally compatible but add an open-ended `data-*` index signature,\n // so we hand them over via useHead's Head input type. Runtime is identical.\n useHead({\n title,\n meta,\n link,\n script,\n } as unknown as Parameters<typeof useHead>[0])\n }\n\n // The narrowing that makes the contract compile-checkable for consumers.\n const applyHead: () => void = applyHeadImpl\n\n return {\n config,\n applyHead,\n getSchema,\n getCanonical,\n hasBuildTimeSeo,\n }\n}\n\n/**\n * Create a typed useSEO function with site-specific defaults.\n * Useful for creating a site-wide wrapper.\n *\n * @example\n * ```ts\n * // composables/useSiteSeo.ts\n * import { createSiteSEO } from '@duffcloudservices/cms'\n *\n * export const useSiteSeo = createSiteSEO({\n * siteName: 'My Site',\n * siteUrl: 'https://example.com'\n * })\n * ```\n */\nexport function createSiteSEO(\n _siteDefaults: Partial<GlobalSeoConfig>\n): (pageSlug: string, pagePath?: string) => UseSeoReturn {\n return function siteUseSEO(pageSlug: string, pagePath?: string): UseSeoReturn {\n // Note: siteDefaults would be used if we needed to override at runtime\n // but build-time injection handles this via dcsSeoPlugin\n return useSEO(pageSlug, pagePath)\n }\n}\n","/**\n * useReleaseNotes Composable\n *\n * Fetches and displays versioned release notes from the DCS Portal API.\n * Supports fetching specific versions or the latest release.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useReleaseNotes } from '@duffcloudservices/cms'\n * import { useRoute } from 'vue-router'\n *\n * const route = useRoute()\n * const version = route.params.version as string || 'latest'\n *\n * const { releaseNote, isLoading, error } = useReleaseNotes(version)\n * </script>\n *\n * <template>\n * <div v-if=\"isLoading\">Loading...</div>\n * <div v-else-if=\"error\">{{ error }}</div>\n * <article v-else-if=\"releaseNote\">\n * <h1>{{ releaseNote.title }}</h1>\n * <p>{{ releaseNote.summary }}</p>\n * <div v-html=\"renderedMarkdown\" />\n * </article>\n * </template>\n * ```\n */\n\nimport { platformFetch } from '@duffcloudservices/cms-core'\nimport { ref, onMounted } from 'vue'\nimport type { ReleaseNote, ReleaseNotesReturn } from '../types/release-notes'\n\n/**\n * Get environment variable value.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n return defaultValue\n}\n\n// Simple cache for release notes\nconst releaseNotesCache = new Map<string, { data: ReleaseNote; expiresAt: number }>()\nconst CACHE_TTL = 5 * 60 * 1000 // 5 minutes\n\n/**\n * useReleaseNotes composable for fetching release notes from the DCS API.\n *\n * @param version - Semantic version (e.g., \"1.2.0\") or \"latest\"\n * @param options - Optional configuration\n * @returns Release notes data and state\n */\nexport function useReleaseNotes(\n version: string,\n options: { fetchOnMount?: boolean } = {}\n): ReleaseNotesReturn {\n const { fetchOnMount = true } = options\n\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\n // @deprecated VITE_SITE_SLUG: the site is now resolved server-side from the\n // request Host or the dedicated Container App's DCS_SITE_SLUG. It is retained\n // only for cache-key continuity and source compatibility, not for routing.\n const siteSlug = getEnvVar('VITE_SITE_SLUG', '')\n\n const releaseNote = ref<ReleaseNote | null>(null)\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n\n async function fetchReleaseNotes(): Promise<void> {\n // Check cache. The slug is no longer required for routing (server resolves\n // the site from Host / DCS_SITE_SLUG); it is only part of the cache key.\n const cacheKey = `${siteSlug}:${version}`\n const cached = releaseNotesCache.get(cacheKey)\n if (cached && cached.expiresAt > Date.now()) {\n releaseNote.value = cached.data\n return\n }\n\n isLoading.value = true\n error.value = null\n\n try {\n const url = `${apiBaseUrl}/api/v1/release-notes/${version}`\n // C-298 layer 2: an HTML body here means the call never reached the API (a host\n // whose Front Door config has no /api/v1/* route answers with the SPA shell at\n // HTTP 200). platformFetch names the URL + content-type instead of letting\n // `response.json()` throw an opaque \"Unexpected token '<'\" into the catch below.\n // A real 404 still lands in the `!response.ok` branch untouched.\n const response = await platformFetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n error.value = `Release notes for version ${version} not found`\n return\n }\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const data = await response.json()\n\n // Normalize the response\n const note: ReleaseNote = {\n version: data.version,\n title: data.title,\n summary: data.summary || '',\n notesMarkdown: data.notesMarkdown || data.notes || '',\n changeCount: data.changeCount || 0,\n releaseDate: data.releaseDate || data.releasedAt || '',\n }\n\n // Cache the result\n releaseNotesCache.set(cacheKey, {\n data: note,\n expiresAt: Date.now() + CACHE_TTL,\n })\n\n releaseNote.value = note\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to load release notes'\n console.error('[@duffcloudservices/cms] Failed to fetch release notes:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n async function refresh(): Promise<void> {\n // Clear cache\n const cacheKey = `${siteSlug}:${version}`\n releaseNotesCache.delete(cacheKey)\n await fetchReleaseNotes()\n }\n\n // Fetch on mount if enabled\n if (fetchOnMount && typeof window !== 'undefined') {\n onMounted(() => {\n fetchReleaseNotes()\n })\n }\n\n return {\n releaseNote,\n isLoading,\n error,\n refresh,\n }\n}\n","/**\n * useSiteVersion Composable\n *\n * Gets the current site version for footer badges and version displays.\n * Fetches the latest release version from the DCS Portal API.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useSiteVersion } from '@duffcloudservices/cms'\n *\n * const { version, releaseNotesUrl } = useSiteVersion()\n * </script>\n *\n * <template>\n * <footer>\n * <a v-if=\"version\" :href=\"releaseNotesUrl\" class=\"version-badge\">\n * v{{ version }}\n * </a>\n * </footer>\n * </template>\n * ```\n */\n\nimport { platformFetch } from '@duffcloudservices/cms-core'\nimport { ref, computed, onMounted } from 'vue'\nimport type { SiteVersionReturn } from '../types/release-notes'\n\n/**\n * Get environment variable value.\n */\nfunction getEnvVar(key: string, defaultValue = ''): string {\n try {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const value = (import.meta.env as Record<string, string | undefined>)[key]\n if (value !== undefined) return value\n }\n } catch {\n // import.meta not available\n }\n return defaultValue\n}\n\n// Cache for site version\nlet versionCache: { version: string; expiresAt: number } | null = null\nconst CACHE_TTL = 10 * 60 * 1000 // 10 minutes\n\n/**\n * useSiteVersion composable for displaying the current site version.\n *\n * @param options - Optional configuration\n * @returns Site version data and computed URL\n */\nexport function useSiteVersion(options: { fetchOnMount?: boolean } = {}): SiteVersionReturn {\n const { fetchOnMount = true } = options\n\n const apiBaseUrl = getEnvVar('VITE_API_BASE_URL', 'https://portal.duffcloudservices.com')\n // @deprecated VITE_SITE_SLUG: the site is now resolved server-side from the\n // request Host or the dedicated Container App's DCS_SITE_SLUG. It is no longer\n // used for routing; retained only for source compatibility.\n void getEnvVar('VITE_SITE_SLUG', '')\n\n const version = ref<string | null>(null)\n const isLoading = ref(false)\n\n const releaseNotesUrl = computed(() => {\n if (!version.value) return '/releaseNotes/latest'\n return `/releaseNotes/${version.value}`\n })\n\n async function fetchVersion(): Promise<void> {\n // Check cache\n if (versionCache && versionCache.expiresAt > Date.now()) {\n version.value = versionCache.version\n return\n }\n\n isLoading.value = true\n\n try {\n // Fetch the latest release notes to get the version\n const url = `${apiBaseUrl}/api/v1/release-notes/latest`\n // C-298 layer 2 — see useReleaseNotes: an HTML body is a misroute, not \"no release\n // notes yet\", and this composable's catch would otherwise swallow it silently.\n const response = await platformFetch(url, {\n headers: {\n Accept: 'application/json',\n },\n })\n\n if (!response.ok) {\n // No release notes yet - that's fine\n return\n }\n\n const data = await response.json()\n const latestVersion = data.version\n\n // Cache the result\n versionCache = {\n version: latestVersion,\n expiresAt: Date.now() + CACHE_TTL,\n }\n\n version.value = latestVersion\n } catch (e) {\n console.error('[@duffcloudservices/cms] Failed to fetch site version:', e)\n } finally {\n isLoading.value = false\n }\n }\n\n // Fetch on mount if enabled\n if (fetchOnMount && typeof window !== 'undefined') {\n onMounted(() => {\n fetchVersion()\n })\n }\n\n return {\n version,\n isLoading,\n releaseNotesUrl,\n }\n}\n","/**\n * useMediaCarousel Composable\n *\n * Extracts media carousel items from text content keys following the pattern:\n * `{prefix}.{N}.url`, `{prefix}.{N}.type`, `{prefix}.{N}.alt`\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useTextContent, useMediaCarousel } from '@duffcloudservices/cms'\n *\n * const { t } = useTextContent({\n * pageSlug: 'bio-mackenzie',\n * defaults: {\n * 'hero.media-carousel.0.url': '/images/staff/mackenzie.webp',\n * 'hero.media-carousel.0.type': 'image',\n * 'hero.media-carousel.0.alt': 'Mackenzie Kowalick',\n * 'hero.media-carousel.1.url': '/videos/intro.mp4',\n * 'hero.media-carousel.1.type': 'video',\n * 'hero.media-carousel.1.alt': 'Introduction video',\n * }\n * })\n *\n * const { items } = useMediaCarousel({\n * prefix: 'hero.media-carousel',\n * t,\n * defaults: [\n * { url: '/images/staff/mackenzie.webp', type: 'image', alt: 'Mackenzie Kowalick' }\n * ]\n * })\n * </script>\n *\n * <template>\n * <MediaCarousel :items=\"items\" />\n * </template>\n * ```\n */\n\nimport { computed, type ComputedRef } from 'vue'\nimport { isCdnAssetUrl } from '@duffcloudservices/cms-core'\n\n/**\n * Media carousel item representing an image, video, or embed\n */\nexport interface MediaCarouselItem {\n /** URL to the image, video file, or embed URL */\n url: string\n /**\n * Type of media: 'image', 'video' (direct file), 'youtube', or 'instagram'.\n * Render `youtube`/`instagram` items through the click-to-load facade\n * `LiteMediaEmbed` (`@duffcloudservices/cms/lite-media-embed`) so the heavy\n * player iframe loads only on user interaction — never eagerly.\n */\n type: 'image' | 'video' | 'youtube' | 'instagram'\n /** Accessibility alt text */\n alt?: string\n /**\n * Whether this image has responsive CDN variants available.\n * Automatically set to `true` when the URL matches the DCS CDN asset pattern.\n * Components rendering the carousel should use `<ResponsiveImage>` when this is `true`.\n */\n responsive?: boolean\n}\n\n/**\n * Configuration for useMediaCarousel composable\n */\nexport interface UseMediaCarouselConfig {\n /** Key prefix for carousel items (e.g., 'hero.media-carousel') */\n prefix: string\n /** The t() function from useTextContent */\n t: (key: string, fallback?: string) => string\n /** Default items to use if no content keys are found */\n defaults?: MediaCarouselItem[]\n /** Maximum number of items to look for (default: 10) */\n maxItems?: number\n}\n\n/**\n * Return type for useMediaCarousel composable\n */\nexport interface UseMediaCarouselReturn {\n /** Computed array of media carousel items */\n items: ComputedRef<MediaCarouselItem[]>\n /** Whether any items were found from content keys */\n hasItems: ComputedRef<boolean>\n /** Number of items in the carousel */\n count: ComputedRef<number>\n}\n\n/**\n * Extract media carousel items from text content keys.\n *\n * Looks for keys in the format:\n * - `{prefix}.{N}.url` - Required URL for the media\n * - `{prefix}.{N}.type` - Type: 'image' or 'video' (defaults to 'image')\n * - `{prefix}.{N}.alt` - Alt text for accessibility\n *\n * Items are sorted by index (0, 1, etc.) and only included if they have a valid URL.\n *\n * @param config - Configuration object\n * @returns Media carousel helpers and state\n */\nexport function useMediaCarousel(config: UseMediaCarouselConfig): UseMediaCarouselReturn {\n const {\n prefix,\n t,\n defaults = [],\n maxItems = 10,\n } = config\n\n const items = computed<MediaCarouselItem[]>(() => {\n const result: MediaCarouselItem[] = []\n\n // Look for items from 0 to maxItems\n for (let i = 0; i < maxItems; i++) {\n const urlKey = `${prefix}.${i}.url`\n const typeKey = `${prefix}.${i}.type`\n const altKey = `${prefix}.${i}.alt`\n\n // Use a sentinel value to detect if the key exists\n const url = t(urlKey, '')\n \n // Skip if no URL (key doesn't exist or is empty)\n if (!url || url === urlKey) {\n continue\n }\n\n const typeValue = t(typeKey, 'image')\n // Parse type value - support image, video, youtube, instagram\n let type: 'image' | 'video' | 'youtube' | 'instagram' = 'image'\n if (typeValue === 'video') type = 'video'\n else if (typeValue === 'youtube') type = 'youtube'\n else if (typeValue === 'instagram') type = 'instagram'\n const alt = t(altKey, '')\n\n result.push({\n url,\n type,\n alt: alt && alt !== altKey ? alt : undefined,\n // Flag CDN-hosted images as responsive so carousel components\n // can render them with <ResponsiveImage> automatically\n responsive: type === 'image' && isCdnAssetUrl(url),\n })\n }\n\n // If no items found from content keys, use defaults\n if (result.length === 0) {\n return defaults\n }\n\n return result\n })\n\n const hasItems = computed(() => items.value.length > 0)\n const count = computed(() => items.value.length)\n\n return {\n items,\n hasItems,\n count,\n }\n}\n","/**\n * Vue 3 composable that resolves responsive image variants for DCS CDN-hosted assets.\n *\n * Wraps the framework-agnostic `resolveResponsiveImage` from `@duffcloudservices/cms-core`\n * with reactive Vue refs so it can be used directly in `<script setup>` blocks.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useResponsiveImage } from '@duffcloudservices/cms'\n *\n * const hero = useResponsiveImage({\n * src: 'https://files.duffcloudservices.com/kept/assets/hero/abc-123.jpg',\n * alt: 'Hero image',\n * context: 'hero',\n * })\n * </script>\n *\n * <template>\n * <picture v-if=\"hero.hasVariants\">\n * <source\n * v-for=\"source in hero.sources\"\n * :key=\"source.type\"\n * :srcset=\"source.srcset\"\n * :type=\"source.type\"\n * :sizes=\"source.sizes\"\n * />\n * <img v-bind=\"hero.imgProps\" class=\"w-full h-full object-cover\" />\n * </picture>\n * <img v-else v-bind=\"hero.imgProps\" class=\"w-full h-full object-cover\" />\n * </template>\n * ```\n */\n\nimport { computed, type MaybeRefOrGetter, toValue } from 'vue'\nimport {\n resolveResponsiveImage,\n type ImageContext,\n type ResponsiveImageResult,\n} from '@duffcloudservices/cms-core'\n\nexport interface UseResponsiveImageOptions {\n /** Source URL — can be a reactive ref, getter, or plain string. */\n src: MaybeRefOrGetter<string>\n /** Alt text — can be a reactive ref, getter, or plain string. */\n alt: MaybeRefOrGetter<string>\n /** Sizing context — determines which variants to include. */\n context?: MaybeRefOrGetter<ImageContext | undefined>\n /** Optional `sizes` attribute override. */\n sizes?: MaybeRefOrGetter<string | undefined>\n /** Skip variant resolution and use the original URL only. */\n original?: MaybeRefOrGetter<boolean | undefined>\n /** Intrinsic width — emitted as a layout-shift hint when paired with `height`. */\n width?: MaybeRefOrGetter<number | undefined>\n /** Intrinsic height — emitted as a layout-shift hint when paired with `width`. */\n height?: MaybeRefOrGetter<number | undefined>\n}\n\n/**\n * Reactively resolves responsive image metadata for a DCS CDN URL.\n *\n * The returned object is a computed ref that recomputes whenever any\n * of the input refs change. Spread `imgProps` onto an `<img>` or\n * combine with `sources` inside a `<picture>` element.\n */\nexport function useResponsiveImage(options: UseResponsiveImageOptions): ResponsiveImageResult {\n const result = computed(() =>\n resolveResponsiveImage({\n src: toValue(options.src),\n alt: toValue(options.alt),\n context: toValue(options.context),\n sizes: toValue(options.sizes),\n original: toValue(options.original) ?? undefined,\n width: toValue(options.width) ?? undefined,\n height: toValue(options.height) ?? undefined,\n }),\n )\n\n // Return a reactive proxy that delegates to the computed\n return {\n get imgProps() {\n return result.value.imgProps\n },\n get sources() {\n return result.value.sources\n },\n get hasVariants() {\n return result.value.hasVariants\n },\n }\n}\n","/**\n * Composable for reading curated review selections from DCS content.\n * Reviews are stored in content.yaml by the visual editor's ReviewPickerSheet.\n */\nimport { computed, onMounted, onUnmounted, shallowRef, type ComputedRef } from 'vue'\n\nexport interface ReviewItem {\n id: string\n platform: 'google' | 'meta' | string\n rating: number\n authorName: string\n authorPhotoUrl?: string\n text?: string\n date?: string\n replyText?: string\n locationName?: string\n sourceLocationName?: string\n sourceUrl?: string\n}\n\nfunction withoutAuthorPhotos(items: ReviewItem[]): ReviewItem[] {\n return items.map(item => ({\n ...item,\n authorPhotoUrl: undefined,\n }))\n}\n\n/**\n * True only in a Vite dev build. Vite statically replaces `import.meta.env.DEV`\n * with a boolean literal (`true` in dev, `false` in production). The\n * `typeof import.meta` guard + try/catch keep this safe if the package is ever\n * SSR-externalized (where `import.meta.env` is undefined at the Node runtime and\n * accessing it would throw) — in that case we deliberately return `false` so the\n * production-safe branch is taken.\n */\nfunction isDevBuild(): boolean {\n try {\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.env.DEV === true\n }\n } catch {\n // import.meta.env unavailable (externalized SSR / non-Vite host) → prod-safe.\n }\n return false\n}\n\n/**\n * Honesty guard: consumer-supplied `defaults` are a DEV-ONLY affordance so a\n * freshly-scaffolded site shows sample reviews while it is being wired up. In a\n * production build they must NEVER render — an SSR production pass emitting\n * placeholder reviews is exactly the fabricated-social-proof / hydration bug\n * this guards against (the content define can be missing server-side if the cms\n * package is SSR-externalized). Production therefore falls back to an honest\n * empty state; real reviews always come from `.dcs/content.yaml`.\n */\nfunction fallbackReviews(defaults: ReviewItem[]): ReviewItem[] {\n return isDevBuild() ? withoutAuthorPhotos(defaults) : []\n}\n\nexport interface UseReviewContentConfig {\n /** The section key matching the data-dcs-reviews attribute value */\n sectionKey: string\n /** Page slug for page-specific content lookup (defaults to current page) */\n pageSlug?: string\n /** Fallback reviews when no content is available */\n defaults?: ReviewItem[]\n}\n\nexport interface UseReviewContentReturn {\n /** The curated review items from content */\n reviews: ComputedRef<ReviewItem[]>\n /** Whether any reviews are available */\n hasReviews: ComputedRef<boolean>\n /** Number of reviews */\n count: ComputedRef<number>\n}\n\nconst previewReviewOverrides = shallowRef<Record<string, ReviewItem[]>>({})\nlet activePreviewReviewConsumers = 0\n\n// Declare the global content variable injected by dcsContentPlugin\ndeclare const __DCS_CONTENT__: {\n global?: Record<string, unknown>\n pages?: Record<string, Record<string, unknown>>\n} | undefined\n\nfunction normalizeReviewItem(item: Record<string, unknown>): ReviewItem | null {\n const id = String(item.id ?? '').trim()\n if (!id) {\n return null\n }\n\n const rawRating = Number(item.rating ?? 5)\n const rating = Number.isFinite(rawRating)\n ? Math.min(5, Math.max(1, Math.round(rawRating)))\n : 5\n\n return {\n id,\n platform: String(item.platform ?? 'google'),\n rating,\n authorName: String(item.authorName ?? 'Anonymous'),\n authorPhotoUrl: undefined,\n text: item.text ? String(item.text) : undefined,\n date: item.date ? String(item.date) : undefined,\n replyText: item.replyText ? String(item.replyText) : undefined,\n locationName: item.locationName ? String(item.locationName) : undefined,\n sourceLocationName: item.sourceLocationName ? String(item.sourceLocationName) : undefined,\n sourceUrl: item.sourceUrl ? String(item.sourceUrl) : undefined,\n }\n}\n\nfunction normalizeReviewList(value: unknown): ReviewItem[] {\n if (!Array.isArray(value)) {\n return []\n }\n\n return value\n .filter((item): item is Record<string, unknown> => item != null && typeof item === 'object')\n .map(normalizeReviewItem)\n .filter((item): item is ReviewItem => item != null)\n}\n\nfunction handlePreviewReviewUpdate(event: Event) {\n const detail = event instanceof CustomEvent && event.detail != null && typeof event.detail === 'object'\n ? event.detail as { key?: unknown; reviews?: unknown }\n : null\n const key = typeof detail?.key === 'string' ? detail.key.trim() : ''\n if (!key) {\n return\n }\n\n const next = { ...previewReviewOverrides.value }\n if (Array.isArray(detail?.reviews)) {\n next[key] = normalizeReviewList(detail.reviews)\n } else {\n delete next[key]\n }\n previewReviewOverrides.value = next\n}\n\nexport function useReviewContent(config: UseReviewContentConfig): UseReviewContentReturn {\n const { sectionKey, pageSlug, defaults = [] } = config\n\n onMounted(() => {\n activePreviewReviewConsumers += 1\n if (activePreviewReviewConsumers === 1) {\n window.addEventListener('dcs:reviews-updated', handlePreviewReviewUpdate)\n }\n })\n\n onUnmounted(() => {\n activePreviewReviewConsumers = Math.max(0, activePreviewReviewConsumers - 1)\n if (activePreviewReviewConsumers === 0) {\n window.removeEventListener('dcs:reviews-updated', handlePreviewReviewUpdate)\n }\n })\n\n const reviews = computed<ReviewItem[]>(() => {\n if (Object.prototype.hasOwnProperty.call(previewReviewOverrides.value, sectionKey)) {\n return previewReviewOverrides.value[sectionKey] ?? []\n }\n\n if (typeof __DCS_CONTENT__ === 'undefined' || __DCS_CONTENT__ == null) {\n return fallbackReviews(defaults)\n }\n\n let reviewData: unknown = null\n\n if (pageSlug && __DCS_CONTENT__.pages?.[pageSlug]) {\n reviewData = __DCS_CONTENT__.pages[pageSlug][`reviews.${sectionKey}.items`]\n ?? __DCS_CONTENT__.pages[pageSlug][`reviews.${sectionKey}`]\n }\n\n if (!reviewData && __DCS_CONTENT__.global) {\n reviewData = __DCS_CONTENT__.global[`reviews.${sectionKey}.items`]\n ?? __DCS_CONTENT__.global[`reviews.${sectionKey}`]\n }\n\n if (!reviewData || !Array.isArray(reviewData)) {\n return fallbackReviews(defaults)\n }\n\n return normalizeReviewList(reviewData)\n })\n\n const hasReviews = computed(() => reviews.value.length > 0)\n const count = computed(() => reviews.value.length)\n\n return { reviews, hasReviews, count }\n}\n","/**\n * Site-visitor session probe client.\n *\n * Customer sites poll \"who is signed in?\" on load to render the right auth UI.\n * Signed-out is the EXPECTED state for the vast majority of visits, so this\n * client treats it as normal and never logs:\n *\n * - New servers answer an anonymous probe with `200 { \"visitor\": null }`.\n * - Old servers (pre-fix) answer an anonymous probe with `401`.\n *\n * Sites bump `@duffcloudservices/cms` independently of server deploys, so this\n * client must accept BOTH shapes and stay silent on the anonymous path — a\n * `console.error`/`console.warn` on every signed-out page load is exactly the\n * console-noise this fixes. A genuine signed-in visitor is returned as a\n * normalized `SiteVisitor`; anything else resolves to `null`.\n *\n * ONE EXCEPTION, added by C-298 layer 2. \"Signed out\" and \"this host does not route\n * `/api/v1/*` to the platform API at all\" used to be the SAME observation here: the\n * default base is the RELATIVE `/api/v1`, so on a host whose Front Door config lacks the\n * route, Front Door's catch-all answers with the SPA shell at HTTP 200, `response.json()`\n * throws, and this client silently resolves to signed-out. That is precisely how KEPT — a\n * paying customer — ran with all nine login routes dead for ~2 weeks with nothing\n * detecting it (C-261), and it is still live today on `www.nateduff.com`.\n *\n * A non-JSON body is therefore NOT treated as an anonymous visit: it is reported loudly\n * (once per probe, naming the URL and the received content-type) and the UI still degrades\n * to signed-out, because a login button that cannot work is better than a broken page.\n * The anonymous path — `200 {\"visitor\":null}`, a legacy `401`, a network error — stays\n * exactly as silent as before.\n */\nimport { platformFetch, isPlatformFetchError } from '@duffcloudservices/cms-core'\nimport { ref, computed, onMounted, type Ref, type ComputedRef } from 'vue'\n\n/** A signed-in site visitor. */\nexport interface SiteVisitor {\n id?: string\n email: string\n name: string\n picture?: string\n createdAt?: string\n}\n\nexport interface SiteVisitorSessionResult {\n /** The signed-in visitor, or `null` when signed out. */\n visitor: SiteVisitor | null\n /** Convenience flag — `true` iff a visitor is present. */\n authenticated: boolean\n /**\n * `true` when the probe did not reach the platform API at all — the response carried a\n * non-JSON body (C-298 layer 2), i.e. this host does not route `/api/v1/*` to the API.\n * Distinct from an ordinary signed-out visit: a site can render \"sign-in temporarily\n * unavailable\" instead of a login button that cannot possibly work. Absent/`false` on\n * every normal path, so existing consumers are unaffected.\n */\n apiUnreachable?: boolean\n}\n\nexport interface FetchSiteVisitorSessionOptions {\n /** API base (default `/api/v1`, same-origin via Front Door). */\n apiBaseUrl?: string\n /** Optional AbortSignal to cancel an in-flight probe. */\n signal?: AbortSignal\n}\n\nconst SIGNED_OUT: SiteVisitorSessionResult = { visitor: null, authenticated: false }\n\nconst apiBase = (value?: string): string => (value ?? '/api/v1').replace(/\\/$/u, '')\n\n/**\n * Normalize the various session-response shapes into a `SiteVisitor | null`.\n * Accepts the primary/legacy `{ visitor: {...} | null }` envelope and the\n * contracts-spec `{ authenticated, user: {...} }` shape defensively.\n */\nfunction extractVisitor(data: unknown): SiteVisitor | null {\n if (!data || typeof data !== 'object') {\n return null\n }\n const obj = data as Record<string, unknown>\n const raw = obj.visitor ?? obj.user\n if (!raw || typeof raw !== 'object') {\n return null\n }\n const v = raw as Record<string, unknown>\n const email = typeof v.email === 'string' ? v.email : ''\n if (!email) {\n return null\n }\n return {\n id: typeof v.id === 'string' ? v.id : undefined,\n email,\n name: typeof v.name === 'string' ? v.name : email,\n picture: typeof v.picture === 'string' ? v.picture : undefined,\n createdAt: typeof v.createdAt === 'string' ? v.createdAt : undefined,\n }\n}\n\n/**\n * Fetch the current site-visitor session. Never throws and never logs for the\n * anonymous case: any network error, non-OK status (incl. a legacy `401`), or\n * `{ visitor: null }` body resolves to a signed-out result.\n */\nexport async function fetchSiteVisitorSession(\n options: FetchSiteVisitorSessionOptions = {},\n): Promise<SiteVisitorSessionResult> {\n const base = apiBase(options.apiBaseUrl)\n\n let response: Response\n try {\n response = await platformFetch(`${base}/site-auth/session`, {\n method: 'GET',\n credentials: 'include',\n headers: { Accept: 'application/json' },\n signal: options.signal,\n })\n } catch (e) {\n // A NON-JSON body is a misroute, not an anonymous visit: the request never reached\n // the API. platformFetch has already written the diagnosable message to the console;\n // flag it so the UI can say so, and still degrade to signed-out so the page renders.\n if (isPlatformFetchError(e)) {\n return { visitor: null, authenticated: false, apiUnreachable: true }\n }\n // Network error / aborted / CORS — signed-out is the safe probe assumption.\n return SIGNED_OUT\n }\n\n // Old servers reply 401 to an anonymous probe; new servers reply 200-null.\n // Treat any non-OK status as signed-out WITHOUT logging.\n if (!response.ok) {\n return SIGNED_OUT\n }\n\n let data: unknown\n try {\n data = await response.json()\n } catch {\n return SIGNED_OUT\n }\n\n const visitor = extractVisitor(data)\n return { visitor, authenticated: visitor !== null }\n}\n\nexport interface UseSiteVisitorSessionOptions extends FetchSiteVisitorSessionOptions {\n /** Probe automatically on mount (browser only). Default `true`. */\n fetchOnMount?: boolean\n}\n\nexport interface UseSiteVisitorSessionReturn {\n /** The signed-in visitor, or `null` when signed out / not yet loaded. */\n visitor: Ref<SiteVisitor | null>\n /** `true` iff a visitor is present. */\n isAuthenticated: ComputedRef<boolean>\n /** `true` while a probe is in flight. */\n isLoading: Ref<boolean>\n /**\n * `true` when the last probe did not reach the platform API (non-JSON body — C-298\n * layer 2). Render \"sign-in unavailable\" rather than a login button that cannot work.\n */\n apiUnreachable: Ref<boolean>\n /** Re-run the probe. */\n refresh: () => Promise<void>\n}\n\n/**\n * Vue composable wrapper around {@link fetchSiteVisitorSession}. Reactive\n * `visitor` / `isAuthenticated` / `isLoading`, and probes on mount by default.\n */\nexport function useSiteVisitorSession(\n options: UseSiteVisitorSessionOptions = {},\n): UseSiteVisitorSessionReturn {\n const { fetchOnMount = true, ...fetchOptions } = options\n\n const visitor = ref<SiteVisitor | null>(null)\n const isLoading = ref(false)\n const apiUnreachable = ref(false)\n const isAuthenticated = computed(() => visitor.value !== null)\n\n async function refresh(): Promise<void> {\n isLoading.value = true\n try {\n const result = await fetchSiteVisitorSession(fetchOptions)\n visitor.value = result.visitor\n apiUnreachable.value = result.apiUnreachable === true\n } finally {\n isLoading.value = false\n }\n }\n\n if (fetchOnMount && typeof window !== 'undefined') {\n onMounted(() => {\n void refresh()\n })\n }\n\n return { visitor, isAuthenticated, isLoading, apiUnreachable, refresh }\n}\n","/**\n * @duffcloudservices/cms\n *\n * Vue 3 composables and Vite plugins for DCS CMS integration.\n *\n * @example Basic usage\n * ```vue\n * <script setup lang=\"ts\">\n * import { useTextContent, useSEO } from '@duffcloudservices/cms'\n *\n * const { t } = useTextContent({\n * pageSlug: 'home',\n * defaults: {\n * 'hero.title': 'Welcome',\n * 'hero.subtitle': 'Build amazing things'\n * }\n * })\n *\n * const { applyHead } = useSEO('home')\n * applyHead()\n * </script>\n *\n * <template>\n * <h1>{{ t('hero.title') }}</h1>\n * <p>{{ t('hero.subtitle') }}</p>\n * </template>\n * ```\n *\n * @example Vite configuration\n * ```typescript\n * // vite.config.ts\n * import { dcsContentPlugin, dcsSeoPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsContentPlugin(),\n * dcsSeoPlugin()\n * ]\n * })\n * ```\n *\n * @packageDocumentation\n */\n\n// ---------------------------------------------------------------------------\n// SIDE EFFECT ON IMPORT — fleet conversion capture (C-326).\n//\n// This is the only side effect the package has, and it is deliberate. Conversion\n// capture existed here as an opt-in for months and was adopted by zero of eleven\n// fleet sites, so on 2026-07-26 two reviews independently measured the same hole:\n// Bryan's five tel: CTAs — the site's only conversion action — emitted nothing,\n// and Kim Duff Homes had 6 pageViews in a year. Making it opt-in again would\n// re-create exactly that.\n//\n// It is safe to run unconditionally: `installConversionCapture` declines on the\n// server, inside the editor/preview iframe, under Do Not Track, and when the page\n// opts out — and with no analytics transport present it binds one passive listener\n// and buffers at most 50 events. It mutates no DOM and cannot throw into a click.\n//\n// A site needing per-site booking hosts calls `installConversionCapture({...})`\n// itself at app entry; the first call wins and this one then no-ops.\n//\n// DO NOT add `\"sideEffects\": false` to this package's package.json — it would let a\n// consumer's bundler drop this line and silently restore the blind spot.\n// ---------------------------------------------------------------------------\nimport { installConversionCapture } from './conversionAutoInstall'\n\ninstallConversionCapture()\n\n// Composables\nexport { useTextContent } from './composables/useTextContent'\nexport { useSEO, createSiteSEO } from './composables/useSEO'\n\n// Framework-agnostic SEO resolver + build-time head-tag helpers.\n// Shared by the runtime composable and the build-time static-HTML emitter.\nexport {\n buildHeadTags,\n resolvePageSeo,\n generateJsonLd,\n generateOpenGraphMeta,\n generateTwitterMeta,\n spliceHeadHtml,\n renderHeadTags,\n stripManagedHeadTags,\n escapeJsonLd,\n // C-357 clause 4 — router-level head re-assert (the coverage half of the\n // head-authority contract). Adoption is one line at app entry:\n // installSeoHead(router, { head })\n installSeoHead,\n normalizeSeoHeadPath,\n buildSeoHeadRouteMap,\n createSeoTransformPageData,\n buildVitePressSeoHead,\n defaultRelativePathToRoute,\n buildSitemapXml,\n buildRobotsTxt,\n buildLlmsTxt,\n isRouteIndexable,\n AI_BOTS,\n // Schema-completeness builders (shared @graph spine + Breadcrumb/Blog/FAQ +\n // honest Review/aggregateRating).\n buildGlobalGraph,\n buildBreadcrumbList,\n breadcrumbTrailFromRoute,\n buildBlogPosting,\n buildFaqPage,\n buildReviewSchemaParts,\n filterRealReviews,\n filterRealFaq,\n findReviewItemsForPage,\n absolutizeUrl,\n slugToTitle,\n type ResolvedHeadTags,\n type HeadMetaTag,\n type HeadLinkTag,\n type HeadScriptTag,\n type HeadTagOverrides,\n type InstallSeoHeadOptions,\n type SeoHeadRouterLike,\n type SeoHeadRouteLike,\n type SeoHeadClientLike,\n type SeoHeadEntryLike,\n type SeoHeadInput,\n type SeoHeadPageRoute,\n type SeoHeadPagesManifest,\n type SeoHeadResolution,\n type SeoHeadResolutionReason,\n type CreateSeoTransformPageDataOptions,\n type SeoPageContext,\n type SeoPageTypeRule,\n type ResolvedPageOverrides,\n type VitePressPageData,\n type VitePressHeadConfig,\n type BuildSitemapParams,\n type BuildRobotsParams,\n type BuildLlmsParams,\n type DcsRobotsOptions,\n type SchemaObject,\n type BreadcrumbCrumb,\n type BlogMeta,\n type ReviewSource,\n type FaqSource,\n type ContentConfig,\n} from './seo'\nexport { useReleaseNotes } from './composables/useReleaseNotes'\nexport { useSiteVersion } from './composables/useSiteVersion'\nexport { useMediaCarousel } from './composables/useMediaCarousel'\nexport { useResponsiveImage } from './composables/useResponsiveImage'\nexport {\n useReviewContent,\n type ReviewItem,\n type UseReviewContentConfig,\n type UseReviewContentReturn,\n} from './composables/useReviewContent'\n// Conversion-click capture: one delegated listener that measures outbound booking\n// links, tel:/sms:/mailto: taps, managed form submits and social clicks. Deliberately\n// SDK-agnostic and buffered so it survives an App Insights deferral (fleet P3) — see the\n// module header.\nexport {\n useConversionTracking,\n createConversionTracker,\n attachConversionSink,\n classifyHref,\n redactHref,\n hashTarget,\n hrefScheme,\n isSubmitControl,\n isConversionType,\n doNotTrackEnabled,\n CONVERSION_CAPTURE_VERSION,\n CONVERSION_INTERACTION_TYPES,\n DEFAULT_BOOKING_HOSTS,\n DEFAULT_SOCIAL_HOSTS,\n type ConversionEvent,\n type ConversionSink,\n type ConversionTracker,\n type ConversionTrackingOptions,\n type ConversionInteractionType,\n} from './composables/useConversionTracking'\nexport {\n installConversionCapture,\n installedConversionTracker,\n resetConversionCapture,\n conversionOptedOut,\n inFrame,\n type InstallConversionCaptureOptions,\n type ConversionInstallResult,\n type ConversionInstallSkipReason,\n} from './conversionAutoInstall'\nexport {\n useSiteVisitorSession,\n fetchSiteVisitorSession,\n type SiteVisitor,\n type SiteVisitorSessionResult,\n type FetchSiteVisitorSessionOptions,\n type UseSiteVisitorSessionOptions,\n type UseSiteVisitorSessionReturn,\n} from './composables/useSiteVisitorSession'\n\n// Note: Vue SFC components (ResponsiveImage, PreviewRibbon, LiteMediaEmbed) are\n// exported via the package.json \"exports\" map pointing to raw .vue source files.\n// Import them as:\n// import { ResponsiveImage } from '@duffcloudservices/cms/responsive-image'\n// import { PreviewRibbon } from '@duffcloudservices/cms/components'\n// import LiteMediaEmbed from '@duffcloudservices/cms/lite-media-embed'\n// LiteMediaEmbed is the default click-to-load facade for `useMediaCarousel`\n// youtube/instagram items (poster first, iframe only on user interaction).\n\n// Types\nexport type {\n // Content types\n DcsContentFile,\n TextContentConfig,\n TextContentReturn,\n // Media carousel types\n MediaCarouselItem,\n UseMediaCarouselConfig,\n UseMediaCarouselReturn,\n // SEO types\n SeoConfiguration,\n GlobalSeoConfig,\n PageSeoConfig,\n SeoAuthorConfig,\n SeoSocialConfig,\n SeoImagesConfig,\n SeoOpenGraphConfig,\n SeoTwitterConfig,\n SeoSchemaConfig,\n SeoAlternateConfig,\n SeoVerificationConfig,\n ResolvedPageSeo,\n UseSeoConfig,\n UseSeoReturn,\n HeadOverrides,\n // Release notes types\n ReleaseNote,\n ReleaseNotesReturn,\n SiteVersionReturn,\n} from './types'\n\n// Re-export responsive image types from core for convenience\nexport type {\n ImageContext,\n ResponsiveImageOptions,\n ResponsiveSource,\n ResponsiveImageResult,\n} from '@duffcloudservices/cms-core'\n\nexport { resolveResponsiveImage, isCdnAssetUrl } from '@duffcloudservices/cms-core'\n"]}