@duffcloudservices/cms 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) 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 +53 -1
  7. package/dist/editor/editorBridge.js +141 -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 +369 -22
  11. package/dist/index.js +424 -22
  12. package/dist/index.js.map +1 -1
  13. package/dist/installSeoHead-kWQwObez.d.ts +627 -0
  14. package/dist/plugins/index.d.ts +173 -6
  15. package/dist/plugins/index.js +628 -54
  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 +26 -16
  21. package/src/components/DcsCallButton.test.ts +58 -0
  22. package/src/components/DcsCallButton.vue +19 -4
  23. package/src/components/DcsReviewShowcase.vue +5 -1
  24. package/src/components/LiteMediaEmbed.vue +3 -3
  25. package/src/components/ManagedImage.test.ts +94 -0
  26. package/src/components/ManagedImage.vue +58 -6
  27. package/src/components/PreviewRibbon.vue +4 -1
  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/useResponsiveImage.ts +6 -0
  32. package/src/composables/useSEO.applyHead.test.ts +150 -0
  33. package/src/composables/useSEO.ts +63 -17
  34. package/src/composables/useSiteVersion.ts +4 -1
  35. package/src/composables/useSiteVisitorSession.test.ts +56 -0
  36. package/src/composables/useSiteVisitorSession.ts +39 -3
  37. package/src/composables/useTextContent.ts +9 -1
  38. package/dist/chunk-DAYLLSEE.js +0 -3
  39. package/dist/chunk-DAYLLSEE.js.map +0 -1
  40. package/dist/chunk-F3EIWEZD.js.map +0 -1
  41. package/dist/spliceHeadHtml-CsBEucGy.d.ts +0 -254
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/serveDefineInject.ts","../../src/plugins/motionTokens.ts","../../src/plugins/agentWidget.ts","../../src/plugins/dcsContentPlugin.ts","../../src/seo/playwrightRenderer.ts","../../src/plugins/dcsSeoPlugin.ts","../../src/plugins/dcsEditorPlugin.ts","../../src/plugins/dcsPreviewPlugin.ts","../../src/plugins/dcsCdnImagePlugin.ts","../../src/plugins/responsiveImagePlugin.ts"],"names":["path","fs","yaml","escapeHtml"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Shared serve-mode helpers for the DCS content + SEO Vite plugins.\n *\n * In a production build the plugins bake the `.dcs/*.yaml` payload into a Vite\n * `define` literal (`define: { __DCS_X__: JSON.stringify(data) }`) so the SSG /\n * SPA bundle inlines it exactly as before.\n *\n * In dev (`vite` / `vitepress dev`, i.e. the portal preview) baking a literal\n * is wrong: the value is frozen at server-start and only a `server.restart()`\n * would refresh it — and that restart is exactly what triggers VitePress's\n * MiniSearch rebuild and the duplicate-heading \"server restart failed\" crash.\n *\n * Instead, in serve mode the plugins:\n * 1. redirect the `__DCS_X__` define to a browser global\n * (`define: { __DCS_X__: 'globalThis.__DCS_X__' }`), and\n * 2. inject an inline classic `<script>` at the top of `<head>` on every HTML\n * request that assigns the FRESH on-disk YAML to that global.\n *\n * Because `transformIndexHtml` re-runs on every dev HTML request, a plain\n * iframe reload (the portal's `?_hc` hard reload) reflects the latest on-disk\n * content with NO `server.restart()` and NO module-graph surgery. The consumer\n * is untouched: it still reads the bare `__DCS_X__` identifier, which the define\n * rewrites to `globalThis.__DCS_X__`.\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\n\n/**\n * The three `.dcs` search locations the plugins have always used. VitePress\n * projects have the `docs/` folder as root, so the parent is checked too.\n */\nexport function dcsSearchPaths(projectRoot: string, relPath: string): string[] {\n return [\n path.resolve(projectRoot, relPath),\n path.resolve(projectRoot, '..', relPath),\n path.resolve(process.cwd(), relPath),\n ]\n}\n\n/** First existing `.dcs` file from the standard search, or `undefined`. */\nexport function resolveDcsFile(projectRoot: string, relPath: string): string | undefined {\n return dcsSearchPaths(projectRoot, relPath).find((p) => fs.existsSync(p))\n}\n\n/**\n * Read + parse a `.dcs` YAML file FRESH from disk on every call (no caching).\n * Returns the parsed value, or `undefined` when the file is missing or\n * unparseable. Never throws — a dev HTML request must never 500 on bad YAML.\n */\nexport function readDcsYamlFresh<T = unknown>(\n projectRoot: string,\n relPath: string,\n label: string,\n debug = false\n): T | undefined {\n const found = resolveDcsFile(projectRoot, relPath)\n if (!found) {\n if (debug) console.log(`[${label}] serve: no ${relPath} found; global left unset`)\n return undefined\n }\n try {\n return yaml.load(fs.readFileSync(found, 'utf8')) as T\n } catch (error) {\n console.warn(`[${label}] serve: failed to parse ${relPath}:`, error)\n return undefined\n }\n}\n\n/**\n * Build a safe inline `<script>` body that assigns a JSON-serialisable value to\n * a browser global. Every `<` is escaped to its `<` unicode form so an\n * embedded `</script>` (or `<!--`) inside the content data can never break out\n * of the inline script.\n */\nexport function buildGlobalAssignScript(globalName: string, value: unknown): string {\n const json = JSON.stringify(value ?? null).replace(/</g, '\\\\u003c')\n return `globalThis.${globalName} = ${json};`\n}\n","import type { HtmlTagDescriptor } from 'vite'\n\n/**\n * DCS motion tokens — the customer-site copy of the shared motion vocabulary.\n *\n * Injected as a constant `<style>` into every DCS site's `<head>` by\n * `dcsContentPlugin` (see its `transformIndexHtml`). This is the fleet-wide\n * seam: every site (vue-spa and VitePress) already wires `dcsContentPlugin`,\n * so a cms version bump ships these tokens to the whole fleet with zero\n * per-site source edits.\n *\n * Source of truth is the platform copy at\n * `packages/dcs-ui/src/styles/motion.css` and the `dcs-product-motion` skill —\n * keep the values here in lockstep. Values ONLY: reduced-motion stays a\n * per-component responsibility so no blanket `!important` rule leaks into a\n * site's isolated `.<slug>-page` design subtree.\n */\nexport const DCS_MOTION_TOKENS_CSS = `:root {\n --motion-duration-stagger: 40ms;\n --motion-duration-micro: 80ms;\n --motion-duration-quick: 150ms;\n --motion-duration-fast: 250ms;\n --motion-duration-medium: 350ms;\n --motion-duration-slow: 400ms;\n --motion-duration-very-slow: 500ms;\n --motion-ease-out: cubic-bezier(0.22, 1, 0.36, 1);\n --motion-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n --motion-ease-linear: linear;\n --motion-ease-bounce: cubic-bezier(0.34, 1.36, 0.64, 1);\n --motion-ease-bounce-strong: cubic-bezier(0.34, 3.85, 0.64, 1);\n --motion-distance-xs: 4px;\n --motion-distance-sm: 8px;\n --motion-distance-md: 12px;\n --motion-distance-lg: 24px;\n --motion-scale-lg: 0.96;\n --motion-scale-md: 0.97;\n --motion-scale-sm: 0.98;\n --motion-scale-xs: 0.99;\n --motion-blur-sm: 2px;\n --motion-blur-md: 3px;\n --motion-blur-lg: 8px;\n}`\n\n/**\n * The `<head>` tag descriptor for the motion-token block. `head-prepend` keeps\n * it above the site's own bundled CSS so a per-site `:root` override still wins.\n */\nexport function dcsMotionTokensStyleTag(): HtmlTagDescriptor {\n return {\n tag: 'style',\n attrs: { 'data-dcs-motion-tokens': '' },\n children: DCS_MOTION_TOKENS_CSS,\n injectTo: 'head-prepend',\n }\n}\n","/**\n * DCS AI-assistant widget loader — the cms head-inject seam for Phase 3 of the\n * Managed Site AI Agents plan (.docs/plans/managed-site-ai-agents/index.html).\n *\n * This rides the same always-on `dcsContentPlugin.transformIndexHtml` seam that\n * ships the motion tokens: every DCS site already wires `dcsContentPlugin`, so a\n * cms version bump delivers this loader to the whole fleet with ZERO per-site\n * source edits, and the portal per-site toggle turns the widget on/off at\n * runtime with no rebuild.\n *\n * What gets injected is a tiny (<2 KB) inline `<script>` — NOT the widget. On\n * idle it fetches the public agent config for this site; only if the agent is\n * enabled (HTTP 200) does it publish `window.__DCS_AGENT_BOOTSTRAP__` and inject\n * the real widget bundle from the CDN. The loader is **fail-silent**: a disabled\n * agent (404), a dead backend, a blocked request, or a malformed response all\n * result in no mount and no console error, so the assistant can never break a\n * customer page.\n *\n * Build-only: the loader is injected into the production build output (the fleet\n * delivery), not into `vite dev`. In local dev the config fetch would hit the\n * prod API from a non-whitelisted `localhost` origin and log an uncatchable CORS\n * error for every site developer — so, unlike the constant motion-token block,\n * this network-touching loader stays out of the dev server.\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\n\nimport yaml from 'js-yaml'\nimport type { HtmlTagDescriptor } from 'vite'\n\n/** DCS API origin the loader calls (matches the cms runtime composables). */\nexport const DEFAULT_AGENT_API_BASE_URL = 'https://portal.duffcloudservices.com'\n\n/**\n * Versioned CDN URL of the widget bundle. Both `cdn.` (branding CDN, CorsAllowAll)\n * and the already-live `files.` route serve the same `sgdcs` blob origin with an\n * identical `/agent-widget/v1/...` layout, so this is a plain classic-script\n * include that needs no CORS on the bundle host. Defaults to `files.` because the\n * `cdn.` Front Door route is still provisioning-gated (enableCdnDomain=false) —\n * flip the default only after that domain is live, or pass cdnScriptUrl.\n */\nexport const DEFAULT_AGENT_WIDGET_SCRIPT_URL =\n 'https://files.duffcloudservices.com/agent-widget/v1/dcs-agent-widget.js'\n\nexport interface DcsAgentWidgetLoaderOptions {\n /** Owning site slug — baked into the loader so it can build the config URL. */\n siteSlug: string\n /** DCS API base (default: the shared portal origin). */\n apiBaseUrl?: string\n /** Full URL of the CDN widget bundle. */\n cdnScriptUrl?: string\n}\n\nconst trimTrailingSlash = (value: string): string => value.replace(/\\/+$/, '')\n\n/**\n * JSON-encodes a value for safe embedding inside an inline `<script>` body.\n * `JSON.stringify` alone stops JS string-literal breakout but NOT the HTML\n * parser: a raw `</script>` (or a U+2028/U+2029 line separator, which is a valid\n * JS line terminator) inside the string would still terminate the element. So we\n * additionally escape `<`, `>`, and the two separators to their `\\uXXXX` forms —\n * defense-in-depth even though today every embedded value is build-time DCS\n * provisioning identity (DNS-label slug + platform URLs).\n */\nconst encodeForInlineScript = (value: string): string =>\n JSON.stringify(value)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029')\n\n/**\n * Builds the inline loader script body. Slug + URLs are HTML-safe-encoded (see\n * encodeForInlineScript) so no value can break out of the string literal OR the\n * `<script>` element. Kept intentionally compact to stay under the ~2 KB perf\n * budget (G4).\n */\nexport function buildAgentWidgetLoaderScript(options: DcsAgentWidgetLoaderOptions): string {\n const slug = encodeForInlineScript(options.siteSlug)\n const api = encodeForInlineScript(trimTrailingSlash(options.apiBaseUrl || DEFAULT_AGENT_API_BASE_URL))\n const cdn = encodeForInlineScript(options.cdnScriptUrl || DEFAULT_AGENT_WIDGET_SCRIPT_URL)\n\n return (\n '(function(){try{' +\n 'if(window.__DCS_AGENT_LOADER__)return;window.__DCS_AGENT_LOADER__=1;' +\n // Suppression parity with PreviewRibbon: skip the editor iframe (self!==top,\n // cross-origin access throws → treated as framed) and the ?dcs-hide-widget\n // screenshot/perf suppressor.\n 'try{if(window.self!==window.top)return;}catch(e){return;}' +\n 'if(/[?&]dcs-hide-widget(?:[=&]|$)/.test(location.search))return;' +\n 'var S=' + slug + ',A=' + api + ',C=' + cdn + ';' +\n 'var go=function(){try{' +\n 'fetch(A+\"/api/v1/sites/\"+encodeURIComponent(S)+\"/agent/config\",{headers:{Accept:\"application/json\"}})' +\n '.then(function(r){return r&&r.ok?r.json():null;})' +\n '.then(function(c){if(!c||!c.enabled)return;' +\n 'window.__DCS_AGENT_BOOTSTRAP__={siteSlug:S,apiBaseUrl:A,config:c};' +\n 'var s=document.createElement(\"script\");s.src=C;s.async=true;s.setAttribute(\"data-dcs-agent-slug\",S);' +\n '(document.head||document.documentElement).appendChild(s);})' +\n '.catch(function(){});' +\n '}catch(e){}};' +\n 'if(\"requestIdleCallback\"in window){requestIdleCallback(go,{timeout:4000});}else{setTimeout(go,1200);}' +\n '}catch(e){}})();'\n )\n}\n\nconst SITE_YAML_CANDIDATES = ['.dcs/site.yaml', '../.dcs/site.yaml']\n\n/**\n * Reads `site_slug` from the site's `.dcs/site.yaml` (the same identity file the\n * announcements/provisioning path uses). Returns null when no site.yaml exists\n * or it carries no slug — the caller then injects nothing (silence for non-DCS\n * builds).\n */\nexport function readSiteSlug(projectRoot: string): string | null {\n const candidates = [\n ...SITE_YAML_CANDIDATES.map((rel) => path.resolve(projectRoot, rel)),\n path.resolve(process.cwd(), '.dcs/site.yaml'),\n ]\n for (const candidate of candidates) {\n try {\n if (!fs.existsSync(candidate)) continue\n const parsed = yaml.load(fs.readFileSync(candidate, 'utf8')) as { site_slug?: unknown } | null\n const slug = parsed && typeof parsed.site_slug === 'string' ? parsed.site_slug.trim() : ''\n if (slug) return slug\n } catch {\n // Unreadable/unparseable — try the next candidate, else return null.\n }\n }\n return null\n}\n\nexport interface DcsAgentWidgetTagOptions {\n /** Vite project root used to resolve the site slug. */\n projectRoot: string\n /** Override the DCS API base (default: process.env.VITE_API_BASE_URL, else the shared origin). */\n apiBaseUrl?: string\n /** Override the CDN bundle URL. */\n cdnScriptUrl?: string\n}\n\n/**\n * The `<head>` tag descriptor for the loader, or `null` when this build is not a\n * DCS-managed site (no resolvable slug) — in which case nothing is injected.\n */\nexport function dcsAgentWidgetLoaderTag(options: DcsAgentWidgetTagOptions): HtmlTagDescriptor | null {\n const siteSlug = readSiteSlug(options.projectRoot)\n if (!siteSlug) return null\n\n return {\n tag: 'script',\n attrs: { 'data-dcs-agent-loader': '' },\n children: buildAgentWidgetLoaderScript({\n siteSlug,\n apiBaseUrl: options.apiBaseUrl,\n cdnScriptUrl: options.cdnScriptUrl,\n }),\n injectTo: 'head',\n }\n}\n","/**\n * DCS Content Plugin for Vite\n *\n * Reads `.dcs/content.yaml` at build time and injects content\n * as `__DCS_CONTENT__` global variable for use by useTextContent.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsContentPlugin({ debug: true })\n * ]\n * })\n * ```\n *\n * For VitePress:\n * ```typescript\n * // .vitepress/config.ts\n * import { defineConfig } from 'vitepress'\n * import { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * vite: {\n * plugins: [\n * dcsContentPlugin()\n * ]\n * }\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\nimport type { Plugin, ResolvedConfig, HtmlTagDescriptor } from 'vite'\nimport type { DcsContentFile } from '../types/content'\nimport { readDcsYamlFresh, buildGlobalAssignScript } from './serveDefineInject'\nimport { dcsMotionTokensStyleTag } from './motionTokens'\nimport { dcsAgentWidgetLoaderTag } from './agentWidget'\n\nexport interface DcsContentPluginOptions {\n /** Path to content.yaml relative to project root (default: '.dcs/content.yaml') */\n contentPath?: string\n /** Enable debug logging */\n debug?: boolean\n}\n\n/**\n * Packages that MUST be bundled into the SSR build (noExternal) rather than\n * externalized to `node_modules`.\n *\n * WHY: this plugin injects the site's content as the `__DCS_CONTENT__` Vite\n * `define`. A `define` is a compile-time text substitution that Vite applies\n * ONLY to source that passes through its transform pipeline. When a package is\n * SSR-externalized (loaded by Node's native resolver at server-render time), the\n * substitution never happens — `__DCS_CONTENT__` stays an undefined global on\n * the server. The content composables (`useReviewContent`, `useTextContent`)\n * then fall back to their empty/placeholder branch during SSR while the client\n * (where the define DID apply) renders the real content — producing the\n * \"Hydration completed but contains mismatches\" bug and, worse, baking\n * fabricated fallback content into the crawler-visible SSR HTML.\n *\n * Forcing the cms package `noExternal` bundles it into the SSR graph so the\n * define reaches the composables server-side, keeping SSR and client in lockstep\n * and putting the REAL content in the pre-rendered HTML. This is applied by the\n * plugin itself (merged into the user's Vite config) so every VitePress / vite\n * consumer inherits the fix on a cms bump — no per-site config knob to forget.\n */\nconst DCS_SSR_NO_EXTERNAL = ['@duffcloudservices/cms']\n\n/**\n * Vite plugin that injects .dcs/content.yaml at build time.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n */\nexport function dcsContentPlugin(options: DcsContentPluginOptions = {}): Plugin {\n const { contentPath = '.dcs/content.yaml', debug = false } = options\n\n let resolvedConfig: ResolvedConfig\n // Captured during configResolved — gates the serve-only inline HTML inject.\n let isDev = false\n\n return {\n name: 'dcs-content',\n\n configResolved(config) {\n resolvedConfig = config\n isDev = config.command === 'serve'\n },\n\n config(config, env) {\n // ── Dev / preview (serve) ──────────────────────────────────────────────\n // Don't bake content into the bundle. Redirect the `__DCS_CONTENT__`\n // define to a browser global that `transformIndexHtml` (below) refreshes\n // from disk on every HTML request — so a plain iframe reload reflects the\n // latest on-disk content with NO server.restart() and NO module-graph\n // surgery. The consumer is untouched: it still reads the bare\n // `__DCS_CONTENT__` identifier, which this define rewrites to\n // `globalThis.__DCS_CONTENT__`.\n if (env.command === 'serve') {\n return {\n define: {\n __DCS_CONTENT__: 'globalThis.__DCS_CONTENT__',\n },\n ssr: { noExternal: DCS_SSR_NO_EXTERNAL },\n }\n }\n\n // ── Production build ───────────────────────────────────────────────────\n // Unchanged: read content.yaml now and bake it into the bundle as a\n // JSON literal so the SSG / SPA output inlines content exactly as before.\n const projectRoot = config.root || process.cwd()\n\n // Try to find content.yaml in multiple locations\n // VitePress projects have the docs folder as root, so check parent too\n const possiblePaths = [\n path.resolve(projectRoot, contentPath),\n path.resolve(projectRoot, '..', contentPath),\n path.resolve(process.cwd(), contentPath),\n ]\n\n let foundPath: string | undefined\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n foundPath = testPath\n break\n }\n }\n\n if (!foundPath) {\n if (debug) {\n console.log('[dcs-content] No content.yaml found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n console.log('[dcs-content] Using defaults only')\n }\n return {\n define: {\n __DCS_CONTENT__: 'undefined',\n },\n ssr: { noExternal: DCS_SSR_NO_EXTERNAL },\n }\n }\n\n try {\n const fileContent = fs.readFileSync(foundPath, 'utf8')\n const content = yaml.load(fileContent) as DcsContentFile\n\n if (debug) {\n console.log(`[dcs-content] Loaded ${foundPath}`)\n console.log(`[dcs-content] Version: ${content.version}`)\n console.log(`[dcs-content] Pages: ${Object.keys(content.pages ?? {}).join(', ') || '(none)'}`)\n console.log(`[dcs-content] Global keys: ${Object.keys(content.global ?? {}).length}`)\n }\n\n return {\n define: {\n __DCS_CONTENT__: JSON.stringify(content),\n },\n ssr: { noExternal: DCS_SSR_NO_EXTERNAL },\n }\n } catch (error) {\n console.warn('[dcs-content] Failed to parse content.yaml:', error)\n return {\n define: {\n __DCS_CONTENT__: 'undefined',\n },\n ssr: { noExternal: DCS_SSR_NO_EXTERNAL },\n }\n }\n },\n\n /**\n * Inject into `<head>` on every HTML request. Two concerns:\n *\n * 1. The constant DCS motion-token `:root` block (`DCS_MOTION_TOKENS_CSS`)\n * — the fleet-wide seam that gives every DCS site the shared `--motion-*`\n * vocabulary. It carries no per-site data, so (unlike the content inject)\n * it is NOT gated on `isDev`: it runs in both `serve` and `build`.\n * `head-prepend` keeps it ABOVE the bundled site CSS, so a site's own\n * `:root` (theme `tailwind.css` / `style.css`) can still override a token.\n *\n * 2. Dev-only: the FRESH on-disk `content.yaml` as an inline\n * `globalThis.__DCS_CONTENT__ = {…}` classic script. Runs on every dev\n * HTML request, so the portal's `?_hc` hard reload re-reads the YAML with\n * no restart. A classic `<script>` in `<head>` executes before the\n * deferred module app entry, so the global is set before the consumer's\n * `useTextContent` setup reads it. Gated on `isDev` so it never runs in\n * `vite`/`vitepress build` (the build path keeps the baked `define`).\n */\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n // (1) Always-on, per the note above: the constant motion-token block.\n const tags: HtmlTagDescriptor[] = [dcsMotionTokensStyleTag()]\n\n // (1b) Build-only: the AI-assistant widget loader. Rides this same\n // fleet-wide seam (a cms bump ships it everywhere with no per-site\n // edits) but is NOT injected into `vite dev` — its idle config\n // fetch would hit the prod API from a non-whitelisted localhost\n // origin and log an uncatchable CORS error for every site dev. The\n // loader is fail-silent and returns null for non-DCS builds (no\n // resolvable site slug), so nothing is injected there.\n if (!isDev) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n const agentTag = dcsAgentWidgetLoaderTag({\n projectRoot,\n apiBaseUrl: process.env.VITE_API_BASE_URL,\n })\n if (agentTag) tags.push(agentTag)\n }\n\n // (2) Dev-only fresh content inject.\n if (isDev) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n const content = readDcsYamlFresh<DcsContentFile>(\n projectRoot,\n contentPath,\n 'dcs-content',\n debug\n )\n // No file (or unparseable) ⇒ leave the global unset; the consumer's\n // `typeof globalThis.__DCS_CONTENT__` check falls back to defaults.\n if (content !== undefined) {\n if (debug) {\n console.log('[dcs-content] serve: injecting fresh __DCS_CONTENT__ into <head>')\n }\n tags.push({\n tag: 'script',\n children: buildGlobalAssignScript('__DCS_CONTENT__', content),\n injectTo: 'head-prepend',\n })\n }\n }\n\n return { html, tags }\n },\n },\n\n // Watch for changes in development.\n //\n // Downgraded from `server.restart()` to a debounced client `full-reload`:\n // freshness already comes from `transformIndexHtml` re-reading the YAML on\n // every HTML request, so all we need on change is to nudge the browser to\n // reload. A `server.restart()` would rebuild VitePress's dev MiniSearch\n // index and can throw \"server restart failed\" on a duplicate heading id —\n // exactly the fragility this fix removes. A `full-reload` does neither.\n configureServer(server) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n\n const watchPaths = [\n path.resolve(projectRoot, contentPath),\n path.resolve(projectRoot, '..', contentPath),\n ].map((p) => path.normalize(p))\n\n watchPaths.forEach((watchPath) => {\n if (fs.existsSync(watchPath)) {\n server.watcher.add(watchPath)\n }\n })\n\n let reloadTimer: ReturnType<typeof setTimeout> | undefined\n server.watcher.on('change', (changedPath) => {\n if (!watchPaths.includes(path.normalize(changedPath))) return\n if (reloadTimer) clearTimeout(reloadTimer)\n reloadTimer = setTimeout(() => {\n if (debug) {\n console.log('[dcs-content] content.yaml changed, sending full-reload')\n }\n server.ws.send({ type: 'full-reload' })\n }, 100)\n })\n },\n }\n}\n","/**\n * The production {@link RouteRenderer}: a headless-Playwright renderer that\n * drives the JUST-BUILT SPA from `dist/` and returns each route's rendered\n * `#app` body.\n *\n * Design choices (see `bodyPrerender.ts` for the why):\n * - **Renders the PRODUCTION bundle**, so `import.meta.env.DEV` is already\n * `false` and the dev-only fabrication gates render their honest empty state\n * (A-34 safety by construction — no separate SSR bundle to leak defaults).\n * - **Playwright is dynamically imported and OPTIONAL.** If it (or a browser)\n * is unavailable, the factory returns `null` and the caller no-ops (the head\n * + JSON-LD emit is untouched, so the build never breaks on missing infra).\n * Playwright is already a universal devDependency of the DCS site fleet\n * (snapshot capture), so this activates on a cms bump with no per-site edit.\n * - **Offline + deterministic.** A tiny built-in static server serves `dist/`;\n * every EXTERNAL request (CDN images, App Insights, the site-auth probe) is\n * aborted. The DOM still carries those `<img>`/`<link>` tags (crawler-visible)\n * while the render stays fast and free of expected offline network noise.\n * - **Crash-only fail-loud.** Only an uncaught page exception or a failed mount\n * is reported as a fatal error; benign offline console errors are not. The\n * caller turns a fatal error into a red build.\n */\n\nimport http from 'node:http'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport type { AddressInfo } from 'node:net'\nimport type { RenderedRoute, RouteRenderer } from './bodyPrerender'\n\n/** A started static server plus its `http://127.0.0.1:<port>` origin. */\ninterface StaticServer {\n origin: string\n close: () => Promise<void>\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.webp': 'image/webp',\n '.gif': 'image/gif',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.txt': 'text/plain; charset=utf-8',\n '.xml': 'application/xml; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n}\n\n/**\n * Serve `dist/` over loopback with directory-index resolution\n * (`/services` → `/services/index.html`) and an SPA fallback to the root\n * `index.html` for any path with no matching file/dir. Bound to `127.0.0.1:0`\n * so the OS assigns a free port; nothing is exposed off-box.\n */\nasync function startStaticServer(outDir: string): Promise<StaticServer> {\n const server = http.createServer((req, res) => {\n let pathname = '/'\n try {\n pathname = decodeURIComponent(new URL(req.url ?? '/', 'http://127.0.0.1').pathname)\n } catch {\n pathname = '/'\n }\n\n // Resolve within outDir; reject path traversal.\n const resolved = path.resolve(outDir, '.' + pathname)\n if (resolved !== outDir && !resolved.startsWith(outDir + path.sep)) {\n res.writeHead(403)\n res.end()\n return\n }\n\n let filePath = resolved\n try {\n const stat = fs.statSync(filePath)\n if (stat.isDirectory()) filePath = path.join(filePath, 'index.html')\n } catch {\n // Missing path: a request for an asset (has an extension) is a genuine 404;\n // a route-shaped request falls back to the SPA shell.\n if (path.extname(pathname)) {\n res.writeHead(404)\n res.end()\n return\n }\n filePath = path.join(outDir, 'index.html')\n }\n\n fs.readFile(filePath, (err, buf) => {\n if (err) {\n res.writeHead(404)\n res.end()\n return\n }\n res.writeHead(200, {\n 'Content-Type': CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream',\n })\n res.end(buf)\n })\n })\n\n await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve))\n const addr = server.address() as AddressInfo\n const origin = `http://127.0.0.1:${addr.port}`\n\n return {\n origin,\n close: () =>\n new Promise<void>((resolve) => {\n server.close(() => resolve())\n }),\n }\n}\n\n/** Options for {@link createPlaywrightRenderer}. */\nexport interface PlaywrightRendererOptions {\n /** Absolute path to the built SPA output directory (`dist`). */\n outDir: string\n /** Per-route navigation + settle budget (ms). Default 30000 nav, 15000 mount. */\n navTimeoutMs?: number\n mountTimeoutMs?: number\n /** Extra quiet settle after mount for post-render microtasks (ms). Default 250. */\n settleMs?: number\n debug?: boolean\n}\n\n/** A ready renderer plus a `close()` that tears down the browser + server. */\nexport interface PlaywrightRendererHandle {\n renderer: RouteRenderer\n close: () => Promise<void>\n}\n\n// Minimal structural types for the OPTIONAL, dynamically-imported `playwright`\n// surface — avoids a hard type/dependency on the package (it is not a cms\n// devDependency; it resolves from the consuming site at build time).\ninterface PwConsoleMessage {\n type(): string\n text(): string\n}\ninterface PwPage {\n on(event: 'pageerror', cb: (err: Error) => void): void\n on(event: 'console', cb: (msg: PwConsoleMessage) => void): void\n route(url: string, handler: (route: PwRoute) => unknown): Promise<void>\n goto(url: string, opts: { waitUntil: string; timeout: number }): Promise<unknown>\n waitForFunction(fn: () => unknown, arg?: unknown, opts?: { timeout: number }): Promise<unknown>\n waitForTimeout(ms: number): Promise<void>\n $eval(sel: string, fn: (el: Element) => string): Promise<string>\n close(): Promise<void>\n}\ninterface PwRoute {\n request(): { url(): string }\n abort(): Promise<void>\n continue(): Promise<void>\n}\ninterface PwContext {\n newPage(): Promise<PwPage>\n close(): Promise<void>\n}\ninterface PwBrowser {\n newContext(opts?: unknown): Promise<PwContext>\n close(): Promise<void>\n}\n\n/**\n * Build the headless-Playwright renderer, or return `null` when Playwright / a\n * browser is unavailable (the caller then no-ops — head + JSON-LD are already\n * emitted, so the build is unaffected). Never throws on missing infra.\n */\nexport async function createPlaywrightRenderer(\n options: PlaywrightRendererOptions\n): Promise<PlaywrightRendererHandle | null> {\n const {\n outDir,\n navTimeoutMs = 30000,\n mountTimeoutMs = 15000,\n settleMs = 250,\n debug = false,\n } = options\n\n // Optional dependency: resolve `playwright` from the consuming site. The\n // computed specifier keeps the bundler from trying to resolve it at build time.\n let chromium: { launch(opts?: unknown): Promise<PwBrowser> } | undefined\n try {\n const specifier = 'playwright'\n const pw = (await import(/* @vite-ignore */ specifier)) as {\n chromium?: { launch(opts?: unknown): Promise<PwBrowser> }\n }\n chromium = pw.chromium\n } catch {\n console.warn(\n '[dcs-seo] body-prerender: `playwright` is not installed; skipping body ' +\n 'prerender (head + JSON-LD still emitted). `pnpm add -D playwright` to enable.'\n )\n return null\n }\n if (!chromium) {\n console.warn('[dcs-seo] body-prerender: playwright has no chromium export; skipping.')\n return null\n }\n\n let browser: PwBrowser\n try {\n browser = await chromium.launch({ headless: true })\n } catch (err) {\n console.warn(\n '[dcs-seo] body-prerender: failed to launch Chromium (run ' +\n '`npx playwright install chromium`); skipping body prerender:',\n err instanceof Error ? err.message : err\n )\n return null\n }\n\n const staticServer = await startStaticServer(outDir)\n const context = await browser.newContext({ reducedMotion: 'reduce' })\n\n const renderer: RouteRenderer = {\n async renderRoute(routePath: string): Promise<RenderedRoute> {\n const page = await context.newPage()\n const errors: string[] = []\n page.on('pageerror', (err) => errors.push(`pageerror: ${err.message}`))\n if (debug) {\n page.on('console', (msg) => {\n if (msg.type() === 'error') console.log(`[dcs-seo] (console) ${msg.text()}`)\n })\n }\n\n // Keep the render offline + fast: serve our own origin, abort everything\n // else. The DOM keeps external <img>/<link> tags regardless. Compare the\n // parsed origin, not a string prefix — `http://127.0.0.1:<port>@evil.com/…`\n // starts with our origin but resolves to evil.com. (Perf/noise gate, not a\n // security boundary — the trust boundary is that we run the site's own\n // production bundle.)\n await page.route('**/*', (route) => {\n let sameOrigin = false\n try {\n sameOrigin = new URL(route.request().url()).origin === staticServer.origin\n } catch {\n sameOrigin = false\n }\n // Swallow the continue/abort rejection that races page.close (\"Route is\n // already handled\" / target closed) — an unhandled rejection would crash\n // the vite process under Node's throw-on-unhandled default.\n if (sameOrigin) {\n route.continue().catch(() => {})\n } else {\n route.abort().catch(() => {})\n }\n })\n\n try {\n await page.goto(staticServer.origin + routePath, {\n waitUntil: 'load',\n timeout: navTimeoutMs,\n })\n // The app mounts into #app; wait until it has real children (a failed\n // mount / crash never satisfies this → timeout → fatal error below).\n await page.waitForFunction(\n () => {\n const el = document.querySelector('#app')\n return !!el && el.children.length > 0\n },\n undefined,\n { timeout: mountTimeoutMs }\n )\n if (settleMs > 0) await page.waitForTimeout(settleMs)\n const bodyHtml = await page.$eval('#app', (el) => el.innerHTML)\n return { bodyHtml: bodyHtml ?? '', errors }\n } catch (err) {\n // A nav/mount timeout is itself a fatal render failure — report it so the\n // caller fails the build loud rather than shipping an empty body.\n errors.push(err instanceof Error ? err.message : String(err))\n return { bodyHtml: '', errors }\n } finally {\n await page.close()\n }\n },\n }\n\n return {\n renderer,\n async close() {\n try {\n await context.close()\n } catch {\n /* ignore */\n }\n try {\n await browser.close()\n } catch {\n /* ignore */\n }\n await staticServer.close()\n },\n }\n}\n","/**\n * DCS SEO Plugin for Vite\n *\n * Two responsibilities, both driven by `.dcs/seo.yaml`:\n *\n * 1. **Build-time define** (always on): reads `.dcs/seo.yaml` and injects it as\n * the `__DCS_SEO__` global for the runtime `useSEO` composable.\n *\n * 2. **Static `<head>` emitter** (opt-in via `emitStaticHtml: true`, default\n * OFF): after the bundle is written, reads the SPA's built `index.html`,\n * and for every route in `.dcs/pages.yaml` writes a per-route\n * `dist/<path>/index.html` whose `<head>` carries the resolved title, meta,\n * canonical, Open Graph, Twitter, and JSON-LD tags. This gives a Vue SPA\n * per-route static SEO **without** vite-ssg.\n *\n * VitePress sites already bake SEO via their own config, so they leave this\n * option OFF and are completely unaffected.\n *\n * @example Runtime define only (default — safe for VitePress)\n * ```typescript\n * // vite.config.ts\n * import { dcsSeoPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [dcsSeoPlugin({ debug: true })]\n * })\n * ```\n *\n * @example Vue SPA with per-route static <head> emission\n * ```typescript\n * // vite.config.ts\n * import { dcsSeoPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsSeoPlugin({\n * emitStaticHtml: true, // turn the emitter ON\n * pagesPath: '.dcs/pages.yaml', // route manifest (default)\n * noindex: ['account', 'projects'], // robots: noindex,nofollow\n * exclude: ['/preview'], // skip these routes entirely\n * })\n * ]\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\nimport type { Plugin, ResolvedConfig, HtmlTagDescriptor } from 'vite'\nimport type { SeoConfiguration } from '../types/seo'\nimport { readDcsYamlFresh, buildGlobalAssignScript } from './serveDefineInject'\nimport { buildHeadTags } from '../seo/headTags'\nimport { spliceHeadHtml } from '../seo/spliceHeadHtml'\nimport { loadPagesManifest, type PageRouteEntry } from '../seo/pagesManifest'\nimport {\n prerenderBodies,\n isBodyPrerenderEnabled,\n routeToOutputFile,\n} from '../seo/bodyPrerender'\nimport { createPlaywrightRenderer } from '../seo/playwrightRenderer'\nimport {\n buildSitemapXml,\n buildRobotsTxt,\n buildLlmsTxt,\n matchesExcludedGlob,\n isHandAuthoredRobotsAcceptable,\n type DcsRobotsOptions,\n} from '../seo/sitemap'\nimport {\n breadcrumbTrailFromRoute,\n findReviewItemsForPage,\n findBusinessLicense,\n type ContentConfig,\n type ReviewSource,\n type BlogMeta,\n type FaqSource,\n} from '../seo/schemaGraph'\n\nexport interface DcsSeoPluginOptions {\n /** Path to seo.yaml relative to project root (default: '.dcs/seo.yaml') */\n seoPath?: string\n /** Enable debug logging */\n debug?: boolean\n\n /**\n * Opt-in: emit per-route static `<head>` (meta + JSON-LD) into the built\n * `dist/` at the end of the build. Default `false` — VitePress sites and any\n * site that bakes its own SEO are unaffected when this is off.\n */\n emitStaticHtml?: boolean\n\n /**\n * Build-time BODY prerender: after the per-route `<head>` is emitted, render\n * each indexable route's real DOM from the just-built SPA (headless Chromium\n * via the site's `playwright`) and splice it into the mount container so non-JS\n * AI crawlers see the page's body prose — not just `<div id=\"app\"></div>`.\n *\n * Defaults to whatever `emitStaticHtml` is, so every SPA already emitting\n * per-route HTML gains crawler-visible bodies on a cms bump with no per-site\n * edit. Set `false` here to keep head-only emission. It is ALSO overridable\n * per-site (no code change / no cms republish) via `.dcs/seo.yaml`\n * `prerenderBody: false`. noindex/auth-gated routes are never body-prerendered;\n * a route that crashes at render time fails the build loud rather than shipping\n * a broken body. Requires `playwright` in the site (already the fleet default);\n * absent ⇒ graceful no-op (head + JSON-LD still emitted).\n */\n prerenderBody?: boolean\n\n /**\n * Path to the route manifest relative to project root, used only when\n * `emitStaticHtml` is true. Default `'.dcs/pages.yaml'`.\n */\n pagesPath?: string\n\n /**\n * Path to `content.yaml` relative to project root, used only when\n * `emitStaticHtml` is true. Threads REAL reviews into the honest\n * Review/aggregateRating on the per-route LocalBusiness node. Default\n * `'.dcs/content.yaml'`. Missing/unparseable ⇒ no reviews emitted.\n */\n contentPath?: string\n\n /**\n * Routes to skip entirely (no per-route HTML written). Matched against the\n * route `path` (e.g. `'/preview'`) OR the route `slug` (e.g. `'account'`).\n */\n exclude?: string[]\n\n /**\n * Routes that should receive `robots: noindex, nofollow`. Matched against the\n * route `path` OR `slug`. The home route (`/`) still overwrites\n * `dist/index.html`.\n */\n noindex?: string[]\n\n /**\n * Opt-in: emit the site-wide static files `dist/sitemap.xml`,\n * `dist/robots.txt`, and `dist/llms.txt` after the per-route HTML loop.\n *\n * Defaults to whatever `emitStaticHtml` is — so every site already calling\n * `dcsSeoPlugin({ emitStaticHtml: true })` gets sitemap/robots/llms for free\n * with zero per-site edits. Set explicitly to `false` to keep emitting\n * per-route HTML without the site files. The whole block is wrapped in the\n * same try/catch + existsSync guards, so it can never break a build.\n */\n emitSiteFiles?: boolean\n\n /**\n * Canonical production origin, e.g. `https://ironoakcontractors.com`. Used as\n * the sitemap/llms base and the absolute `Sitemap:` line in robots.txt. When\n * absent, falls back to `seo.yaml`'s `global.siteUrl`; if neither is known and\n * no per-page canonicals exist, the sitemap + llms.txt no-op (robots still\n * emits). Never guessed.\n */\n siteUrl?: string\n\n /** robots.txt override hooks (disallow/allow/extra/aiBots/force). */\n robots?: DcsRobotsOptions\n\n /**\n * Preview / staging gate. When `true`, robots.txt becomes `Disallow: /` (no\n * `Sitemap:` line) and neither sitemap.xml nor llms.txt is emitted (privacy).\n * Detected ONLY via this explicit option — never guessed.\n */\n preview?: boolean\n\n /** Emit `dist/llms.txt` (default `true`; always off in preview mode). */\n llms?: boolean\n}\n\n/** Resolve the SPA output directory from Vite config (absolute path). */\nfunction resolveOutDir(config: ResolvedConfig): string {\n const out = config.build?.outDir || 'dist'\n return path.isAbsolute(out) ? out : path.resolve(config.root || process.cwd(), out)\n}\n\n/**\n * Load + parse seo.yaml from the usual `.dcs` locations.\n * Returns `{ config, foundPath }` or `null` (missing/unparseable).\n */\nfunction loadSeoConfig(\n projectRoot: string,\n seoPath: string,\n debug: boolean\n): { config: SeoConfiguration; foundPath: string } | null {\n const possiblePaths = [\n path.resolve(projectRoot, seoPath),\n path.resolve(projectRoot, '..', seoPath),\n path.resolve(process.cwd(), seoPath),\n ]\n\n let foundPath: string | undefined\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n foundPath = testPath\n break\n }\n }\n\n if (!foundPath) {\n if (debug) {\n console.log('[dcs-seo] No seo.yaml found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n console.log('[dcs-seo] Using defaults only')\n }\n return null\n }\n\n try {\n const fileContent = fs.readFileSync(foundPath, 'utf8')\n const config = yaml.load(fileContent) as SeoConfiguration\n return { config, foundPath }\n } catch (error) {\n console.warn('[dcs-seo] Failed to parse seo.yaml:', error)\n return null\n }\n}\n\n/**\n * Load + parse `.dcs/content.yaml` from the usual `.dcs` locations. Returns the\n * parsed config or `undefined` (missing/unparseable). Used to thread REAL\n * reviews into the honest Review/aggregateRating builder. Never throws.\n */\nfunction loadContentConfig(\n projectRoot: string,\n contentRelPath: string,\n debug: boolean\n): ContentConfig | undefined {\n const possiblePaths = [\n path.resolve(projectRoot, contentRelPath),\n path.resolve(projectRoot, '..', contentRelPath),\n path.resolve(process.cwd(), contentRelPath),\n ]\n const foundPath = possiblePaths.find((p) => fs.existsSync(p))\n if (!foundPath) {\n if (debug) console.log(`[dcs-seo] No content.yaml found (reviews disabled)`)\n return undefined\n }\n try {\n const parsed = yaml.load(fs.readFileSync(foundPath, 'utf8'))\n if (!parsed || typeof parsed !== 'object') return undefined\n return parsed as ContentConfig\n } catch (error) {\n console.warn('[dcs-seo] Failed to parse content.yaml (reviews disabled):', error)\n return undefined\n }\n}\n\n/** Extract the optional top-level `excluded:` glob list from pages.yaml. */\nfunction loadExcludedGlobs(projectRoot: string, pagesPath: string): string[] {\n const possiblePaths = [\n path.resolve(projectRoot, pagesPath),\n path.resolve(projectRoot, '..', pagesPath),\n path.resolve(process.cwd(), pagesPath),\n ]\n const found = possiblePaths.find((p) => fs.existsSync(p))\n if (!found) return []\n try {\n const raw = yaml.load(fs.readFileSync(found, 'utf8')) as { excluded?: unknown } | null\n const excluded = raw?.excluded\n if (!Array.isArray(excluded)) return []\n return excluded.filter((e): e is string => typeof e === 'string')\n } catch {\n return []\n }\n}\n\n/**\n * Derive a single site-wide lastmod from seo.yaml `lastUpdated` (preferred) or\n * pages.yaml `lastUpdated`. No fabricated per-page dates.\n */\nfunction deriveLastmod(\n seoConfig: SeoConfiguration | undefined,\n projectRoot: string,\n pagesPath: string\n): string | undefined {\n const fromSeo = seoConfig?.lastUpdated\n if (typeof fromSeo === 'string' && fromSeo.length > 0) return fromSeo\n\n const possiblePaths = [\n path.resolve(projectRoot, pagesPath),\n path.resolve(projectRoot, '..', pagesPath),\n path.resolve(process.cwd(), pagesPath),\n ]\n const found = possiblePaths.find((p) => fs.existsSync(p))\n if (!found) return undefined\n try {\n const raw = yaml.load(fs.readFileSync(found, 'utf8')) as { lastUpdated?: unknown } | null\n const fromPages = raw?.lastUpdated\n if (typeof fromPages === 'string' && fromPages.length > 0) return fromPages\n } catch {\n /* ignore */\n }\n return undefined\n}\n\n/**\n * Core site-file emitter, separated from Vite so it is unit-testable.\n *\n * Writes the site-wide static files into `outDir`, reusing the SAME routes +\n * seoConfig already loaded by the per-route HTML loop (no second manifest read):\n * - `sitemap.xml` (skipped in preview, or when no absolute base is derivable)\n * - `robots.txt` (always; preview = `Disallow: /`; don't-clobber unless force)\n * - `llms.txt` (skipped in preview, or when nothing indexable)\n *\n * Returns the list of relative filenames written. Pure aside from `fs` writes —\n * deterministic + idempotent. Never throws (the caller also wraps in try/catch).\n */\nexport function emitSiteFiles(params: {\n outDir: string\n projectRoot: string\n pagesPath: string\n routes: PageRouteEntry[]\n seoConfig: SeoConfiguration | undefined\n siteUrl?: string\n exclude?: string[]\n noindex?: string[]\n preview?: boolean\n robots?: DcsRobotsOptions\n llms?: boolean\n debug?: boolean\n}): string[] {\n const {\n outDir,\n projectRoot,\n pagesPath,\n routes,\n seoConfig,\n siteUrl,\n exclude = [],\n noindex = [],\n preview = false,\n robots = {},\n llms = true,\n debug = false,\n } = params\n\n const written: string[] = []\n\n const effectiveSiteUrl = siteUrl || seoConfig?.global?.siteUrl\n const excludedGlobs = loadExcludedGlobs(projectRoot, pagesPath)\n const lastmod = deriveLastmod(seoConfig, projectRoot, pagesPath)\n\n // ── sitemap.xml (not in preview) ──────────────────────────────────────────\n let wroteSitemap = false\n if (!preview) {\n const xml = buildSitemapXml({\n routes,\n siteUrl: effectiveSiteUrl,\n seoConfig,\n exclude,\n noindex,\n excludedGlobs,\n lastmod,\n })\n if (xml) {\n fs.writeFileSync(path.join(outDir, 'sitemap.xml'), xml, 'utf8')\n wroteSitemap = true\n written.push('sitemap.xml')\n if (debug) console.log('[dcs-seo] wrote sitemap.xml')\n } else if (debug) {\n console.log('[dcs-seo] no absolute <loc> derivable; sitemap.xml not written')\n }\n }\n\n // ── robots.txt (always, unless robots.enabled is false) ───────────────────\n if (robots.enabled ?? true) {\n const robotsPath = path.join(outDir, 'robots.txt')\n // Factory-as-single-source (validated override): a hand-authored\n // public/robots.txt Vite copied into dist is KEPT only when it meets the\n // factory floor (a real `User-agent: *` group + every AI-bot tier). A stub\n // (`Sitemap:`-only, like just-posh) or an SPA-shell HTML page fails the\n // floor and is overwritten so AI discovery is guaranteed on every site.\n // `force` always overwrites; an absent file is always written.\n const existing =\n fs.existsSync(robotsPath) && !robots.force ? fs.readFileSync(robotsPath, 'utf8') : null\n const keepHandAuthored = existing !== null && isHandAuthoredRobotsAcceptable(existing)\n if (keepHandAuthored) {\n if (debug)\n console.log('[dcs-seo] dist/robots.txt is hand-authored and meets the floor; leaving it')\n } else {\n const txt = buildRobotsTxt({\n siteUrl: effectiveSiteUrl,\n preview,\n robots,\n hasSitemap: wroteSitemap,\n })\n fs.writeFileSync(robotsPath, txt, 'utf8')\n written.push('robots.txt')\n if (debug) {\n console.log(\n existing !== null\n ? '[dcs-seo] dist/robots.txt failed the factory floor; overwrote with emitted robots'\n : '[dcs-seo] wrote robots.txt'\n )\n }\n }\n }\n\n // ── llms.txt (not in preview) ─────────────────────────────────────────────\n if (llms && !preview) {\n const txt = buildLlmsTxt({\n routes,\n siteUrl: effectiveSiteUrl,\n seoConfig,\n exclude,\n noindex,\n excludedGlobs,\n })\n if (txt) {\n fs.writeFileSync(path.join(outDir, 'llms.txt'), txt, 'utf8')\n written.push('llms.txt')\n if (debug) console.log('[dcs-seo] wrote llms.txt')\n } else if (debug) {\n console.log('[dcs-seo] nothing indexable; llms.txt not written')\n }\n }\n\n return written\n}\n\n/**\n * Pick a structured FAQ array (`[{ q, a }]`) for a page from content.yaml, if\n * one exists under a `faq` key (page-scoped, then global). Returns `undefined`\n * when there is no structured Q&A — a `<FaqPage />` component is NOT a source,\n * so its pages correctly yield nothing (honesty).\n */\nfunction pickPageFaq(\n content: ContentConfig | undefined,\n pageSlug: string\n): FaqSource[] | undefined {\n if (!content) return undefined\n const fromBlock = (block: Record<string, unknown> | undefined): FaqSource[] | undefined => {\n if (!block) return undefined\n const v = block['faq']\n return Array.isArray(v) ? (v as FaqSource[]) : undefined\n }\n return fromBlock(content.pages?.[pageSlug]) ?? fromBlock(content.global)\n}\n\n/**\n * Core emitter, separated from Vite so it is unit-testable.\n *\n * For each route: build the head tags from the shared resolver, splice them\n * into the shell HTML, and write `dist/<path>/index.html`. Returns the number\n * of files written. Pure aside from `fs` writes — deterministic + idempotent.\n */\nexport function emitStaticSeoHtml(params: {\n outDir: string\n shellHtml: string\n routes: PageRouteEntry[]\n seoConfig: SeoConfiguration | undefined\n /**\n * Parsed `.dcs/content.yaml` — threads REAL review items (honesty-gated) into\n * the per-route LocalBusiness node. Optional; omit ⇒ no Review/aggregateRating.\n */\n contentConfig?: ContentConfig\n exclude?: string[]\n noindex?: string[]\n /** `pages.yaml` top-level `excluded:` globs — routes matching are NOT emitted. */\n excludedGlobs?: string[]\n debug?: boolean\n}): number {\n const {\n outDir,\n shellHtml,\n routes,\n seoConfig,\n contentConfig,\n exclude = [],\n noindex = [],\n excludedGlobs = [],\n debug = false,\n } = params\n\n const excludeSet = new Set(exclude)\n const noindexSet = new Set(noindex)\n\n // Route → human title map (for breadcrumb hop labels), from the manifest.\n const siteUrl = seoConfig?.global?.siteUrl ?? ''\n const routeTitles: Record<string, string> = { '/': 'Home' }\n for (const r of routes) {\n if (r.title && r.path) routeTitles[r.path.replace(/\\/+$/, '') || '/'] = r.title\n }\n\n let written = 0\n for (const route of routes) {\n if (excludeSet.has(route.path) || (route.slug && excludeSet.has(route.slug))) {\n if (debug) console.log(`[dcs-seo] skip (excluded): ${route.path}`)\n continue\n }\n\n // Honor pages.yaml top-level `excluded:` globs (e.g. `/dev-*`, `/_*`): an\n // excluded route must NOT get a per-route index.html, or it becomes a\n // crawlable/indexable page despite being intentionally hidden — a privacy\n // and crawl-budget leak. Same glob semantics as the sitemap path.\n if (matchesExcludedGlob(route.path, excludedGlobs)) {\n if (debug) console.log(`[dcs-seo] skip (excluded glob): ${route.path}`)\n continue\n }\n\n const forceNoindex =\n noindexSet.has(route.path) || (route.slug && noindexSet.has(route.slug))\n\n // Breadcrumb trail from route depth (home ⇒ none); titles from the manifest.\n const breadcrumbTrail = siteUrl\n ? breadcrumbTrailFromRoute(route.path, siteUrl, routeTitles)\n : undefined\n\n // REAL reviews for this page from content.yaml (honesty-gated downstream).\n const reviews: ReviewSource[] | undefined = contentConfig\n ? findReviewItemsForPage(contentConfig, route.slug)\n : undefined\n\n // Trade/occupational license from content.yaml `business.license` (dark when\n // the key is empty/absent — no hasCredential is emitted). Honesty-gated in\n // buildHasCredential downstream.\n const license: string | undefined = contentConfig\n ? findBusinessLicense(contentConfig, route.slug)\n : undefined\n\n // BlogPosting for a blog post route — only when seo.yaml has NOT already\n // hand-authored one (buildHeadTags skips ours if it has). A blog post is a\n // route nested under `/blog/` (depth ≥ 2) with a manifest title.\n const isBlogPost = /^\\/blog\\/.+/.test(route.path) && !!route.title\n const blogMeta: BlogMeta | undefined = isBlogPost\n ? { headline: route.title, url: breadcrumbTrail?.[breadcrumbTrail.length - 1]?.item }\n : undefined\n\n // FAQ from a structured `.dcs/faq.yaml`-style block on the content page, if\n // present (honesty-gated). Component-only FAQ pages carry no such data.\n const faq: FaqSource[] | undefined = pickPageFaq(contentConfig, route.slug)\n\n const tags = buildHeadTags(route.slug, route.path, seoConfig, {\n includeKeywords: true,\n fallbackTitle: route.title,\n robots: forceNoindex ? 'noindex, nofollow' : undefined,\n emitGraph: true,\n breadcrumbTrail,\n reviews,\n license,\n blogMeta,\n faq,\n })\n\n const html = spliceHeadHtml(shellHtml, tags)\n const outFile = routeToOutputFile(outDir, route.path)\n\n fs.mkdirSync(path.dirname(outFile), { recursive: true })\n fs.writeFileSync(outFile, html, 'utf8')\n written++\n\n if (debug) {\n console.log(\n `[dcs-seo] wrote ${path.relative(outDir, outFile)} (title=\"${tags.title}\"${\n forceNoindex ? ', noindex' : ''\n })`\n )\n }\n }\n\n return written\n}\n\n/**\n * Run the body-prerender pass: bring up the headless renderer, fill each\n * indexable route's `<div id=\"app\">` with its real rendered DOM, then tear the\n * renderer down. Missing infra (no playwright / no browser) is a graceful no-op;\n * a render crash/fabrication propagates a `BodyPrerenderError` to fail the build.\n */\nasync function runBodyPrerender(\n ctx: { outDir: string; routes: PageRouteEntry[]; excludedGlobs: string[] },\n opts: { exclude: string[]; noindex: string[]; debug: boolean }\n): Promise<void> {\n const handle = await createPlaywrightRenderer({ outDir: ctx.outDir, debug: opts.debug })\n if (!handle) return // graceful: playwright/browser unavailable\n\n console.log('[dcs-seo] body-prerender: rendering routes via headless Chromium…')\n try {\n const result = await prerenderBodies({\n outDir: ctx.outDir,\n routes: ctx.routes,\n renderer: handle.renderer,\n exclude: opts.exclude,\n noindex: opts.noindex,\n excludedGlobs: ctx.excludedGlobs,\n debug: opts.debug,\n })\n console.log(\n `[dcs-seo] body-prerender: ${result.prerendered} route(s) now have ` +\n `crawler-visible body content (${result.skipped} skipped)`\n )\n } finally {\n await handle.close()\n }\n}\n\n/**\n * Vite plugin that injects .dcs/seo.yaml at build time and, optionally, emits\n * per-route static `<head>` HTML for SPA SEO.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n */\nexport function dcsSeoPlugin(options: DcsSeoPluginOptions = {}): Plugin {\n const {\n seoPath = '.dcs/seo.yaml',\n debug = false,\n emitStaticHtml = false,\n prerenderBody,\n pagesPath = '.dcs/pages.yaml',\n contentPath = '.dcs/content.yaml',\n exclude = [],\n noindex = [],\n // Site files default ON whenever per-route HTML emission is on, so the\n // sites already using emitStaticHtml gain sitemap/robots/llms with no edit.\n emitSiteFiles: emitSiteFilesOpt = emitStaticHtml,\n siteUrl,\n robots = {},\n // Self-detect preview builds from DCS_PREVIEW (mirrors dcsEditorPlugin) so a\n // release/** SWA preview build never body-prerenders; an explicit option wins.\n preview = process.env.DCS_PREVIEW === 'true',\n llms = true,\n } = options\n\n let resolvedConfig: ResolvedConfig\n // Captured during configResolved — gates the serve-only inline HTML inject.\n let isDev = false\n\n return {\n name: 'dcs-seo',\n\n configResolved(config) {\n resolvedConfig = config\n isDev = config.command === 'serve'\n },\n\n config(config, env) {\n // ── Dev / preview (serve) ──────────────────────────────────────────────\n // Don't bake seo.yaml into the bundle. Redirect the `__DCS_SEO__` define\n // to a browser global that `transformIndexHtml` (below) refreshes from\n // disk on every HTML request — live preview with NO server.restart().\n // The consumer is untouched: it still reads the bare `__DCS_SEO__`\n // identifier, which this define rewrites to `globalThis.__DCS_SEO__`.\n // (VitePress treats `useSEO` as a no-op and bakes SEO via\n // transformPageData, so this only affects the runtime `useSEO` SPA path.)\n if (env.command === 'serve') {\n return {\n define: {\n __DCS_SEO__: 'globalThis.__DCS_SEO__',\n },\n }\n }\n\n // ── Production build ───────────────────────────────────────────────────\n // Unchanged: read seo.yaml now and bake it into the bundle as a literal.\n const projectRoot = config.root || process.cwd()\n\n const loaded = loadSeoConfig(projectRoot, seoPath, debug)\n if (!loaded) {\n return {\n define: {\n __DCS_SEO__: 'undefined',\n },\n }\n }\n\n if (debug) {\n console.log(`[dcs-seo] Loaded ${loaded.foundPath}`)\n console.log(`[dcs-seo] Version: ${loaded.config.version}`)\n console.log(`[dcs-seo] Site Name: ${loaded.config.global?.siteName || '(not set)'}`)\n console.log(\n `[dcs-seo] Pages: ${Object.keys(loaded.config.pages ?? {}).join(', ') || '(none)'}`\n )\n }\n\n return {\n define: {\n __DCS_SEO__: JSON.stringify(loaded.config),\n },\n }\n },\n\n /**\n * Build-time static `<head>` emitter (opt-in via `emitStaticHtml`).\n *\n * Runs after the bundle is written so the built `index.html` shell exists\n * on disk. For an SPA, Vite emits a single `index.html`; we fan it out to\n * per-route files with route-specific `<head>` content.\n *\n * Fully defensive: any missing/unparseable input logs a warning and\n * no-ops. Never throws (so it can never break a production build).\n */\n async writeBundle() {\n if (!emitStaticHtml) {\n // The whole emit path is gated on emitStaticHtml. If the operator\n // explicitly asked for site files (`emitSiteFiles: true`) while\n // emitStaticHtml is off, that request is silently inert — warn so it's\n // not mistaken for \"robots/sitemap were emitted\".\n if (options.emitSiteFiles === true) {\n console.warn(\n '[dcs-seo] emitSiteFiles:true is ignored because emitStaticHtml is false; ' +\n 'set emitStaticHtml:true to emit sitemap.xml/robots.txt/llms.txt'\n )\n }\n return\n }\n\n // Captured inside the never-break emit below, consumed by the separately\n // guarded body-prerender step after it (so a fabrication/crash there can\n // fail the build loud instead of being swallowed by the emit try/catch).\n let bodyPrerenderCtx:\n | { outDir: string; routes: PageRouteEntry[]; excludedGlobs: string[] }\n | null = null\n\n try {\n const projectRoot = resolvedConfig?.root || process.cwd()\n const outDir = resolveOutDir(resolvedConfig)\n\n if (!fs.existsSync(outDir)) {\n console.warn(`[dcs-seo] emitStaticHtml: outDir not found (${outDir}); skipping`)\n return\n }\n\n const shellPath = path.join(outDir, 'index.html')\n if (!fs.existsSync(shellPath)) {\n console.warn(\n `[dcs-seo] emitStaticHtml: no index.html in ${outDir}; nothing to emit`\n )\n return\n }\n\n const routes = loadPagesManifest(projectRoot, pagesPath, debug)\n if (!routes) {\n console.warn(\n `[dcs-seo] emitStaticHtml: ${pagesPath} missing or empty; skipping per-route emission`\n )\n return\n }\n\n const loaded = loadSeoConfig(projectRoot, seoPath, debug)\n if (!loaded) {\n // seo.yaml is optional for emission — routes still get baked with\n // global/runtime defaults (which here means just the shell title and\n // a derived canonical-less head). Warn so the operator knows.\n console.warn(\n `[dcs-seo] emitStaticHtml: ${seoPath} missing or unparseable; emitting with defaults only`\n )\n }\n\n const shellHtml = fs.readFileSync(shellPath, 'utf8')\n\n // pages.yaml top-level `excluded:` globs gate BOTH the per-route HTML\n // emission below and the sitemap/llms set inside emitSiteFiles, so an\n // excluded route is never written as a crawlable index.html.\n const excludedGlobs = loadExcludedGlobs(projectRoot, pagesPath)\n // Optional: load content.yaml so REAL reviews can feed the honest\n // Review/aggregateRating on the per-route LocalBusiness node. Absent or\n // unparseable ⇒ undefined ⇒ no reviews emitted (no synthesis).\n const contentConfig = loadContentConfig(projectRoot, contentPath, debug)\n\n const written = emitStaticSeoHtml({\n outDir,\n shellHtml,\n routes,\n seoConfig: loaded?.config,\n contentConfig,\n exclude,\n noindex,\n excludedGlobs,\n debug,\n })\n\n if (debug) {\n console.log(`[dcs-seo] emitStaticHtml: wrote ${written} per-route HTML file(s)`)\n }\n\n // ── site-wide static files (sitemap.xml, robots.txt, llms.txt) ──────\n // Emitted AFTER the per-route HTML loop so canonicals/routes are\n // resolved and the shell exists. Reuses the already-loaded routes +\n // seoConfig (no second manifest read). Same outDir/existsSync guard +\n // the outer try/catch — so it can never break a production build.\n if (emitSiteFilesOpt) {\n const siteFiles = emitSiteFiles({\n outDir,\n projectRoot,\n pagesPath,\n routes,\n seoConfig: loaded?.config,\n siteUrl,\n exclude,\n noindex,\n preview,\n robots,\n llms,\n debug,\n })\n if (debug) {\n console.log(\n `[dcs-seo] emitSiteFiles: wrote ${siteFiles.length} site file(s): ${\n siteFiles.join(', ') || '(none)'\n }`\n )\n }\n }\n\n // Body prerender runs only when enabled for this build (default ON when\n // emitting; off via the `prerenderBody` option, the seo.yaml escape\n // hatch, or preview mode). Stash what it needs so it executes under its\n // own fail-loud guard below, NOT this swallow-catch.\n // Extra guard: the prerender server serves the built assets from root,\n // so a non-root `base` (subpath preview deploys like /kept/) would 404\n // every asset → the app never mounts → a spurious build failure. Skip\n // (loudly) rather than fail — head + JSON-LD are already emitted.\n const buildBase = resolvedConfig?.base ?? '/'\n const baseIsRoot = buildBase === '/' || buildBase === './' || buildBase === ''\n if (\n isBodyPrerenderEnabled({\n emitStaticHtml,\n prerenderBodyOption: prerenderBody,\n preview,\n seoPrerenderBody: loaded?.config?.prerenderBody,\n })\n ) {\n if (baseIsRoot) {\n bodyPrerenderCtx = { outDir, routes, excludedGlobs }\n } else {\n console.warn(\n `[dcs-seo] body prerender skipped: non-root base \"${buildBase}\" ` +\n '(assets would not resolve through the prerender server; head + JSON-LD still emitted)',\n )\n }\n }\n } catch (error) {\n // Never break the build on SEO emission failure.\n console.warn('[dcs-seo] emitStaticHtml failed; build output left unchanged:', error)\n }\n\n // ── Body prerender (SEPARATE guard — fail loud on fabrication/crash) ──────\n // Deliberately OUTSIDE the swallow-catch above: missing infra (no\n // playwright / no browser) degrades gracefully to a no-op, but a route that\n // crashes or would ship broken content throws BodyPrerenderError and FAILS\n // the build. Head + JSON-LD are already on disk regardless.\n if (bodyPrerenderCtx) {\n await runBodyPrerender(bodyPrerenderCtx, { exclude, noindex, debug })\n }\n },\n\n /**\n * Dev-only: inject the FRESH on-disk `seo.yaml` as an inline\n * `globalThis.__DCS_SEO__ = {…}` classic script at the top of `<head>`,\n * mirroring dcsContentPlugin. Lets the runtime `useSEO` SPA path reflect\n * on-disk SEO edits on the portal's `?_hc` hard reload with no restart.\n * Gated on `isDev` so it never runs in `vite`/`vitepress build` (the build\n * path keeps the baked `define` literal + the `writeBundle` emitter).\n */\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n if (!isDev) return html\n\n const projectRoot = resolvedConfig?.root || process.cwd()\n const config = readDcsYamlFresh<SeoConfiguration>(projectRoot, seoPath, 'dcs-seo', debug)\n // No file (or unparseable) ⇒ leave the global unset; the consumer's\n // `typeof globalThis.__DCS_SEO__` check falls back to defaults.\n if (config === undefined) return html\n\n if (debug) {\n console.log('[dcs-seo] serve: injecting fresh __DCS_SEO__ into <head>')\n }\n\n const tags: HtmlTagDescriptor[] = [\n {\n tag: 'script',\n children: buildGlobalAssignScript('__DCS_SEO__', config),\n injectTo: 'head-prepend',\n },\n ]\n return { html, tags }\n },\n },\n\n // Watch for changes in development.\n //\n // Downgraded from `server.restart()` to a debounced client `full-reload`:\n // freshness already comes from `transformIndexHtml` re-reading the YAML on\n // every HTML request, so all we need on change is a browser reload. A\n // `server.restart()` would rebuild VitePress's dev MiniSearch index and can\n // throw \"server restart failed\" on a duplicate heading id — the fragility\n // this fix removes. A `full-reload` does neither.\n configureServer(server) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n\n const watchPaths = [\n path.resolve(projectRoot, seoPath),\n path.resolve(projectRoot, '..', seoPath),\n ].map((p) => path.normalize(p))\n\n watchPaths.forEach((watchPath) => {\n if (fs.existsSync(watchPath)) {\n server.watcher.add(watchPath)\n }\n })\n\n let reloadTimer: ReturnType<typeof setTimeout> | undefined\n server.watcher.on('change', (changedPath) => {\n if (!watchPaths.includes(path.normalize(changedPath))) return\n if (reloadTimer) clearTimeout(reloadTimer)\n reloadTimer = setTimeout(() => {\n if (debug) {\n console.log('[dcs-seo] seo.yaml changed, sending full-reload')\n }\n server.ws.send({ type: 'full-reload' })\n }, 100)\n })\n },\n }\n}\n","/**\n * DCS Editor Plugin for Vite\n *\n * Injects the editor bridge script into customer sites when running\n * in portal preview mode (inside the visual editor iframe).\n *\n * The bridge enables:\n * - Inline text editing via contenteditable\n * - Section hover highlights and AI ✨ buttons\n * - postMessage communication with the portal\n *\n * This plugin should only be active in development/preview mode.\n * It's safe to include in production builds — it does nothing unless\n * the page detects it's inside an iframe.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { dcsEditorPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsEditorPlugin()\n * ]\n * })\n * ```\n */\n\nimport type { Plugin } from 'vite'\n\nexport interface DcsEditorPluginOptions {\n /** Enable debug logging */\n debug?: boolean\n}\n\n/**\n * Vite plugin that injects the editor bridge script for portal preview integration.\n *\n * The bridge auto-initializes only when the page detects it's running inside an iframe,\n * so it's safe to include in all builds — it's a no-op in standalone browsing.\n *\n * Uses a virtual module (`/__dcs-editor-bridge.js`) served through Vite's dev server\n * so that bare module specifiers (like `@duffcloudservices/cms/editor`) are properly\n * resolved through Vite's module graph rather than hitting the browser's native ESM\n * resolver, which can't handle bare specifiers.\n */\nexport function dcsEditorPlugin(options: DcsEditorPluginOptions = {}): Plugin {\n const { debug = false } = options\n const VIRTUAL_PATH = '/__dcs-editor-bridge.js'\n const RIBBON_VIRTUAL_PATH = '/__dcs-preview-ribbon.js'\n\n // Captured during configResolved — used to gate HTML injection.\n let isDev = false\n let basePath = '/'\n let isPreview = false\n\n return {\n name: 'dcs-editor-bridge',\n\n // Preview only: stop Vite running an unreachable HMR WebSocket server.\n // Freshness comes from the cms content/seo plugins (fresh transformIndexHtml\n // re-read) + the portal's ?_hc hard reload, not HMR. NOTE: this alone does\n // NOT silence the browser — Vite still injects @vite/client and calls\n // connect(); the client-side stub in transformIndexHtml is what removes the\n // console errors. A normal local `vite dev` (no DCS_PREVIEW) keeps full HMR.\n config(_config, env) {\n if (env.command === 'serve' && process.env.DCS_PREVIEW === 'true') {\n return { server: { hmr: false } }\n }\n },\n\n configResolved(config) {\n isDev = config.command === 'serve'\n basePath = config.base ?? '/'\n isPreview = process.env.DCS_PREVIEW === 'true'\n },\n\n resolveId(id) {\n if (id === VIRTUAL_PATH || id === RIBBON_VIRTUAL_PATH) {\n return id\n }\n },\n\n load(id) {\n if (id === VIRTUAL_PATH) {\n return `\nimport { initEditorBridge } from '@duffcloudservices/cms/editor'\n// The bridge only activates inside an iframe (portal preview)\nif (window.parent !== window) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => setTimeout(initEditorBridge, 200))\n } else {\n setTimeout(initEditorBridge, 200)\n }\n}\n`\n }\n\n if (id === RIBBON_VIRTUAL_PATH) {\n return `\nimport { createApp } from 'vue'\nimport PreviewRibbon from '@duffcloudservices/cms/components'\n\nfunction mountRibbon() {\n // Create a detached mount point\n const el = document.createElement('div')\n el.id = 'dcs-preview-ribbon-root'\n document.body.appendChild(el)\n\n const app = createApp(PreviewRibbon)\n app.mount(el)\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', mountRibbon)\n} else {\n mountRibbon()\n}\n`\n }\n },\n\n /**\n * Only inject the editor bridge and ribbon scripts when:\n * 1. Running in dev mode (Vite dev server), AND\n * 2. The app is served at the root path (base === '/')\n *\n * These are virtual modules resolved via absolute paths (e.g.\n * `/__dcs-editor-bridge.js`). When base !== '/', the app is served at a\n * sub-path (e.g. /handyman-bryan/) and the absolute virtual paths are NOT\n * routed to the Vite dev server by the reverse proxy — they hit the main\n * DCS server instead and get redirected to portal.duffcloudservices.com\n * (no CORS headers), causing browser console errors.\n *\n * In production builds, App.vue imports and mounts PreviewRibbon\n * directly, so virtual script injection is always redundant anyway.\n */\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n if (!isDev || basePath !== '/') return html\n\n if (debug) {\n console.log('[dcs-editor] Injecting editor bridge + preview ribbon script tags')\n }\n\n // Inject both the editor bridge and preview ribbon scripts\n const editorTag = `<script type=\"module\" src=\"${VIRTUAL_PATH}\"></script>`\n const ribbonTag = `<script type=\"module\" src=\"${RIBBON_VIRTUAL_PATH}\"></script>`\n const bodyHtml = html.replace('</body>', `${editorTag}\\n${ribbonTag}\\n</body>`)\n\n if (!isPreview) return bodyHtml\n\n // Preview only: neutralise Vite's HMR WebSocket. Vite always injects\n // @vite/client and unconditionally calls transport.connect(); behind the\n // preview proxy chain there is no reachable HMR WS, so it logs\n // WebSocket connection to 'wss://<portal>/?token=…' failed\n // WebSocket connection to 'wss://localhost:<port>/?token=…' failed\n // [vite] failed to connect to websocket\n // server.hmr:false does NOT stop this (the client is still injected). A\n // classic <script> in <head> runs during parse, BEFORE the deferred\n // @vite/client module, so returning an inert socket for the 'vite-hmr'\n // subprotocol silences the attempt with zero effect on app/runtime\n // WebSockets. No-op outside preview / in production builds.\n const wsStub =\n `(function(){try{var N=window.WebSocket;if(!N)return;` +\n `function hmr(p){return p==='vite-hmr'||(Array.isArray(p)&&p.indexOf('vite-hmr')!==-1)}` +\n `function Inert(){this.readyState=0}` +\n `Inert.prototype.send=function(){};Inert.prototype.close=function(){};` +\n `Inert.prototype.addEventListener=function(){};Inert.prototype.removeEventListener=function(){};` +\n `var W=function(u,p){if(hmr(p))return new Inert();return new N(u,p)};` +\n `W.prototype=N.prototype;W.CONNECTING=N.CONNECTING;W.OPEN=N.OPEN;W.CLOSING=N.CLOSING;W.CLOSED=N.CLOSED;` +\n `window.WebSocket=W}catch(e){}})()`\n\n return {\n html: bodyHtml,\n tags: [{ tag: 'script', children: wsStub, injectTo: 'head-prepend' }],\n }\n },\n },\n }\n}\n","/**\n * DCS Preview Plugin for Vue\n *\n * Registers a supplied ribbon component as a global `DcsPreviewRibbon`\n * component. The ribbon handles its own visibility — it only renders on\n * `preview.duffcloudservices.com` and hides everywhere else (localhost,\n * production domains, and inside the visual page editor iframe).\n *\n * Why does the caller pass the component in? Because this file is compiled\n * by tsup (esbuild) which has no `.vue` SFC loader. Keeping the raw `.vue`\n * import out of the compiled plugins bundle avoids the build error while\n * still letting consumer code (which *does* run through Vite) resolve the\n * SFC at dev/build time.\n *\n * @example VitePress theme/index.ts\n * ```typescript\n * import { dcsPreviewPlugin } from '@duffcloudservices/cms/plugins'\n * import PreviewRibbon from '@duffcloudservices/cms/components'\n *\n * export default {\n * Layout,\n * enhanceApp({ app }) {\n * app.use(dcsPreviewPlugin(PreviewRibbon))\n * }\n * }\n * ```\n */\n\nimport { defineComponent, h, type App, type Component, type Plugin } from 'vue'\n\nexport interface DcsPreviewPluginOptions {\n /**\n * Override the version string displayed on the ribbon.\n * If omitted, the ribbon auto-detects from VITE_SITE_VERSION or the API.\n */\n version?: string | null\n}\n\n/**\n * Creates and returns the DCS Preview plugin.\n *\n * When installed, it registers the supplied ribbon component as a global\n * `DcsPreviewRibbon` component. Add `<DcsPreviewRibbon />` to your root\n * Layout, or use the `dcsEditorPlugin` Vite plugin which injects it via\n * `transformIndexHtml`.\n *\n * @param ribbonComponent - The PreviewRibbon SFC (imported by the consumer)\n * @param options - Optional configuration\n */\nexport function dcsPreviewPlugin(\n ribbonComponent: Component,\n options: DcsPreviewPluginOptions = {},\n): Plugin {\n const Wrapper = defineComponent({\n name: 'DcsPreviewRibbon',\n setup() {\n return () => h(ribbonComponent, { version: options.version ?? null })\n },\n })\n\n return {\n install(app: App) {\n app.component('DcsPreviewRibbon', Wrapper)\n\n if (options.version !== undefined) {\n app.provide('__dcs_preview_version__', options.version)\n }\n },\n }\n}\n","/**\n * DCS CDN Image Plugin for Vite\n *\n * Rewrites local static image references (e.g. `/images/staff/photo.jpg`)\n * to CDN URLs at build time using the `.dcs/cdn-image-map.json` mapping file\n * generated by the `image-migrate adopt` CLI command.\n *\n * For raster images with WebP variants, `<img>` elements are transformed into\n * responsive `<picture>` elements with `srcset` for optimised delivery.\n * SVGs receive a simple URL swap with no variant handling.\n *\n * **In development mode this plugin is a no-op** — local `/images/` paths\n * continue to work via Vite's static asset serving so hot-reload is unaffected.\n *\n * The plugin handles two in-pipeline replacement vectors:\n *\n * 1. **Module transform** (`transform` hook) — rewrites `<img>` tags and\n * string literals in Vue SFCs, TS, JS, CSS, MD, and HTML modules.\n * 2. **Chunk rendering** (`renderChunk` hook) — rewrites image paths in\n * final rendered JS/CSS chunks AFTER Vite's `define` substitution.\n * This catches data injected by `dcsContentPlugin` via `__DCS_CONTENT__`\n * (from `.dcs/content.yaml`) which bypasses the `transform` hook.\n *\n * A third vector — **post-build file processing** — is handled by the\n * companion `dcsCdnBuildEnd` hook, which must be registered separately in\n * VitePress config. VitePress generates HTML *after* both Vite builds\n * complete, so Vite plugin hooks (`closeBundle`, `transformIndexHtml`)\n * cannot catch SSR-rendered `<head>` tags or static `public/` files like\n * `service-worker.js`.\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { dcsCdnImagePlugin, dcsCdnBuildEnd } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * vite: {\n * plugins: [\n * dcsCdnImagePlugin()\n * ]\n * },\n * buildEnd: dcsCdnBuildEnd()\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport type { Plugin } from 'vite'\n\n// ────────────────────────────────────────────────────────────────────────────\n// Types\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface CdnImageMapVariant {\n suffix: string\n blobPath: string\n cdnUrl: string\n width: number\n height: number\n size: number\n}\n\nexport interface CdnImageMapEntry {\n localPath: string\n blobPath: string\n cdnUrl: string\n fileName: string\n contentType: string\n originalSize: number\n width?: number\n height?: number\n hash: string\n variants?: CdnImageMapVariant[]\n}\n\nexport interface CdnImageMap {\n generated: string\n site: string\n cdnBase: string\n images: CdnImageMapEntry[]\n}\n\nexport interface DcsCdnImagePluginOptions {\n /** Path to cdn-image-map.json relative to project root (default: '.dcs/cdn-image-map.json') */\n mapPath?: string\n\n /**\n * Patterns to match for replacement. Each must be a **leading-slash path prefix**\n * that appears in source code (e.g. `/images/`). The `localPath` field in the\n * mapping file is compared *without* a leading slash.\n *\n * Default: `['/images/']`\n */\n pathPrefixes?: string[]\n\n /**\n * Default `sizes` attribute for responsive `<picture>` elements.\n * Override per-context via the `data-sizes` attribute on the original `<img>`.\n *\n * Default: `'(max-width: 1024px) 100vw, 1024px'`\n */\n defaultSizes?: string\n\n /** Enable debug logging */\n debug?: boolean\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ────────────────────────────────────────────────────────────────────────────\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\n/**\n * Build a responsive `<picture>` element for a raster image that has\n * WebP variants.\n */\nfunction buildPictureElement(\n entry: CdnImageMapEntry,\n alt: string,\n extraAttrs: string,\n sizes: string\n): string {\n const variants = entry.variants ?? []\n\n // Collect only the display-relevant variants (not thumb, not og)\n const displayVariants = variants.filter(\n (v) => v.suffix === '-sm' || v.suffix === '-md' || v.suffix === '-lg'\n )\n\n if (displayVariants.length === 0) {\n // No display variants — fall back to original CDN URL as plain <img>\n return `<img src=\"${escapeHtml(entry.cdnUrl)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs} loading=\"lazy\" decoding=\"async\" />`\n }\n\n const srcset = displayVariants\n .map((v) => `${v.cdnUrl} ${v.width}w`)\n .join(', ')\n\n // Pick the -md variant as the default src, or fall through to original\n const mdVariant = variants.find((v) => v.suffix === '-md')\n const fallbackSrc = mdVariant ? mdVariant.cdnUrl : entry.cdnUrl\n\n return [\n '<picture>',\n ` <source srcset=\"${srcset}\" type=\"image/webp\" sizes=\"${escapeHtml(sizes)}\" />`,\n ` <img src=\"${escapeHtml(fallbackSrc)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs} loading=\"lazy\" decoding=\"async\" />`,\n '</picture>',\n ].join('\\n')\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Shared helpers\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Try to find and load the CDN image map from common locations.\n * VitePress nests docs under a subfolder, so we check parent dirs too.\n */\nfunction loadCdnImageMap(\n projectRoot: string,\n relativeMapPath: string,\n debug: boolean\n): Map<string, CdnImageMapEntry> | null {\n const possiblePaths = [\n path.resolve(projectRoot, relativeMapPath),\n path.resolve(projectRoot, '..', relativeMapPath),\n path.resolve(process.cwd(), relativeMapPath),\n ]\n\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n try {\n const raw = fs.readFileSync(testPath, 'utf8')\n const data: CdnImageMap = JSON.parse(raw)\n\n const map = new Map<string, CdnImageMapEntry>()\n for (const entry of data.images) {\n // Store both with and without leading slash for flexible matching\n map.set(entry.localPath, entry)\n if (!entry.localPath.startsWith('/')) {\n map.set('/' + entry.localPath, entry)\n }\n }\n\n if (debug) {\n console.log(`[dcs-cdn-image] Loaded ${data.images.length} entries from ${testPath}`)\n }\n return map\n } catch (error) {\n console.warn(`[dcs-cdn-image] Failed to parse ${testPath}:`, error)\n }\n }\n }\n\n if (debug) {\n console.log('[dcs-cdn-image] No cdn-image-map.json found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n }\n return null\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Vite Plugin\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Vite plugin that rewrites static `/images/` paths to CDN URLs at build time.\n */\nexport function dcsCdnImagePlugin(options: DcsCdnImagePluginOptions = {}): Plugin {\n const {\n mapPath = '.dcs/cdn-image-map.json',\n pathPrefixes = ['/images/'],\n defaultSizes = '(max-width: 1024px) 100vw, 1024px',\n debug = false,\n } = options\n\n // Lookup map built from the JSON file: key = localPath (no leading slash)\n let imageMap: Map<string, CdnImageMapEntry> | null = null\n let isProduction = false\n\n /**\n * Check whether a source code string contains any of the path prefixes\n * we care about. Quick bail-out for files that don't reference images.\n */\n function hasImageReferences(code: string): boolean {\n return pathPrefixes.some((prefix) => code.includes(prefix))\n }\n\n /**\n * Replace image path references in any string value with CDN URLs.\n * Used for code transforms and renderChunk post-processing.\n */\n function replaceImagePaths(str: string): string {\n if (!imageMap || imageMap.size === 0) return str\n let result = str\n for (const prefix of pathPrefixes) {\n // Global replace: matches prefix followed by a relative path bounded\n // by quotes, parens, or whitespace. Allows spaces in filenames.\n const regex = new RegExp(\n `([\"'\\`(])${escapeRegex(prefix)}([^\"'\\`()]+)`,\n 'g'\n )\n result = result.replace(regex, (match, quote, relPath) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n return `${quote}${entry.cdnUrl}`\n })\n }\n return result\n }\n\n /**\n * Replace standalone URL references in JS/TS string literals and Vue template\n * attributes. This handles patterns like:\n * src=\"/images/foo.jpg\"\n * { src: '/images/foo.jpg' }\n * url(/images/foo.svg)\n *\n * For `<img>` tags with raster images that have variants, the entire tag is\n * replaced with a `<picture>` element.\n */\n function transformCode(code: string): string {\n if (!imageMap || imageMap.size === 0) return code\n\n // ── Pass 1: Replace <img> tags that reference mapped raster images ──────\n // Matches: <img ... src=\"/images/foo.jpg\" ... /> or <img ... src=\"/images/foo.jpg\" ... >\n const imgTagRegex = /<img\\b([^>]*?)src=[\"'](\\/(images\\/[^\"']+))[\"']([^>]*?)\\/?>/gi\n code = code.replace(imgTagRegex, (_match, beforeSrc, _srcWithSlash, localPath, afterSrc) => {\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return _match\n\n // Extract alt from the existing attributes\n const altMatch = (beforeSrc + afterSrc).match(/alt=[\"']([^\"']*)[\"']/)\n const alt = altMatch ? altMatch[1] : ''\n\n // Collect other attributes (not src, alt, loading, decoding — we set those)\n const otherAttrs = (beforeSrc + afterSrc)\n .replace(/\\balt=[\"'][^\"']*[\"']/gi, '')\n .replace(/\\bloading=[\"'][^\"']*[\"']/gi, '')\n .replace(/\\bdecoding=[\"'][^\"']*[\"']/gi, '')\n .trim()\n\n const extraAttrs = otherAttrs ? ' ' + otherAttrs : ''\n\n // Extract custom sizes if specified\n const sizesMatch = (beforeSrc + afterSrc).match(/data-sizes=[\"']([^\"']*)[\"']/)\n const sizes = sizesMatch ? sizesMatch[1] : defaultSizes\n\n // SVGs and images without variants → simple URL swap <img>\n if (!entry.variants || entry.variants.length === 0) {\n return `<img src=\"${escapeHtml(entry.cdnUrl)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs} loading=\"lazy\" decoding=\"async\" />`\n }\n\n // Raster with variants → <picture>\n return buildPictureElement(entry, alt, extraAttrs, sizes)\n })\n\n // ── Pass 2: Replace remaining string literal / CSS url() references ─────\n code = replaceImagePaths(code)\n\n return code\n }\n\n return {\n name: 'dcs-cdn-image',\n enforce: 'pre',\n\n configResolved(config) {\n isProduction = config.command === 'build'\n if (!isProduction) {\n if (debug) {\n console.log('[dcs-cdn-image] Dev mode — plugin disabled (local images served by Vite)')\n }\n return\n }\n imageMap = loadCdnImageMap(config.root, mapPath, debug)\n },\n\n transform(code, id) {\n // Only run in production builds\n if (!isProduction || !imageMap) return\n\n // Only process relevant file types\n if (!/\\.(vue|ts|tsx|js|jsx|css|scss|md|html)(\\?.*)?$/.test(id)) return\n\n // Quick bail — skip files with no image path references\n if (!hasImageReferences(code)) return\n\n const transformed = transformCode(code)\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n\n /**\n * Replace image paths in final rendered JS/CSS chunks.\n *\n * This fires AFTER Vite's `define` substitution, catching paths that were\n * injected via `define` values (e.g. `__DCS_CONTENT__` set by\n * `dcsContentPlugin` from `.dcs/content.yaml`). Those paths bypass the\n * `transform` hook because `define` replacement happens at bundle time.\n */\n renderChunk(code, _chunk) {\n if (!isProduction || !imageMap) return null\n if (!hasImageReferences(code)) return null\n\n const replaced = replaceImagePaths(code)\n if (replaced !== code) {\n if (debug) {\n console.log(`[dcs-cdn-image] renderChunk: rewrote image paths in ${_chunk.fileName}`)\n }\n return { code: replaced, map: null }\n }\n return null\n },\n\n /**\n * Transform HTML output to catch references injected outside the module\n * pipeline — e.g. VitePress `head` config tags (favicons, OG images).\n *\n * Note: In VitePress, this hook fires for the `index.html` template but\n * NOT for SSR-rendered per-page HTML. Use `dcsCdnBuildEnd` for full\n * coverage of generated HTML files.\n */\n transformIndexHtml(html) {\n if (!isProduction || !imageMap) return html\n if (!hasImageReferences(html)) return html\n\n // Replace href/src/content attributes that reference mapped images\n for (const prefix of pathPrefixes) {\n const attrRegex = new RegExp(\n `((?:href|src|content)=[\"'])${escapeRegex(prefix)}([^\"']+)([\"'])`,\n 'gi'\n )\n html = html.replace(attrRegex, (match, pre, relPath, post) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n return `${pre}${entry.cdnUrl}${post}`\n })\n }\n\n return html\n },\n }\n}\n\n/**\n * Escape a string for use in a RegExp.\n */\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// VitePress buildEnd hook\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface DcsCdnBuildEndOptions {\n /** Path to cdn-image-map.json relative to project root (default: '.dcs/cdn-image-map.json') */\n mapPath?: string\n\n /**\n * Patterns to match for replacement.\n * Default: `['/images/']`\n */\n pathPrefixes?: string[]\n\n /** Enable debug logging */\n debug?: boolean\n\n /**\n * File extensions to process in the output directory.\n * Default: `['.html', '.js']`\n */\n extensions?: string[]\n}\n\n/**\n * VitePress `buildEnd` hook factory that post-processes generated HTML and\n * static files in the output directory to replace remaining `/images/` paths\n * with CDN URLs.\n *\n * VitePress generates HTML **after** both Vite builds complete, so Vite\n * plugin hooks (`closeBundle`, `transformIndexHtml`) cannot catch\n * SSR-rendered `<head>` tags (favicons, OG images). This hook runs after all\n * HTML files are written to disk.\n *\n * Also rewrites static files (e.g. `service-worker.js`) copied from\n * `public/` that are not part of Vite's module pipeline.\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { dcsCdnBuildEnd } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * buildEnd: dcsCdnBuildEnd({ debug: true })\n * })\n * ```\n */\nexport function dcsCdnBuildEnd(options: DcsCdnBuildEndOptions = {}) {\n const {\n mapPath = '.dcs/cdn-image-map.json',\n pathPrefixes = ['/images/'],\n debug = false,\n extensions = ['.html', '.js'],\n } = options\n\n return async (siteConfig: { root: string; outDir: string }) => {\n const imageMap = loadCdnImageMap(siteConfig.root, mapPath, debug)\n if (!imageMap) {\n if (debug) {\n console.log('[dcs-cdn-image] buildEnd: no image map found')\n }\n return\n }\n\n const outDir = siteConfig.outDir\n if (!fs.existsSync(outDir)) return\n\n let filesProcessed = 0\n let refsReplaced = 0\n\n function walkDir(dir: string) {\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n walkDir(fullPath)\n } else if (extensions.some((ext) => entry.name.endsWith(ext))) {\n processFile(fullPath)\n }\n }\n }\n\n function processFile(filePath: string) {\n let content = fs.readFileSync(filePath, 'utf8')\n if (!pathPrefixes.some((p) => content.includes(p))) return\n\n let changed = false\n\n if (filePath.endsWith('.html')) {\n // HTML: replace href/src/content attribute values\n for (const prefix of pathPrefixes) {\n const attrRegex = new RegExp(\n `((?:href|src|content)=[\"'])${escapeRegex(prefix)}([^\"']+)([\"'])`,\n 'gi'\n )\n content = content.replace(attrRegex, (match, pre, relPath, post) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const mapEntry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!mapEntry) return match\n changed = true\n refsReplaced++\n return `${pre}${mapEntry.cdnUrl}${post}`\n })\n }\n } else {\n // JS/other: replace quoted string paths\n for (const prefix of pathPrefixes) {\n const regex = new RegExp(\n `([\"'\\`(])${escapeRegex(prefix)}([^\"'\\`()]+)`,\n 'g'\n )\n content = content.replace(regex, (match, quote, relPath) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n changed = true\n refsReplaced++\n return `${quote}${entry.cdnUrl}`\n })\n }\n }\n\n if (changed) {\n fs.writeFileSync(filePath, content, 'utf8')\n filesProcessed++\n }\n }\n\n walkDir(outDir)\n\n if (debug) {\n console.log(\n `[dcs-cdn-image] buildEnd: post-processed ${filesProcessed} files, replaced ${refsReplaced} references`\n )\n }\n }\n}","/**\n * markdown-it plugin that transforms standard `![alt](url)` image syntax\n * into responsive `<picture>` elements when the URL matches the DCS CDN\n * asset pattern.\n *\n * Non-CDN images are rendered with the default image renderer (plain `<img>`).\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { responsiveImagePlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * markdown: {\n * config: (md) => {\n * md.use(responsiveImagePlugin)\n * },\n * },\n * })\n * ```\n *\n * Input markdown:\n * ```md\n * ![Physical therapy session](https://files.duffcloudservices.com/kept/assets/blog/abc-123.jpg)\n * ```\n *\n * Rendered HTML:\n * ```html\n * <picture>\n * <source srcset=\"...abc-123-sm.webp 640w, ...abc-123-md.webp 1024w, ...abc-123-lg.webp 1920w\"\n * type=\"image/webp\"\n * sizes=\"(max-width: 1024px) 100vw, 1024px\" />\n * <img src=\"...abc-123-md.webp\" alt=\"Physical therapy session\" loading=\"lazy\" decoding=\"async\" />\n * </picture>\n * ```\n */\n\nimport type MarkdownIt from 'markdown-it'\n\n/**\n * Matches DCS CDN asset URLs: base path / UUID . extension.\n *\n * Aligned with `@duffcloudservices/cms-core` `responsive-image.ts`: the\n * `(?:content\\/)?` group covers the content-container shape\n * `https://files.duffcloudservices.com/content/{slug}/assets/...` in addition\n * to the branding-container shape `.../{slug}/assets/...`. The group is\n * non-capturing, so the captured base path / UUID / extension are unchanged.\n */\nconst CDN_PATTERN = /^(https?:\\/\\/files\\.[^/]+\\/(?:content\\/)?[^/]+\\/assets\\/(?:[^/]+\\/)*)([a-f0-9-]+)\\.(\\w+)$/\n\n/**\n * Escapes HTML special characters for safe attribute embedding.\n * Falls back to a simple replace chain when `md.utils.escapeHtml` is unavailable.\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\nexport function responsiveImagePlugin(md: MarkdownIt): void {\n const defaultImageRenderer = md.renderer.rules.image\n\n md.renderer.rules.image = (tokens, idx, options, env, self) => {\n const token = tokens[idx]\n const src = token.attrGet('src') ?? ''\n const alt = token.content ?? ''\n\n const match = src.match(CDN_PATTERN)\n if (!match) {\n // Not a CDN image — render normally\n if (defaultImageRenderer) {\n return defaultImageRenderer(tokens, idx, options, env, self)\n }\n return self.renderToken(tokens, idx, options)\n }\n\n const [, basePath, uuid] = match\n\n const srcset = [\n `${basePath}${uuid}-sm.webp 640w`,\n `${basePath}${uuid}-md.webp 1024w`,\n `${basePath}${uuid}-lg.webp 1920w`,\n ].join(', ')\n\n const escapedAlt = escapeHtml(alt)\n\n // Self-heal: this plugin emits a STATIC HTML string at build time (no Vue\n // runtime), so the fallback must be an inline onerror. A fit-variant\n // (`-md`/`-lg`) may legitimately be absent (internal/imaging variants.go\n // skips variants by DIMENSION). On the first error, swap the `<img>` to the\n // ORIGINAL url AND strip the sibling `<source>` elements — a `<picture>`\n // does NOT re-resolve after a `<source>` 404s, so the variant set must be\n // removed for the original to display. (`this.onerror=null` prevents loops.)\n const escapedOriginal = escapeHtml(src)\n const onError =\n `this.onerror=null;this.src='${escapedOriginal}';` +\n `this.closest('picture')?.querySelectorAll('source').forEach(s=>s.remove())`\n\n return [\n '<picture>',\n ` <source srcset=\"${srcset}\" type=\"image/webp\" sizes=\"(max-width: 1024px) 100vw, 1024px\" />`,\n ` <img src=\"${basePath}${uuid}-md.webp\" alt=\"${escapedAlt}\" loading=\"lazy\" decoding=\"async\" onerror=\"${onError}\" />`,\n '</picture>',\n ].join('\\n')\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/plugins/serveDefineInject.ts","../../src/plugins/motionTokens.ts","../../src/plugins/agentWidget.ts","../../src/plugins/dcsContentPlugin.ts","../../src/seo/playwrightRenderer.ts","../../src/plugins/dcsSeoPlugin.ts","../../src/plugins/dcsEditorPlugin.ts","../../src/plugins/dcsPreviewPlugin.ts","../../src/plugins/dcsCdnImagePlugin.ts","../../src/plugins/responsiveImagePlugin.ts","../../src/plugins/dcsResponsiveImageLint.ts"],"names":["path","fs","yaml","h","escapeHtml"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Shared serve-mode helpers for the DCS content + SEO Vite plugins.\n *\n * In a production build the plugins bake the `.dcs/*.yaml` payload into a Vite\n * `define` literal (`define: { __DCS_X__: JSON.stringify(data) }`) so the SSG /\n * SPA bundle inlines it exactly as before.\n *\n * In dev (`vite` / `vitepress dev`, i.e. the portal preview) baking a literal\n * is wrong: the value is frozen at server-start and only a `server.restart()`\n * would refresh it — and that restart is exactly what triggers VitePress's\n * MiniSearch rebuild and the duplicate-heading \"server restart failed\" crash.\n *\n * Instead, in serve mode the plugins:\n * 1. redirect the `__DCS_X__` define to a browser global\n * (`define: { __DCS_X__: 'globalThis.__DCS_X__' }`), and\n * 2. inject an inline classic `<script>` at the top of `<head>` on every HTML\n * request that assigns the FRESH on-disk YAML to that global.\n *\n * Because `transformIndexHtml` re-runs on every dev HTML request, a plain\n * iframe reload (the portal's `?_hc` hard reload) reflects the latest on-disk\n * content with NO `server.restart()` and NO module-graph surgery. The consumer\n * is untouched: it still reads the bare `__DCS_X__` identifier, which the define\n * rewrites to `globalThis.__DCS_X__`.\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\n\n/**\n * The three `.dcs` search locations the plugins have always used. VitePress\n * projects have the `docs/` folder as root, so the parent is checked too.\n */\nexport function dcsSearchPaths(projectRoot: string, relPath: string): string[] {\n return [\n path.resolve(projectRoot, relPath),\n path.resolve(projectRoot, '..', relPath),\n path.resolve(process.cwd(), relPath),\n ]\n}\n\n/** First existing `.dcs` file from the standard search, or `undefined`. */\nexport function resolveDcsFile(projectRoot: string, relPath: string): string | undefined {\n return dcsSearchPaths(projectRoot, relPath).find((p) => fs.existsSync(p))\n}\n\n/**\n * Read + parse a `.dcs` YAML file FRESH from disk on every call (no caching).\n * Returns the parsed value, or `undefined` when the file is missing or\n * unparseable. Never throws — a dev HTML request must never 500 on bad YAML.\n */\nexport function readDcsYamlFresh<T = unknown>(\n projectRoot: string,\n relPath: string,\n label: string,\n debug = false\n): T | undefined {\n const found = resolveDcsFile(projectRoot, relPath)\n if (!found) {\n if (debug) console.log(`[${label}] serve: no ${relPath} found; global left unset`)\n return undefined\n }\n try {\n return yaml.load(fs.readFileSync(found, 'utf8')) as T\n } catch (error) {\n console.warn(`[${label}] serve: failed to parse ${relPath}:`, error)\n return undefined\n }\n}\n\n/**\n * Build a safe inline `<script>` body that assigns a JSON-serialisable value to\n * a browser global. Every `<` is escaped to its `<` unicode form so an\n * embedded `</script>` (or `<!--`) inside the content data can never break out\n * of the inline script.\n */\nexport function buildGlobalAssignScript(globalName: string, value: unknown): string {\n const json = JSON.stringify(value ?? null).replace(/</g, '\\\\u003c')\n return `globalThis.${globalName} = ${json};`\n}\n","import type { HtmlTagDescriptor } from 'vite'\n\n/**\n * DCS motion tokens — the customer-site copy of the shared motion vocabulary.\n *\n * Injected as a constant `<style>` into every DCS site's `<head>` by\n * `dcsContentPlugin` (see its `transformIndexHtml`). This is the fleet-wide\n * seam: every site (vue-spa and VitePress) already wires `dcsContentPlugin`,\n * so a cms version bump ships these tokens to the whole fleet with zero\n * per-site source edits.\n *\n * Source of truth is the platform copy at\n * `packages/dcs-ui/src/styles/motion.css` and the `dcs-product-motion` skill —\n * keep the values here in lockstep. Values ONLY: reduced-motion stays a\n * per-component responsibility so no blanket `!important` rule leaks into a\n * site's isolated `.<slug>-page` design subtree.\n */\nexport const DCS_MOTION_TOKENS_CSS = `:root {\n --motion-duration-stagger: 40ms;\n --motion-duration-micro: 80ms;\n --motion-duration-quick: 150ms;\n --motion-duration-fast: 250ms;\n --motion-duration-medium: 350ms;\n --motion-duration-slow: 400ms;\n --motion-duration-very-slow: 500ms;\n --motion-ease-out: cubic-bezier(0.22, 1, 0.36, 1);\n --motion-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n --motion-ease-linear: linear;\n --motion-ease-bounce: cubic-bezier(0.34, 1.36, 0.64, 1);\n --motion-ease-bounce-strong: cubic-bezier(0.34, 3.85, 0.64, 1);\n --motion-distance-xs: 4px;\n --motion-distance-sm: 8px;\n --motion-distance-md: 12px;\n --motion-distance-lg: 24px;\n --motion-scale-lg: 0.96;\n --motion-scale-md: 0.97;\n --motion-scale-sm: 0.98;\n --motion-scale-xs: 0.99;\n --motion-blur-sm: 2px;\n --motion-blur-md: 3px;\n --motion-blur-lg: 8px;\n}`\n\n/**\n * The `<head>` tag descriptor for the motion-token block.\n *\n * `injectTo: 'head'`, NOT `'head-prepend'` — C-414. Vite's `head-prepend`\n * inserts immediately after the `<head>` open tag, i.e. AHEAD of the site's own\n * `<meta charset>`. This block is 858 bytes, so it pushed the encoding\n * declaration to byte 910 of the HTML spec's 1024-byte encoding-sniffing window\n * on every site in the fleet (measured on Iron Oak and Bryan's 2026-07-27, and\n * re-measured identical here), leaving ~90 bytes of headroom before every DCS\n * site silently mojibakes.\n *\n * `'head'` appends to the END of the `<head>` the site authored — which is\n * still ABOVE Vite's own build-time `<link rel=stylesheet>` / entry `<script>`,\n * because those are injected AFTER this `order: 'pre'` hook's tags. So the\n * per-site `:root` override still wins the cascade (that was the original\n * reason for `head-prepend`) AND the charset lands at byte ~46 with ~954 bytes\n * of headroom. Measured, not assumed — see `verify-honesty-rails.mjs` P12.\n *\n * If you ever move this back to `head-prepend`, the P12 rail in\n * `scripts/verify-honesty-rails.mjs` will fail: it asserts the charset comes\n * FIRST straight out of `dcsContentPlugin`, with no `dcsSeoPlugin` hoist.\n */\nexport function dcsMotionTokensStyleTag(): HtmlTagDescriptor {\n return {\n tag: 'style',\n attrs: { 'data-dcs-motion-tokens': '' },\n children: DCS_MOTION_TOKENS_CSS,\n injectTo: 'head',\n }\n}\n","/**\n * DCS AI-assistant widget loader — the cms head-inject seam for Phase 3 of the\n * Managed Site AI Agents plan (.docs/archive/plans/managed-site-ai-agents/plan.md).\n *\n * This rides the same always-on `dcsContentPlugin.transformIndexHtml` seam that\n * ships the motion tokens: every DCS site already wires `dcsContentPlugin`, so a\n * cms version bump delivers this loader to the whole fleet with ZERO per-site\n * source edits, and the portal per-site toggle turns the widget on/off at\n * runtime with no rebuild.\n *\n * What gets injected is a tiny (<2 KB) inline `<script>` — NOT the widget. On\n * idle it fetches the public agent config for this site; only if the agent is\n * enabled (HTTP 200) does it publish `window.__DCS_AGENT_BOOTSTRAP__` and inject\n * the real widget bundle from the CDN. The loader is **fail-silent**: a disabled\n * agent (404), a dead backend, a blocked request, or a malformed response all\n * result in no mount and no console error, so the assistant can never break a\n * customer page.\n *\n * Build-only: the loader is injected into the production build output (the fleet\n * delivery), not into `vite dev`. In local dev the config fetch would hit the\n * prod API from a non-whitelisted `localhost` origin and log an uncatchable CORS\n * error for every site developer — so, unlike the constant motion-token block,\n * this network-touching loader stays out of the dev server.\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\n\nimport yaml from 'js-yaml'\nimport type { HtmlTagDescriptor } from 'vite'\n\n/** DCS API origin the loader calls (matches the cms runtime composables). */\nexport const DEFAULT_AGENT_API_BASE_URL = 'https://portal.duffcloudservices.com'\n\n/**\n * Versioned CDN URL of the widget bundle. Both `cdn.` (branding CDN, CorsAllowAll)\n * and the already-live `files.` route serve the same `sgdcs` blob origin with an\n * identical `/agent-widget/v1/...` layout, so this is a plain classic-script\n * include that needs no CORS on the bundle host. Defaults to `files.` because the\n * `cdn.` Front Door route is still provisioning-gated (enableCdnDomain=false) —\n * flip the default only after that domain is live, or pass cdnScriptUrl.\n */\nexport const DEFAULT_AGENT_WIDGET_SCRIPT_URL =\n 'https://files.duffcloudservices.com/agent-widget/v1/dcs-agent-widget.js'\n\nexport interface DcsAgentWidgetLoaderOptions {\n /** Owning site slug — baked into the loader so it can build the config URL. */\n siteSlug: string\n /** DCS API base (default: the shared portal origin). */\n apiBaseUrl?: string\n /** Full URL of the CDN widget bundle. */\n cdnScriptUrl?: string\n}\n\nconst trimTrailingSlash = (value: string): string => value.replace(/\\/+$/, '')\n\n/**\n * JSON-encodes a value for safe embedding inside an inline `<script>` body.\n * `JSON.stringify` alone stops JS string-literal breakout but NOT the HTML\n * parser: a raw `</script>` (or a U+2028/U+2029 line separator, which is a valid\n * JS line terminator) inside the string would still terminate the element. So we\n * additionally escape `<`, `>`, and the two separators to their `\\uXXXX` forms —\n * defense-in-depth even though today every embedded value is build-time DCS\n * provisioning identity (DNS-label slug + platform URLs).\n */\nconst encodeForInlineScript = (value: string): string =>\n JSON.stringify(value)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029')\n\n/**\n * Builds the inline loader script body. Slug + URLs are HTML-safe-encoded (see\n * encodeForInlineScript) so no value can break out of the string literal OR the\n * `<script>` element. Kept intentionally compact to stay under the ~2 KB perf\n * budget (G4).\n */\nexport function buildAgentWidgetLoaderScript(options: DcsAgentWidgetLoaderOptions): string {\n const slug = encodeForInlineScript(options.siteSlug)\n const api = encodeForInlineScript(trimTrailingSlash(options.apiBaseUrl || DEFAULT_AGENT_API_BASE_URL))\n const cdn = encodeForInlineScript(options.cdnScriptUrl || DEFAULT_AGENT_WIDGET_SCRIPT_URL)\n\n return (\n '(function(){try{' +\n 'if(window.__DCS_AGENT_LOADER__)return;window.__DCS_AGENT_LOADER__=1;' +\n // Suppression parity with PreviewRibbon: skip the editor iframe (self!==top,\n // cross-origin access throws → treated as framed) and the ?dcs-hide-widget\n // screenshot/perf suppressor.\n 'try{if(window.self!==window.top)return;}catch(e){return;}' +\n 'if(/[?&]dcs-hide-widget(?:[=&]|$)/.test(location.search))return;' +\n 'var S=' + slug + ',A=' + api + ',C=' + cdn + ';' +\n 'var go=function(){try{' +\n // C-180: slug-free bootstrap probe. The loader runs on the customer's own\n // page, so a SAME-ORIGIN /api/v1/agent/config carries the customer Host and\n // SiteContextMiddleware resolves the site server-side — no slug in the path.\n // (Baking the slug into A+\"/api/v1/sites/\"+S+... hit the shared portal Host,\n // which cannot resolve a customer site.) A (apiBaseUrl) and S (slug) are still\n // handed to the widget bundle below for the chat/contact calls, which remain\n // slug-based against the legacy routes until they are migrated too.\n //\n // FALLBACK (2026-07-09 live finding): not every customer apex proxies\n // /api/v1/* to an API — kept's apex serves it from the STATIC origin (200\n // text/html SPA fallback), so the same-origin probe gets HTML and json()\n // rejects. When the slug-free probe yields nothing, retry the legacy\n // cross-origin slug route so the widget keeps mounting on proxy-less sites.\n //\n // C-290 (2026-07-25, probe matrix\n // .docs/plans/dynamic-site-resolution/evidence/agent-config-cors-matrix-2026-07-25.json):\n // the two arms were ALREADY sequential — the defect was that p() collapsed\n // \"no API on this route\" and \"the API answered, there is no agent\" into the\n // same null, so a site whose API authoritatively 404s fired a SECOND\n // guaranteed-404 request and logged a second uncatchable browser console\n // error on every page load. p() now distinguishes them:\n // null -> no API reached (network error, or a non-JSON body = SPA\n // fallback) => the legacy arm is worth trying\n // 0 -> a JSON API answered but the agent is off/absent => STOP, the\n // legacy arm can only 404 too\n // 0 is falsy, so the mount gate below is unchanged.\n 'var p=function(u){return fetch(u,{headers:{Accept:\"application/json\"}})' +\n // NB: no `<` / `>` anywhere in this body — the inline script must stay\n // angle-bracket-free so nothing can terminate the <script> element\n // (asserted by the HTML-hostile-slug test), hence indexOf(...)===-1.\n '.then(function(r){if(!r||(r.headers.get(\"content-type\")||\"\").indexOf(\"json\")===-1)return null;' +\n 'return r.ok?r.json()[\"catch\"](function(){return null;}):0;})' +\n '[\"catch\"](function(){return null;});};' +\n 'p(\"/api/v1/agent/config\")' +\n '.then(function(c){return c===null?p(A+\"/api/v1/sites/\"+encodeURIComponent(S)+\"/agent/config\"):c;})' +\n '.then(function(c){if(!c||!c.enabled)return;' +\n 'window.__DCS_AGENT_BOOTSTRAP__={siteSlug:S,apiBaseUrl:A,config:c};' +\n 'var s=document.createElement(\"script\");s.src=C;s.async=true;s.setAttribute(\"data-dcs-agent-slug\",S);' +\n '(document.head||document.documentElement).appendChild(s);})' +\n '.catch(function(){});' +\n '}catch(e){}};' +\n 'if(\"requestIdleCallback\"in window){requestIdleCallback(go,{timeout:4000});}else{setTimeout(go,1200);}' +\n '}catch(e){}})();'\n )\n}\n\nconst SITE_YAML_CANDIDATES = ['.dcs/site.yaml', '../.dcs/site.yaml']\n\n/**\n * Reads `site_slug` from the site's `.dcs/site.yaml` (the same identity file the\n * announcements/provisioning path uses). Returns null when no site.yaml exists\n * or it carries no slug — the caller then injects nothing (silence for non-DCS\n * builds).\n */\nexport function readSiteSlug(projectRoot: string): string | null {\n const candidates = [\n ...SITE_YAML_CANDIDATES.map((rel) => path.resolve(projectRoot, rel)),\n path.resolve(process.cwd(), '.dcs/site.yaml'),\n ]\n for (const candidate of candidates) {\n try {\n if (!fs.existsSync(candidate)) continue\n const parsed = yaml.load(fs.readFileSync(candidate, 'utf8')) as { site_slug?: unknown } | null\n const slug = parsed && typeof parsed.site_slug === 'string' ? parsed.site_slug.trim() : ''\n if (slug) return slug\n } catch {\n // Unreadable/unparseable — try the next candidate, else return null.\n }\n }\n return null\n}\n\nexport interface DcsAgentWidgetTagOptions {\n /** Vite project root used to resolve the site slug. */\n projectRoot: string\n /** Override the DCS API base (default: process.env.VITE_API_BASE_URL, else the shared origin). */\n apiBaseUrl?: string\n /** Override the CDN bundle URL. */\n cdnScriptUrl?: string\n}\n\n/**\n * The `<head>` tag descriptor for the loader, or `null` when this build is not a\n * DCS-managed site (no resolvable slug) — in which case nothing is injected.\n */\nexport function dcsAgentWidgetLoaderTag(options: DcsAgentWidgetTagOptions): HtmlTagDescriptor | null {\n const siteSlug = readSiteSlug(options.projectRoot)\n if (!siteSlug) return null\n\n return {\n tag: 'script',\n attrs: { 'data-dcs-agent-loader': '' },\n children: buildAgentWidgetLoaderScript({\n siteSlug,\n apiBaseUrl: options.apiBaseUrl,\n cdnScriptUrl: options.cdnScriptUrl,\n }),\n injectTo: 'head',\n }\n}\n","/**\n * DCS Content Plugin for Vite\n *\n * Reads `.dcs/content.yaml` at build time and injects content\n * as `__DCS_CONTENT__` global variable for use by useTextContent.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsContentPlugin({ debug: true })\n * ]\n * })\n * ```\n *\n * For VitePress:\n * ```typescript\n * // .vitepress/config.ts\n * import { defineConfig } from 'vitepress'\n * import { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * vite: {\n * plugins: [\n * dcsContentPlugin()\n * ]\n * }\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\nimport type { Plugin, ResolvedConfig, HtmlTagDescriptor } from 'vite'\nimport type { DcsContentFile } from '../types/content'\nimport { readDcsYamlFresh, buildGlobalAssignScript } from './serveDefineInject'\nimport { dcsMotionTokensStyleTag } from './motionTokens'\nimport { dcsAgentWidgetLoaderTag } from './agentWidget'\n\nexport interface DcsContentPluginOptions {\n /** Path to content.yaml relative to project root (default: '.dcs/content.yaml') */\n contentPath?: string\n /** Enable debug logging */\n debug?: boolean\n}\n\n/**\n * Packages that MUST be bundled into the SSR build (noExternal) rather than\n * externalized to `node_modules`.\n *\n * WHY: this plugin injects the site's content as the `__DCS_CONTENT__` Vite\n * `define`. A `define` is a compile-time text substitution that Vite applies\n * ONLY to source that passes through its transform pipeline. When a package is\n * SSR-externalized (loaded by Node's native resolver at server-render time), the\n * substitution never happens — `__DCS_CONTENT__` stays an undefined global on\n * the server. The content composables (`useReviewContent`, `useTextContent`)\n * then fall back to their empty/placeholder branch during SSR while the client\n * (where the define DID apply) renders the real content — producing the\n * \"Hydration completed but contains mismatches\" bug and, worse, baking\n * fabricated fallback content into the crawler-visible SSR HTML.\n *\n * Forcing the cms package `noExternal` bundles it into the SSR graph so the\n * define reaches the composables server-side, keeping SSR and client in lockstep\n * and putting the REAL content in the pre-rendered HTML. This is applied by the\n * plugin itself (merged into the user's Vite config) so every VitePress / vite\n * consumer inherits the fix on a cms bump — no per-site config knob to forget.\n */\nconst DCS_SSR_NO_EXTERNAL = ['@duffcloudservices/cms']\n\n/**\n * Vite plugin that injects .dcs/content.yaml at build time.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n */\nexport function dcsContentPlugin(options: DcsContentPluginOptions = {}): Plugin {\n const { contentPath = '.dcs/content.yaml', debug = false } = options\n\n let resolvedConfig: ResolvedConfig\n // Captured during configResolved — gates the serve-only inline HTML inject.\n let isDev = false\n\n return {\n name: 'dcs-content',\n\n configResolved(config) {\n resolvedConfig = config\n isDev = config.command === 'serve'\n },\n\n config(config, env) {\n // ── Dev / preview (serve) ──────────────────────────────────────────────\n // Don't bake content into the bundle. Redirect the `__DCS_CONTENT__`\n // define to a browser global that `transformIndexHtml` (below) refreshes\n // from disk on every HTML request — so a plain iframe reload reflects the\n // latest on-disk content with NO server.restart() and NO module-graph\n // surgery. The consumer is untouched: it still reads the bare\n // `__DCS_CONTENT__` identifier, which this define rewrites to\n // `globalThis.__DCS_CONTENT__`.\n if (env.command === 'serve') {\n return {\n define: {\n __DCS_CONTENT__: 'globalThis.__DCS_CONTENT__',\n },\n ssr: { noExternal: DCS_SSR_NO_EXTERNAL },\n }\n }\n\n // ── Production build ───────────────────────────────────────────────────\n // Unchanged: read content.yaml now and bake it into the bundle as a\n // JSON literal so the SSG / SPA output inlines content exactly as before.\n const projectRoot = config.root || process.cwd()\n\n // Try to find content.yaml in multiple locations\n // VitePress projects have the docs folder as root, so check parent too\n const possiblePaths = [\n path.resolve(projectRoot, contentPath),\n path.resolve(projectRoot, '..', contentPath),\n path.resolve(process.cwd(), contentPath),\n ]\n\n let foundPath: string | undefined\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n foundPath = testPath\n break\n }\n }\n\n if (!foundPath) {\n if (debug) {\n console.log('[dcs-content] No content.yaml found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n console.log('[dcs-content] Using defaults only')\n }\n return {\n define: {\n __DCS_CONTENT__: 'undefined',\n },\n ssr: { noExternal: DCS_SSR_NO_EXTERNAL },\n }\n }\n\n try {\n const fileContent = fs.readFileSync(foundPath, 'utf8')\n const content = yaml.load(fileContent) as DcsContentFile\n\n if (debug) {\n console.log(`[dcs-content] Loaded ${foundPath}`)\n console.log(`[dcs-content] Version: ${content.version}`)\n console.log(`[dcs-content] Pages: ${Object.keys(content.pages ?? {}).join(', ') || '(none)'}`)\n console.log(`[dcs-content] Global keys: ${Object.keys(content.global ?? {}).length}`)\n }\n\n return {\n define: {\n __DCS_CONTENT__: JSON.stringify(content),\n },\n ssr: { noExternal: DCS_SSR_NO_EXTERNAL },\n }\n } catch (error) {\n console.warn('[dcs-content] Failed to parse content.yaml:', error)\n return {\n define: {\n __DCS_CONTENT__: 'undefined',\n },\n ssr: { noExternal: DCS_SSR_NO_EXTERNAL },\n }\n }\n },\n\n /**\n * Inject into `<head>` on every HTML request. Two concerns:\n *\n * 1. The constant DCS motion-token `:root` block (`DCS_MOTION_TOKENS_CSS`)\n * — the fleet-wide seam that gives every DCS site the shared `--motion-*`\n * vocabulary. It carries no per-site data, so (unlike the content inject)\n * it is NOT gated on `isDev`: it runs in both `serve` and `build`.\n * It is injected `head` (append), NOT `head-prepend` — C-414. Appending\n * still lands it ABOVE the bundled site CSS (Vite injects the build's\n * `<link rel=stylesheet>` AFTER this `order: 'pre'` hook's tags), so a\n * site's own `:root` (theme `tailwind.css` / `style.css`) can still\n * override a token — while leaving the site's `<meta charset>` first in\n * `<head>` instead of 858 bytes deep. See `motionTokens.ts`.\n *\n * 2. Dev-only: the FRESH on-disk `content.yaml` as an inline\n * `globalThis.__DCS_CONTENT__ = {…}` classic script. Runs on every dev\n * HTML request, so the portal's `?_hc` hard reload re-reads the YAML with\n * no restart. A classic `<script>` in `<head>` executes before the\n * deferred module app entry, so the global is set before the consumer's\n * `useTextContent` setup reads it. Gated on `isDev` so it never runs in\n * `vite`/`vitepress build` (the build path keeps the baked `define`).\n */\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n // (1) Always-on, per the note above: the constant motion-token block.\n const tags: HtmlTagDescriptor[] = [dcsMotionTokensStyleTag()]\n\n // (1b) Build-only: the AI-assistant widget loader. Rides this same\n // fleet-wide seam (a cms bump ships it everywhere with no per-site\n // edits) but is NOT injected into `vite dev` — its idle config\n // fetch would hit the prod API from a non-whitelisted localhost\n // origin and log an uncatchable CORS error for every site dev. The\n // loader is fail-silent and returns null for non-DCS builds (no\n // resolvable site slug), so nothing is injected there.\n if (!isDev) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n const agentTag = dcsAgentWidgetLoaderTag({\n projectRoot,\n apiBaseUrl: process.env.VITE_API_BASE_URL,\n })\n if (agentTag) tags.push(agentTag)\n }\n\n // (2) Dev-only fresh content inject.\n if (isDev) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n const content = readDcsYamlFresh<DcsContentFile>(\n projectRoot,\n contentPath,\n 'dcs-content',\n debug\n )\n // No file (or unparseable) ⇒ leave the global unset; the consumer's\n // `typeof globalThis.__DCS_CONTENT__` check falls back to defaults.\n if (content !== undefined) {\n if (debug) {\n console.log('[dcs-content] serve: injecting fresh __DCS_CONTENT__ into <head>')\n }\n tags.push({\n tag: 'script',\n children: buildGlobalAssignScript('__DCS_CONTENT__', content),\n injectTo: 'head-prepend',\n })\n }\n }\n\n return { html, tags }\n },\n },\n\n // Watch for changes in development.\n //\n // Downgraded from `server.restart()` to a debounced client `full-reload`:\n // freshness already comes from `transformIndexHtml` re-reading the YAML on\n // every HTML request, so all we need on change is to nudge the browser to\n // reload. A `server.restart()` would rebuild VitePress's dev MiniSearch\n // index and can throw \"server restart failed\" on a duplicate heading id —\n // exactly the fragility this fix removes. A `full-reload` does neither.\n configureServer(server) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n\n const watchPaths = [\n path.resolve(projectRoot, contentPath),\n path.resolve(projectRoot, '..', contentPath),\n ].map((p) => path.normalize(p))\n\n watchPaths.forEach((watchPath) => {\n if (fs.existsSync(watchPath)) {\n server.watcher.add(watchPath)\n }\n })\n\n let reloadTimer: ReturnType<typeof setTimeout> | undefined\n server.watcher.on('change', (changedPath) => {\n if (!watchPaths.includes(path.normalize(changedPath))) return\n if (reloadTimer) clearTimeout(reloadTimer)\n reloadTimer = setTimeout(() => {\n if (debug) {\n console.log('[dcs-content] content.yaml changed, sending full-reload')\n }\n server.ws.send({ type: 'full-reload' })\n }, 100)\n })\n },\n }\n}\n","/**\n * The production {@link RouteRenderer}: a headless-Playwright renderer that\n * drives the JUST-BUILT SPA from `dist/` and returns each route's rendered\n * `#app` body.\n *\n * Design choices (see `bodyPrerender.ts` for the why):\n * - **Renders the PRODUCTION bundle**, so `import.meta.env.DEV` is already\n * `false` and the dev-only fabrication gates render their honest empty state\n * (A-34 safety by construction — no separate SSR bundle to leak defaults).\n * - **Playwright is dynamically imported and OPTIONAL.** If it (or a browser)\n * is unavailable, the factory returns `null` and the caller no-ops (the head\n * + JSON-LD emit is untouched, so the build never breaks on missing infra).\n * Playwright is already a universal devDependency of the DCS site fleet\n * (snapshot capture), so this activates on a cms bump with no per-site edit.\n * - **Offline + deterministic.** A tiny built-in static server serves `dist/`;\n * every EXTERNAL request (CDN images, App Insights, the site-auth probe) is\n * aborted. The DOM still carries those `<img>`/`<link>` tags (crawler-visible)\n * while the render stays fast and free of expected offline network noise.\n * - **Crash-only fail-loud.** Only an uncaught page exception or a failed mount\n * is reported as a fatal error; benign offline console errors are not. The\n * caller turns a fatal error into a red build.\n */\n\nimport http from 'node:http'\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport type { AddressInfo } from 'node:net'\nimport type { RenderedRoute, RouteRenderer } from './bodyPrerender'\n\n/** A started static server plus its `http://127.0.0.1:<port>` origin. */\ninterface StaticServer {\n origin: string\n close: () => Promise<void>\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.webp': 'image/webp',\n '.gif': 'image/gif',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.txt': 'text/plain; charset=utf-8',\n '.xml': 'application/xml; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n}\n\n/**\n * Serve `dist/` over loopback with directory-index resolution\n * (`/services` → `/services/index.html`) and an SPA fallback to the root\n * `index.html` for any path with no matching file/dir. Bound to `127.0.0.1:0`\n * so the OS assigns a free port; nothing is exposed off-box.\n */\nasync function startStaticServer(outDir: string): Promise<StaticServer> {\n const server = http.createServer((req, res) => {\n let pathname = '/'\n try {\n pathname = decodeURIComponent(new URL(req.url ?? '/', 'http://127.0.0.1').pathname)\n } catch {\n pathname = '/'\n }\n\n // Resolve within outDir; reject path traversal.\n const resolved = path.resolve(outDir, '.' + pathname)\n if (resolved !== outDir && !resolved.startsWith(outDir + path.sep)) {\n res.writeHead(403)\n res.end()\n return\n }\n\n let filePath = resolved\n try {\n const stat = fs.statSync(filePath)\n if (stat.isDirectory()) filePath = path.join(filePath, 'index.html')\n } catch {\n // Missing path: a request for an asset (has an extension) is a genuine 404;\n // a route-shaped request falls back to the SPA shell.\n if (path.extname(pathname)) {\n res.writeHead(404)\n res.end()\n return\n }\n filePath = path.join(outDir, 'index.html')\n }\n\n fs.readFile(filePath, (err, buf) => {\n if (err) {\n res.writeHead(404)\n res.end()\n return\n }\n res.writeHead(200, {\n 'Content-Type': CONTENT_TYPES[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream',\n })\n res.end(buf)\n })\n })\n\n await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve))\n const addr = server.address() as AddressInfo\n const origin = `http://127.0.0.1:${addr.port}`\n\n return {\n origin,\n close: () =>\n new Promise<void>((resolve) => {\n server.close(() => resolve())\n }),\n }\n}\n\n/** Options for {@link createPlaywrightRenderer}. */\nexport interface PlaywrightRendererOptions {\n /** Absolute path to the built SPA output directory (`dist`). */\n outDir: string\n /** Per-route navigation + settle budget (ms). Default 30000 nav, 15000 mount. */\n navTimeoutMs?: number\n mountTimeoutMs?: number\n /** Extra quiet settle after mount for post-render microtasks (ms). Default 250. */\n settleMs?: number\n debug?: boolean\n}\n\n/** A ready renderer plus a `close()` that tears down the browser + server. */\nexport interface PlaywrightRendererHandle {\n renderer: RouteRenderer\n close: () => Promise<void>\n}\n\n// Minimal structural types for the OPTIONAL, dynamically-imported `playwright`\n// surface — avoids a hard type/dependency on the package (it is not a cms\n// devDependency; it resolves from the consuming site at build time).\ninterface PwConsoleMessage {\n type(): string\n text(): string\n}\ninterface PwPage {\n on(event: 'pageerror', cb: (err: Error) => void): void\n on(event: 'console', cb: (msg: PwConsoleMessage) => void): void\n route(url: string, handler: (route: PwRoute) => unknown): Promise<void>\n goto(url: string, opts: { waitUntil: string; timeout: number }): Promise<unknown>\n waitForFunction(fn: () => unknown, arg?: unknown, opts?: { timeout: number }): Promise<unknown>\n waitForTimeout(ms: number): Promise<void>\n $eval(sel: string, fn: (el: Element) => string): Promise<string>\n evaluate<T>(fn: () => T): Promise<T>\n close(): Promise<void>\n}\ninterface PwRoute {\n request(): { url(): string }\n abort(): Promise<void>\n continue(): Promise<void>\n}\ninterface PwContext {\n newPage(): Promise<PwPage>\n close(): Promise<void>\n}\ninterface PwBrowser {\n newContext(opts?: unknown): Promise<PwContext>\n close(): Promise<void>\n}\n\n/**\n * Build the headless-Playwright renderer, or return `null` when Playwright / a\n * browser is unavailable (the caller then no-ops — head + JSON-LD are already\n * emitted, so the build is unaffected). Never throws on missing infra.\n */\nexport async function createPlaywrightRenderer(\n options: PlaywrightRendererOptions\n): Promise<PlaywrightRendererHandle | null> {\n const {\n outDir,\n navTimeoutMs = 30000,\n mountTimeoutMs = 15000,\n settleMs = 250,\n debug = false,\n } = options\n\n // Optional dependency: resolve `playwright` from the consuming site. The\n // computed specifier keeps the bundler from trying to resolve it at build time.\n let chromium: { launch(opts?: unknown): Promise<PwBrowser> } | undefined\n try {\n const specifier = 'playwright'\n const pw = (await import(/* @vite-ignore */ specifier)) as {\n chromium?: { launch(opts?: unknown): Promise<PwBrowser> }\n }\n chromium = pw.chromium\n } catch {\n console.warn(\n '[dcs-seo] body-prerender: `playwright` is not installed; skipping body ' +\n 'prerender (head + JSON-LD still emitted). `pnpm add -D playwright` to enable.'\n )\n return null\n }\n if (!chromium) {\n console.warn('[dcs-seo] body-prerender: playwright has no chromium export; skipping.')\n return null\n }\n\n let browser: PwBrowser\n try {\n browser = await chromium.launch({ headless: true })\n } catch (err) {\n console.warn(\n '[dcs-seo] body-prerender: failed to launch Chromium (run ' +\n '`npx playwright install chromium`); skipping body prerender:',\n err instanceof Error ? err.message : err\n )\n return null\n }\n\n // From here on a LIVE Chromium process is owned by this function, and the only\n // thing that can ever close it is the `close()` returned at the bottom. If any\n // step between the launch and that return throws, the caller receives a\n // rejection and no handle — so nothing is left holding a reference to the\n // browser, and it survives as an orphan process plus the open handles that\n // keep its host alive (a `vite build`, or a vitest worker). C-488: unwind what\n // we managed to construct, in reverse, before letting the error out.\n let staticServer: StaticServer | undefined\n let context: PwContext\n try {\n staticServer = await startStaticServer(outDir)\n context = await browser.newContext({ reducedMotion: 'reduce' })\n } catch (err) {\n await staticServer?.close().catch(() => {})\n await browser.close().catch(() => {})\n throw err\n }\n const server = staticServer\n\n const renderer: RouteRenderer = {\n async renderRoute(routePath: string): Promise<RenderedRoute> {\n const page = await context.newPage()\n const errors: string[] = []\n page.on('pageerror', (err) => errors.push(`pageerror: ${err.message}`))\n if (debug) {\n page.on('console', (msg) => {\n if (msg.type() === 'error') console.log(`[dcs-seo] (console) ${msg.text()}`)\n })\n }\n\n // Keep the render offline + fast: serve our own origin, abort everything\n // else. The DOM keeps external <img>/<link> tags regardless. Compare the\n // parsed origin, not a string prefix — `http://127.0.0.1:<port>@evil.com/…`\n // starts with our origin but resolves to evil.com. (Perf/noise gate, not a\n // security boundary — the trust boundary is that we run the site's own\n // production bundle.)\n await page.route('**/*', (route) => {\n let sameOrigin = false\n try {\n sameOrigin = new URL(route.request().url()).origin === server.origin\n } catch {\n sameOrigin = false\n }\n // Swallow the continue/abort rejection that races page.close (\"Route is\n // already handled\" / target closed) — an unhandled rejection would crash\n // the vite process under Node's throw-on-unhandled default.\n if (sameOrigin) {\n route.continue().catch(() => {})\n } else {\n route.abort().catch(() => {})\n }\n })\n\n try {\n await page.goto(server.origin + routePath, {\n waitUntil: 'load',\n timeout: navTimeoutMs,\n })\n // The app mounts into #app; wait until it has real children (a failed\n // mount / crash never satisfies this → timeout → fatal error below).\n await page.waitForFunction(\n () => {\n const el = document.querySelector('#app')\n return !!el && el.children.length > 0\n },\n undefined,\n { timeout: mountTimeoutMs }\n )\n if (settleMs > 0) await page.waitForTimeout(settleMs)\n const bodyHtml = await page.$eval('#app', (el) => el.innerHTML)\n // P1: read the POST-MOUNT <head> from the same render. This is what a\n // JS-rendering crawler (and every human) sees, and it is compared against\n // the baked <head> already on disk — see `headHonesty.ts`. The LAST\n // description wins because that is what unhead leaves in place after it\n // replaces the baked one; the count is carried so a duplicate is visible.\n const head = await page.evaluate(() => {\n const metas = Array.from(\n document.querySelectorAll('meta[name=\"description\" i]')\n ) as HTMLMetaElement[]\n return {\n title: document.title ?? null,\n description: metas.length ? metas[metas.length - 1].getAttribute('content') : null,\n descriptionCount: metas.length,\n }\n })\n return { bodyHtml: bodyHtml ?? '', errors, head }\n } catch (err) {\n // A nav/mount timeout is itself a fatal render failure — report it so the\n // caller fails the build loud rather than shipping an empty body.\n errors.push(err instanceof Error ? err.message : String(err))\n return { bodyHtml: '', errors }\n } finally {\n await page.close()\n }\n },\n }\n\n // C-488: `close()` is called from a `finally` (see `runBodyPrerender`), and a\n // caller that also closes defensively — or an error path and a normal path\n // that both reach the same `finally` — can call it twice. Memoise the teardown\n // so it is idempotent: the second caller awaits the SAME settled promise\n // instead of re-entering `context.close()` / `browser.close()` on already-torn-\n // down handles and racing the first teardown that is still in flight. Each step\n // is individually caught, so teardown reports success once everything it owns\n // is down and never surfaces a shutdown error as a build failure.\n let teardown: Promise<void> | null = null\n\n return {\n renderer,\n close() {\n teardown ??= (async () => {\n try {\n await context.close()\n } catch {\n /* ignore */\n }\n try {\n await browser.close()\n } catch {\n /* ignore */\n }\n try {\n await server.close()\n } catch {\n /* ignore */\n }\n })()\n return teardown\n },\n }\n}\n","/**\n * DCS SEO Plugin for Vite\n *\n * Two responsibilities, both driven by `.dcs/seo.yaml`:\n *\n * 1. **Build-time define** (always on): reads `.dcs/seo.yaml` and injects it as\n * the `__DCS_SEO__` global for the runtime `useSEO` composable.\n *\n * 2. **Static `<head>` emitter** (opt-in via `emitStaticHtml: true`, default\n * OFF): after the bundle is written, reads the SPA's built `index.html`,\n * and for every route in `.dcs/pages.yaml` writes a per-route\n * `dist/<path>/index.html` whose `<head>` carries the resolved title, meta,\n * canonical, Open Graph, Twitter, and JSON-LD tags. This gives a Vue SPA\n * per-route static SEO **without** vite-ssg.\n *\n * VitePress sites already bake SEO via their own config, so they leave this\n * option OFF and are completely unaffected.\n *\n * @example Runtime define only (default — safe for VitePress)\n * ```typescript\n * // vite.config.ts\n * import { dcsSeoPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [dcsSeoPlugin({ debug: true })]\n * })\n * ```\n *\n * @example Vue SPA with per-route static <head> emission\n * ```typescript\n * // vite.config.ts\n * import { dcsSeoPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsSeoPlugin({\n * emitStaticHtml: true, // turn the emitter ON\n * pagesPath: '.dcs/pages.yaml', // route manifest (default)\n * noindex: ['account', 'projects'], // robots: noindex,nofollow\n * exclude: ['/preview'], // skip these routes entirely\n * })\n * ]\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\nimport type { Plugin, ResolvedConfig, HtmlTagDescriptor } from 'vite'\nimport type { SeoConfiguration, SeoHonestyBlock } from '../types/seo'\nimport { readDcsYamlFresh, buildGlobalAssignScript } from './serveDefineInject'\nimport { buildHeadTags } from '../seo/headTags'\nimport { spliceHeadHtml } from '../seo/spliceHeadHtml'\nimport {\n loadPagesManifest,\n routeExclusionReason,\n isRouteEmitted,\n type PageRouteEntry,\n} from '../seo/pagesManifest'\nimport {\n findDuplicateNormalizedPaths,\n formatDuplicateNormalizedPaths,\n} from '../seo/installSeoHead'\nimport {\n prerenderBodies,\n isBodyPrerenderEnabled,\n routeToOutputFile,\n} from '../seo/bodyPrerender'\nimport { createPlaywrightRenderer } from '../seo/playwrightRenderer'\nimport {\n assertHeadHonesty,\n assertHeadHonestyExecuted,\n findHeadHonestyViolations,\n resolveHonestyMode,\n type HeadObservation,\n type HonestyMode,\n} from '../seo/headHonesty'\nimport {\n collectEmittedUrlsFromFiles,\n dedupeUrls,\n classifyUrl,\n auditSameOriginAssets,\n checkRemoteAssets,\n formatEmittedUrlReport,\n formatEmittedUrlAudit,\n auditCheckedTotal,\n EmittedUrlError,\n EmittedUrlNotRunError,\n type ClassifiedUrl,\n type EmittedUrlAudit,\n type EmittedUrlViolation,\n} from '../seo/emittedUrls'\nimport {\n hoistCharsetMeta,\n checkCharsetBudget,\n formatCharsetHeadroomWarning,\n CharsetBudgetError,\n} from '../seo/charsetBudget'\nimport {\n buildSitemapXml,\n buildRobotsTxt,\n buildLlmsTxt,\n isHandAuthoredRobotsAcceptable,\n type DcsRobotsOptions,\n} from '../seo/sitemap'\nimport {\n breadcrumbTrailFromRoute,\n findReviewItemsForPage,\n findBusinessLicense,\n type ContentConfig,\n type ReviewSource,\n type BlogMeta,\n type FaqSource,\n} from '../seo/schemaGraph'\n\nexport interface DcsSeoPluginOptions {\n /** Path to seo.yaml relative to project root (default: '.dcs/seo.yaml') */\n seoPath?: string\n /** Enable debug logging */\n debug?: boolean\n\n /**\n * Opt-in: emit per-route static `<head>` (meta + JSON-LD) into the built\n * `dist/` at the end of the build. Default `false` — VitePress sites and any\n * site that bakes its own SEO are unaffected when this is off.\n */\n emitStaticHtml?: boolean\n\n /**\n * Build-time BODY prerender: after the per-route `<head>` is emitted, render\n * each indexable route's real DOM from the just-built SPA (headless Chromium\n * via the site's `playwright`) and splice it into the mount container so non-JS\n * AI crawlers see the page's body prose — not just `<div id=\"app\"></div>`.\n *\n * Defaults to whatever `emitStaticHtml` is, so every SPA already emitting\n * per-route HTML gains crawler-visible bodies on a cms bump with no per-site\n * edit. Set `false` here to keep head-only emission. It is ALSO overridable\n * per-site (no code change / no cms republish) via `.dcs/seo.yaml`\n * `prerenderBody: false`. noindex/auth-gated routes are never body-prerendered;\n * a route that crashes at render time fails the build loud rather than shipping\n * a broken body. Requires `playwright` in the site (already the fleet default);\n * absent ⇒ graceful no-op (head + JSON-LD still emitted).\n */\n prerenderBody?: boolean\n\n /**\n * Path to the route manifest relative to project root, used only when\n * `emitStaticHtml` is true. Default `'.dcs/pages.yaml'`.\n */\n pagesPath?: string\n\n /**\n * Path to `content.yaml` relative to project root, used only when\n * `emitStaticHtml` is true. Threads REAL reviews into the honest\n * Review/aggregateRating on the per-route LocalBusiness node. Default\n * `'.dcs/content.yaml'`. Missing/unparseable ⇒ no reviews emitted.\n */\n contentPath?: string\n\n /**\n * Routes to skip entirely (no per-route HTML written). Matched against the\n * route `path` (e.g. `'/preview'`) OR the route `slug` (e.g. `'account'`).\n */\n exclude?: string[]\n\n /**\n * Routes that should receive `robots: noindex, nofollow`. Matched against the\n * route `path` OR `slug`. The home route (`/`) still overwrites\n * `dist/index.html`.\n *\n * This is a PREDICATE OVER `pages.yaml`, not a standalone directive about a\n * URL. An entry that matches no page is a **build error**\n * (`NoindexOrphanError`, C-416) — with no matching page nothing is emitted for\n * it, so the entry would silently do nothing while the URL stayed\n * `index, follow`. Matched raw, so `'/account/'` does not match a `'/account'`\n * route; matched against the full manifest, so an entry that is also in\n * `exclude` is fine.\n */\n noindex?: string[]\n\n /**\n * Opt-in: emit the site-wide static files `dist/sitemap.xml`,\n * `dist/robots.txt`, and `dist/llms.txt` after the per-route HTML loop.\n *\n * Defaults to whatever `emitStaticHtml` is — so every site already calling\n * `dcsSeoPlugin({ emitStaticHtml: true })` gets sitemap/robots/llms for free\n * with zero per-site edits. Set explicitly to `false` to keep emitting\n * per-route HTML without the site files. The whole block is wrapped in the\n * same try/catch + existsSync guards, so it can never break a build.\n */\n emitSiteFiles?: boolean\n\n /**\n * Canonical production origin, e.g. `https://ironoakcontractors.com`. Used as\n * the sitemap/llms base and the absolute `Sitemap:` line in robots.txt. When\n * absent, falls back to `seo.yaml`'s `global.siteUrl`; if neither is known and\n * no per-page canonicals exist, the sitemap + llms.txt no-op (robots still\n * emits). Never guessed.\n */\n siteUrl?: string\n\n /** robots.txt override hooks (disallow/allow/extra/aiBots/force). */\n robots?: DcsRobotsOptions\n\n /**\n * Preview / staging gate. When `true`, robots.txt becomes `Disallow: /` (no\n * `Sitemap:` line) and neither sitemap.xml nor llms.txt is emitted (privacy).\n * Detected ONLY via this explicit option — never guessed.\n */\n preview?: boolean\n\n /** Emit `dist/llms.txt` (default `true`; always off in preview mode). */\n llms?: boolean\n\n // ── SEO emission honesty rails (C-334) ────────────────────────────────────\n // Three asserts against one failure class: a capability that exists is\n // silently undone downstream and nothing notices. All three default ON\n // (severity `error`) whenever `emitStaticHtml` is on, and all three can be\n // lowered per-site from `.dcs/seo.yaml` (portal-owned) or per-build from an\n // env var that prints in the log. There is no silent off switch.\n\n /**\n * P1 — assert that each route's BAKED `<title>`/description equals what the\n * app leaves in the `<head>` after it mounts. Rides the prerender browser, so\n * the marginal cost is one `page.evaluate()` per route.\n *\n * Env override: `DCS_SEO_HEAD_HONESTY=error|warn|off`.\n * seo.yaml override: `headHonesty: warn` or `headHonesty: { mode, allow }`.\n */\n headHonesty?: boolean | HonestyMode | SeoHonestyBlock\n\n /**\n * P2 — assert that every URL the factory publishes (JSON-LD logo/image IRIs,\n * `og:image`, icon links, sitemap `<loc>`s, llms.txt links) resolves to what it\n * promises. Same-origin assets are proven against `dist/` with no network;\n * cross-origin assets are probed under a cache + time budget and only a\n * DEFINITIVE wrong answer fails the build.\n *\n * Env override: `DCS_SEO_URL_HONESTY=error|warn|off`,\n * `DCS_SEO_URL_HONESTY_NETWORK=off` to skip the cross-origin probes.\n */\n urlHonesty?: boolean | HonestyMode | SeoHonestyBlock\n\n /**\n * P12 — hoist `<meta charset>` to the top of `<head>` and assert it lands\n * inside the spec's 1024-byte encoding-sniffing window.\n *\n * Env override: `DCS_SEO_CHARSET_BUDGET=error|warn|off`.\n */\n charsetBudget?: boolean | HonestyMode | SeoHonestyBlock\n}\n\n/** Split a `boolean | mode | { mode, allow, … }` option into its parts. */\nfunction readHonestyBlock(\n value: boolean | HonestyMode | SeoHonestyBlock | undefined\n): { mode?: HonestyMode | boolean; allow: string[]; checkDescription?: boolean; network?: boolean } {\n if (value === undefined) return { allow: [] }\n if (typeof value === 'boolean' || typeof value === 'string') return { mode: value, allow: [] }\n return {\n mode: value.mode,\n allow: Array.isArray(value.allow) ? value.allow : [],\n checkDescription: value.checkDescription,\n network: value.network,\n }\n}\n\n/** Resolve the SPA output directory from Vite config (absolute path). */\nfunction resolveOutDir(config: ResolvedConfig): string {\n const out = config.build?.outDir || 'dist'\n return path.isAbsolute(out) ? out : path.resolve(config.root || process.cwd(), out)\n}\n\n/**\n * Load + parse seo.yaml from the usual `.dcs` locations.\n * Returns `{ config, foundPath }` or `null` (missing/unparseable).\n */\nfunction loadSeoConfig(\n projectRoot: string,\n seoPath: string,\n debug: boolean\n): { config: SeoConfiguration; foundPath: string } | null {\n const possiblePaths = [\n path.resolve(projectRoot, seoPath),\n path.resolve(projectRoot, '..', seoPath),\n path.resolve(process.cwd(), seoPath),\n ]\n\n let foundPath: string | undefined\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n foundPath = testPath\n break\n }\n }\n\n if (!foundPath) {\n if (debug) {\n console.log('[dcs-seo] No seo.yaml found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n console.log('[dcs-seo] Using defaults only')\n }\n return null\n }\n\n try {\n const fileContent = fs.readFileSync(foundPath, 'utf8')\n const config = yaml.load(fileContent) as SeoConfiguration\n return { config, foundPath }\n } catch (error) {\n console.warn('[dcs-seo] Failed to parse seo.yaml:', error)\n return null\n }\n}\n\n/**\n * Load + parse `.dcs/content.yaml` from the usual `.dcs` locations. Returns the\n * parsed config or `undefined` (missing/unparseable). Used to thread REAL\n * reviews into the honest Review/aggregateRating builder. Never throws.\n */\nfunction loadContentConfig(\n projectRoot: string,\n contentRelPath: string,\n debug: boolean\n): ContentConfig | undefined {\n const possiblePaths = [\n path.resolve(projectRoot, contentRelPath),\n path.resolve(projectRoot, '..', contentRelPath),\n path.resolve(process.cwd(), contentRelPath),\n ]\n const foundPath = possiblePaths.find((p) => fs.existsSync(p))\n if (!foundPath) {\n if (debug) console.log(`[dcs-seo] No content.yaml found (reviews disabled)`)\n return undefined\n }\n try {\n const parsed = yaml.load(fs.readFileSync(foundPath, 'utf8'))\n if (!parsed || typeof parsed !== 'object') return undefined\n return parsed as ContentConfig\n } catch (error) {\n console.warn('[dcs-seo] Failed to parse content.yaml (reviews disabled):', error)\n return undefined\n }\n}\n\n/** Extract the optional top-level `excluded:` glob list from pages.yaml. */\nfunction loadExcludedGlobs(projectRoot: string, pagesPath: string): string[] {\n const possiblePaths = [\n path.resolve(projectRoot, pagesPath),\n path.resolve(projectRoot, '..', pagesPath),\n path.resolve(process.cwd(), pagesPath),\n ]\n const found = possiblePaths.find((p) => fs.existsSync(p))\n if (!found) return []\n try {\n const raw = yaml.load(fs.readFileSync(found, 'utf8')) as { excluded?: unknown } | null\n const excluded = raw?.excluded\n if (!Array.isArray(excluded)) return []\n return excluded.filter((e): e is string => typeof e === 'string')\n } catch {\n return []\n }\n}\n\n/**\n * Refuse a manifest whose route paths collide after trailing-slash\n * normalization (C-361 F6).\n *\n * ONE OF TWO PLACES THE PLUGIN DELIBERATELY THROWS (the other is\n * {@link assertNoOrphanNoindexEntries}). Everything else in the\n * emit path is defensive by design — a missing or unparseable input warns and\n * no-ops, because a bad input must never break a production build. A duplicate\n * normalized path is a different animal: it is not a missing input, it is an\n * input for which every possible behaviour is wrong. The emitter's last write\n * wins the file on disk; the runtime lookup's first entry wins the head. Picking\n * a side ships a GUARANTEED baked/runtime divergence and calls it success —\n * exactly the silent-wrong-answer class this plan exists to kill — and the two\n * colliding entries have different slugs, so only the owner knows which page\n * that URL is. A red build is the cheap failure here; a green one is not.\n *\n * Thrown from `config()`, so it fails before a single byte is emitted.\n */\nfunction assertNoDuplicateRoutePaths(routes: PageRouteEntry[]): void {\n const dupes = findDuplicateNormalizedPaths(routes)\n if (dupes.length === 0) return\n throw new Error(`[dcs-seo] ${formatDuplicateNormalizedPaths(dupes)}`)\n}\n\n/**\n * Every key a `noindex` entry can legitimately match.\n *\n * The plugin's contract for `noindex` is a PREDICATE OVER THE ROUTE MANIFEST,\n * never a directive about a bare URL. Every consumer asks the question\n * route-first and identically —\n * `noindexSet.has(route.path) || (route.slug && noindexSet.has(route.slug))` —\n * in {@link emitStaticSeoHtml}, in the sitemap/llms `isRouteIndexable`\n * predicate, and in the baked `__DCS_PAGES__` manifest the runtime re-assert\n * reads. So the set of entries that can ever do anything is exactly the union\n * of the manifest's paths and slugs, and this function is the exact inverse of\n * that same relation rather than a second, drifting implementation of it.\n */\nexport function collectNoindexMatchKeys(routes: PageRouteEntry[]): Set<string> {\n const keys = new Set<string>()\n for (const route of routes) {\n if (route.path) keys.add(route.path)\n if (route.slug) keys.add(route.slug)\n }\n return keys\n}\n\n/**\n * `noindex` entries that match no route in the manifest — i.e. entries that\n * are, today, silent no-ops.\n *\n * Matched with the SAME raw string equality the consumers use, deliberately:\n * a `'/account/'` entry against a `'/account'` route does not match in\n * `emitStaticSeoHtml`, so it must not \"match\" here either, or the check would\n * bless the very entry the emitter is about to ignore.\n */\nexport function findOrphanNoindexEntries(\n noindex: string[],\n routes: PageRouteEntry[]\n): string[] {\n const known = collectNoindexMatchKeys(routes)\n const seen = new Set<string>()\n const orphans: string[] = []\n for (const entry of noindex) {\n if (known.has(entry) || seen.has(entry)) continue\n seen.add(entry)\n orphans.push(entry)\n }\n return orphans\n}\n\n/** Thrown when a `noindex` entry names nothing in `pages.yaml` (C-416). */\nexport class NoindexOrphanError extends Error {\n readonly orphans: string[]\n readonly knownKeys: string[]\n constructor(orphans: string[], knownKeys: string[]) {\n super(\n `[dcs-seo] noindex entr${orphans.length === 1 ? 'y' : 'ies'} matching NO page in the ` +\n `route manifest: ${orphans.map((o) => JSON.stringify(o)).join(', ')}. ` +\n `A noindex entry is a predicate over pages.yaml, not a standalone directive: ` +\n `with no matching page NOTHING is emitted for that route, so the entry is a ` +\n `silent no-op and the URL stays \"index, follow\" to crawlers — which is exactly ` +\n `how six homepage-clone URLs on KEPT stayed indexable. ` +\n `Fix it by adding the page to pages.yaml (it then gets a real ` +\n `robots: noindex, nofollow document, sitemap/llms omission, and a runtime ` +\n `re-assert), or by deleting the stale entry. ` +\n `Matchable keys are the manifest's paths and slugs: ` +\n `${knownKeys.map((k) => JSON.stringify(k)).join(', ') || '(manifest is empty)'}`\n )\n this.name = 'NoindexOrphanError'\n this.orphans = orphans\n this.knownKeys = knownKeys\n }\n}\n\n/**\n * Refuse a `noindex` list containing an entry that matches no page (C-416).\n *\n * THE SECOND PLACE THE PLUGIN DELIBERATELY THROWS, and for the same reason as\n * {@link assertNoDuplicateRoutePaths}: this is not a missing input to be\n * defended against, it is an input whose only possible behaviour is wrong.\n *\n * Why a hard error and NOT \"emit a noindex document for it instead\":\n * - An entry is allowed to be a bare SLUG (`'account'`), and a slug carries no\n * URL. Synthesizing a document would mean inventing a path — inventing a\n * crawlable URL to paper over a config typo is strictly worse than the bug\n * being fixed.\n * - Even for the path form, writing `dist/<path>/index.html` for a route the\n * SPA does not have MANUFACTURES a real, fetchable URL that would otherwise\n * never resolve. The remedy would create crawl surface, not remove it.\n * - Adding the page to `pages.yaml` is the operator action that makes the\n * directive real everywhere at once (emitted document + sitemap/llms\n * omission + runtime re-assert). Anything the plugin could synthesize on its\n * own would cover only the first of those three, i.e. a second partial\n * capability with nothing watching — the class this whole rail family exists\n * to kill.\n *\n * There is deliberately NO severity knob / escape hatch (unlike the C-334\n * rails, which measure emitted artifacts and can hit network or environment\n * flake). There is no legitimate orphan: the fix is always \"add the page\" or\n * \"delete the row\", both free.\n *\n * Matched against the FULL manifest, BEFORE `exclude` / `excluded:` globs are\n * applied. A route that is both excluded and noindexed names a real page, and\n * an excluded route publishes no document to be indexable — erroring there\n * would be a false positive that pushes operators to delete honest rows.\n *\n * Thrown from `config()`, so it fails before a single byte is emitted — and,\n * critically, OUTSIDE the `writeBundle` emit try/catch, which downgrades\n * everything it wraps to a `console.warn`. A version of this check placed\n * inside that catch would be vacuously green.\n */\nfunction assertNoOrphanNoindexEntries(noindex: string[], routes: PageRouteEntry[]): void {\n if (noindex.length === 0) return\n const orphans = findOrphanNoindexEntries(noindex, routes)\n if (orphans.length === 0) return\n throw new NoindexOrphanError(orphans, [...collectNoindexMatchKeys(routes)].sort())\n}\n\n/**\n * Derive a single site-wide lastmod from seo.yaml `lastUpdated` (preferred) or\n * pages.yaml `lastUpdated`. No fabricated per-page dates.\n */\nfunction deriveLastmod(\n seoConfig: SeoConfiguration | undefined,\n projectRoot: string,\n pagesPath: string\n): string | undefined {\n const fromSeo = seoConfig?.lastUpdated\n if (typeof fromSeo === 'string' && fromSeo.length > 0) return fromSeo\n\n const possiblePaths = [\n path.resolve(projectRoot, pagesPath),\n path.resolve(projectRoot, '..', pagesPath),\n path.resolve(process.cwd(), pagesPath),\n ]\n const found = possiblePaths.find((p) => fs.existsSync(p))\n if (!found) return undefined\n try {\n const raw = yaml.load(fs.readFileSync(found, 'utf8')) as { lastUpdated?: unknown } | null\n const fromPages = raw?.lastUpdated\n if (typeof fromPages === 'string' && fromPages.length > 0) return fromPages\n } catch {\n /* ignore */\n }\n return undefined\n}\n\n/**\n * Core site-file emitter, separated from Vite so it is unit-testable.\n *\n * Writes the site-wide static files into `outDir`, reusing the SAME routes +\n * seoConfig already loaded by the per-route HTML loop (no second manifest read):\n * - `sitemap.xml` (skipped in preview, or when no absolute base is derivable)\n * - `robots.txt` (always; preview = `Disallow: /`; don't-clobber unless force)\n * - `llms.txt` (skipped in preview, or when nothing indexable)\n *\n * Returns the list of relative filenames written. Pure aside from `fs` writes —\n * deterministic + idempotent. Never throws (the caller also wraps in try/catch).\n */\nexport function emitSiteFiles(params: {\n outDir: string\n projectRoot: string\n pagesPath: string\n routes: PageRouteEntry[]\n seoConfig: SeoConfiguration | undefined\n siteUrl?: string\n exclude?: string[]\n noindex?: string[]\n preview?: boolean\n robots?: DcsRobotsOptions\n llms?: boolean\n debug?: boolean\n}): string[] {\n const {\n outDir,\n projectRoot,\n pagesPath,\n routes,\n seoConfig,\n siteUrl,\n exclude = [],\n noindex = [],\n preview = false,\n robots = {},\n llms = true,\n debug = false,\n } = params\n\n const written: string[] = []\n\n const effectiveSiteUrl = siteUrl || seoConfig?.global?.siteUrl\n const excludedGlobs = loadExcludedGlobs(projectRoot, pagesPath)\n const lastmod = deriveLastmod(seoConfig, projectRoot, pagesPath)\n\n // ── sitemap.xml (not in preview) ──────────────────────────────────────────\n let wroteSitemap = false\n if (!preview) {\n const xml = buildSitemapXml({\n routes,\n siteUrl: effectiveSiteUrl,\n seoConfig,\n exclude,\n noindex,\n excludedGlobs,\n lastmod,\n })\n if (xml) {\n fs.writeFileSync(path.join(outDir, 'sitemap.xml'), xml, 'utf8')\n wroteSitemap = true\n written.push('sitemap.xml')\n if (debug) console.log('[dcs-seo] wrote sitemap.xml')\n } else if (debug) {\n console.log('[dcs-seo] no absolute <loc> derivable; sitemap.xml not written')\n }\n }\n\n // ── robots.txt (always, unless robots.enabled is false) ───────────────────\n if (robots.enabled ?? true) {\n const robotsPath = path.join(outDir, 'robots.txt')\n // Factory-as-single-source (validated override): a hand-authored\n // public/robots.txt Vite copied into dist is KEPT only when it meets the\n // factory floor (a real `User-agent: *` group + every AI-bot tier). A stub\n // (`Sitemap:`-only, like just-posh) or an SPA-shell HTML page fails the\n // floor and is overwritten so AI discovery is guaranteed on every site.\n // `force` always overwrites; an absent file is always written.\n const existing =\n fs.existsSync(robotsPath) && !robots.force ? fs.readFileSync(robotsPath, 'utf8') : null\n const keepHandAuthored = existing !== null && isHandAuthoredRobotsAcceptable(existing)\n if (keepHandAuthored) {\n if (debug)\n console.log('[dcs-seo] dist/robots.txt is hand-authored and meets the floor; leaving it')\n } else {\n const txt = buildRobotsTxt({\n siteUrl: effectiveSiteUrl,\n preview,\n robots,\n hasSitemap: wroteSitemap,\n })\n fs.writeFileSync(robotsPath, txt, 'utf8')\n written.push('robots.txt')\n if (debug) {\n console.log(\n existing !== null\n ? '[dcs-seo] dist/robots.txt failed the factory floor; overwrote with emitted robots'\n : '[dcs-seo] wrote robots.txt'\n )\n }\n }\n }\n\n // ── llms.txt (not in preview) ─────────────────────────────────────────────\n if (llms && !preview) {\n const txt = buildLlmsTxt({\n routes,\n siteUrl: effectiveSiteUrl,\n seoConfig,\n exclude,\n noindex,\n excludedGlobs,\n })\n if (txt) {\n fs.writeFileSync(path.join(outDir, 'llms.txt'), txt, 'utf8')\n written.push('llms.txt')\n if (debug) console.log('[dcs-seo] wrote llms.txt')\n } else if (debug) {\n console.log('[dcs-seo] nothing indexable; llms.txt not written')\n }\n }\n\n return written\n}\n\n/**\n * Pick a structured FAQ array (`[{ q, a }]`) for a page from content.yaml, if\n * one exists under a `faq` key (page-scoped, then global). Returns `undefined`\n * when there is no structured Q&A — a `<FaqPage />` component is NOT a source,\n * so its pages correctly yield nothing (honesty).\n */\nfunction pickPageFaq(\n content: ContentConfig | undefined,\n pageSlug: string\n): FaqSource[] | undefined {\n if (!content) return undefined\n const fromBlock = (block: Record<string, unknown> | undefined): FaqSource[] | undefined => {\n if (!block) return undefined\n const v = block['faq']\n return Array.isArray(v) ? (v as FaqSource[]) : undefined\n }\n return fromBlock(content.pages?.[pageSlug]) ?? fromBlock(content.global)\n}\n\n/**\n * Core emitter, separated from Vite so it is unit-testable.\n *\n * For each route: build the head tags from the shared resolver, splice them\n * into the shell HTML, and write `dist/<path>/index.html`. Returns the number\n * of files written. Pure aside from `fs` writes — deterministic + idempotent.\n */\nexport function emitStaticSeoHtml(params: {\n outDir: string\n shellHtml: string\n routes: PageRouteEntry[]\n seoConfig: SeoConfiguration | undefined\n /**\n * Parsed `.dcs/content.yaml` — threads REAL review items (honesty-gated) into\n * the per-route LocalBusiness node. Optional; omit ⇒ no Review/aggregateRating.\n */\n contentConfig?: ContentConfig\n exclude?: string[]\n noindex?: string[]\n /** `pages.yaml` top-level `excluded:` globs — routes matching are NOT emitted. */\n excludedGlobs?: string[]\n debug?: boolean\n}): number {\n const {\n outDir,\n shellHtml,\n routes,\n seoConfig,\n contentConfig,\n exclude = [],\n noindex = [],\n excludedGlobs = [],\n debug = false,\n } = params\n\n const noindexSet = new Set(noindex)\n\n // Route → human title map (for breadcrumb hop labels), from the manifest.\n const siteUrl = seoConfig?.global?.siteUrl ?? ''\n const routeTitles: Record<string, string> = { '/': 'Home' }\n for (const r of routes) {\n if (r.title && r.path) routeTitles[r.path.replace(/\\/+$/, '') || '/'] = r.title\n }\n\n let written = 0\n for (const route of routes) {\n // Exclusion goes through the SHARED predicate (`routeExclusionReason`), the\n // same one that filters the `__DCS_PAGES__` runtime manifest. Two agreeing\n // implementations is how a private `/dev-*` route came to be never-baked but\n // still runtime-asserted (C-361 F5); one predicate is why it cannot recur.\n // An excluded route must NOT get a per-route index.html, or it becomes a\n // crawlable/indexable page despite being intentionally hidden — a privacy\n // and crawl-budget leak. Same glob semantics as the sitemap path.\n const exclusion = routeExclusionReason(route, { exclude, excludedGlobs })\n if (exclusion) {\n if (debug) {\n console.log(\n `[dcs-seo] skip (${exclusion === 'excluded-glob' ? 'excluded glob' : 'excluded'}): ${route.path}`\n )\n }\n continue\n }\n\n const forceNoindex =\n noindexSet.has(route.path) || (route.slug && noindexSet.has(route.slug))\n\n // Breadcrumb trail from route depth (home ⇒ none); titles from the manifest.\n const breadcrumbTrail = siteUrl\n ? breadcrumbTrailFromRoute(route.path, siteUrl, routeTitles)\n : undefined\n\n // REAL reviews for this page from content.yaml (honesty-gated downstream).\n const reviews: ReviewSource[] | undefined = contentConfig\n ? findReviewItemsForPage(contentConfig, route.slug)\n : undefined\n\n // Trade/occupational license from content.yaml `business.license` (dark when\n // the key is empty/absent — no hasCredential is emitted). Honesty-gated in\n // buildHasCredential downstream.\n const license: string | undefined = contentConfig\n ? findBusinessLicense(contentConfig, route.slug)\n : undefined\n\n // BlogPosting for a blog post route — only when seo.yaml has NOT already\n // hand-authored one (buildHeadTags skips ours if it has). A blog post is a\n // route nested under `/blog/` (depth ≥ 2) with a manifest title.\n const isBlogPost = /^\\/blog\\/.+/.test(route.path) && !!route.title\n const blogMeta: BlogMeta | undefined = isBlogPost\n ? { headline: route.title, url: breadcrumbTrail?.[breadcrumbTrail.length - 1]?.item }\n : undefined\n\n // FAQ from a structured `.dcs/faq.yaml`-style block on the content page, if\n // present (honesty-gated). Component-only FAQ pages carry no such data.\n const faq: FaqSource[] | undefined = pickPageFaq(contentConfig, route.slug)\n\n const tags = buildHeadTags(route.slug, route.path, seoConfig, {\n includeKeywords: true,\n fallbackTitle: route.title,\n robots: forceNoindex ? 'noindex, nofollow' : undefined,\n emitGraph: true,\n breadcrumbTrail,\n reviews,\n license,\n blogMeta,\n faq,\n })\n\n const html = spliceHeadHtml(shellHtml, tags)\n const outFile = routeToOutputFile(outDir, route.path)\n\n fs.mkdirSync(path.dirname(outFile), { recursive: true })\n fs.writeFileSync(outFile, html, 'utf8')\n written++\n\n if (debug) {\n console.log(\n `[dcs-seo] wrote ${path.relative(outDir, outFile)} (title=\"${tags.title}\"${\n forceNoindex ? ', noindex' : ''\n })`\n )\n }\n }\n\n return written\n}\n\n/**\n * What the render pass produced, INCLUDING whether it ran at all. The\n * `rendererAvailable` / `eligibleRoutes` fields are what make the P1 rail\n * provably executed rather than silently skipped (C-338).\n */\ninterface BodyPrerenderOutcome {\n rendererAvailable: boolean\n headObservations: HeadObservation[]\n eligibleRoutes: string[]\n filteredRoutes: Array<{ route: string; reason: string }>\n}\n\n/**\n * Run the body-prerender pass: bring up the headless renderer, fill each\n * indexable route's `<div id=\"app\">` with its real rendered DOM, then tear the\n * renderer down. Missing infra (no playwright / no browser) makes the BODY pass\n * a no-op but is reported back so the P1 rail can fail loud instead of passing\n * green having checked nothing; a render crash propagates a `BodyPrerenderError`.\n */\nasync function runBodyPrerender(\n ctx: { outDir: string; routes: PageRouteEntry[]; excludedGlobs: string[] },\n opts: {\n exclude: string[]\n noindex: string[]\n debug: boolean\n /** `false` ⇒ render for OBSERVATION only (P1) and leave `dist/` untouched. */\n spliceBody: boolean\n }\n): Promise<BodyPrerenderOutcome> {\n // Kill-test hook (C-338): forces the renderer-absent path so the P1 execution\n // assertion can be proven to FAIL a build. NOT an escape hatch — with the\n // assertion in place, setting this in `error` mode turns the build RED.\n const forcedAbsent = process.env.DCS_SEO_FORCE_NO_RENDERER === '1'\n if (forcedAbsent) {\n console.warn(\n '[dcs-seo] DCS_SEO_FORCE_NO_RENDERER=1 — pretending the headless renderer is absent ' +\n '(kill-test hook; in error mode this MUST fail the build).'\n )\n }\n const handle = forcedAbsent\n ? null\n : await createPlaywrightRenderer({ outDir: ctx.outDir, debug: opts.debug })\n if (!handle) {\n // playwright/browser unavailable. Say so explicitly — without a renderer the\n // P1 head-honesty rail cannot run either, and a rail that silently does not\n // run is the exact class this work exists to close. The CALLER turns this\n // into a hard failure in `error` mode (see `assertHeadHonestyExecuted`).\n console.warn(\n '[dcs-seo] no headless renderer available: body prerender AND the P1 ' +\n 'baked-vs-rendered <head> assert were both SKIPPED for this build.'\n )\n return {\n rendererAvailable: false,\n headObservations: [],\n eligibleRoutes: [],\n filteredRoutes: [],\n }\n }\n\n console.log(\n opts.spliceBody\n ? '[dcs-seo] body-prerender: rendering routes via headless Chromium…'\n : '[dcs-seo] head-honesty: rendering routes via headless Chromium (observe-only)…'\n )\n try {\n const result = await prerenderBodies({\n outDir: ctx.outDir,\n routes: ctx.routes,\n renderer: handle.renderer,\n exclude: opts.exclude,\n noindex: opts.noindex,\n excludedGlobs: ctx.excludedGlobs,\n spliceBody: opts.spliceBody,\n debug: opts.debug,\n })\n if (opts.spliceBody) {\n console.log(\n `[dcs-seo] body-prerender: ${result.prerendered} route(s) now have ` +\n `crawler-visible body content (${result.skipped} skipped)`\n )\n }\n return {\n rendererAvailable: true,\n headObservations: result.headObservations,\n eligibleRoutes: result.eligibleRoutes,\n filteredRoutes: result.filteredRoutes,\n }\n } finally {\n await handle.close()\n }\n}\n\n/**\n * P2 — collect every URL the just-completed emit published and assert it\n * resolves to what it promises.\n *\n * Deliberately two-phase, because the two halves have different reliability:\n * the same-origin half is proven against `dist/` with no network and is\n * therefore a hard gate; the cross-origin half asks the network and only fails\n * on a definitive wrong answer. See `emittedUrls.ts` for the verdict table.\n */\nasync function runEmittedUrlHonesty(params: {\n outDir: string\n projectRoot: string\n routes: PageRouteEntry[]\n siteUrl: string | undefined\n base: string\n mode: HonestyMode\n allow: string[]\n network: boolean\n debug: boolean\n /** Receives the audit BEFORE any throw, so the report survives a red build. */\n sink?: (audit: EmittedUrlAudit, violations: EmittedUrlViolation[]) => void\n}): Promise<EmittedUrlAudit | null> {\n const { outDir, projectRoot, routes, siteUrl, base, mode, allow, network, debug, sink } = params\n if (mode === 'off') {\n console.warn(\n '[dcs-seo] emitted-URL honesty is OFF for this build — nothing is checking that the ' +\n 'logo/og:image/sitemap URLs this site publishes actually resolve.'\n )\n return null\n }\n if (allow.length > 0) {\n console.warn(\n `[dcs-seo] emitted-URL honesty: ${allow.length} URL(s) EXEMPTED: ${allow.join(', ')}`\n )\n }\n\n const files: Array<{ path: string; from: string; kind: 'html' | 'sitemap' | 'llms' }> = []\n for (const route of routes) {\n files.push({ path: routeToOutputFile(outDir, route.path), from: route.path, kind: 'html' })\n }\n files.push({ path: path.join(outDir, 'sitemap.xml'), from: 'sitemap.xml', kind: 'sitemap' })\n files.push({ path: path.join(outDir, 'llms.txt'), from: 'llms.txt', kind: 'llms' })\n\n const collected = dedupeUrls(collectEmittedUrlsFromFiles(files))\n const classified: ClassifiedUrl[] = collected.map((u) => classifyUrl(u, siteUrl))\n\n const sameOrigin = auditSameOriginAssets({ urls: classified, outDir, base, allow })\n const violations: EmittedUrlViolation[] = [...sameOrigin.violations]\n\n // Every count below comes from the CHECKER, never from `classified.length` —\n // that conflation is what let C-334's log claim page URLs were verified when\n // both checkers only ever look at assets (C-338).\n const audit: EmittedUrlAudit = {\n collected: classified.length,\n sameOriginAssetsChecked: sameOrigin.checked,\n remoteAssetsChecked: 0,\n remoteProbed: 0,\n remoteCached: 0,\n unknown: 0,\n pagesUnverified: classified.filter((u) => u.expectation === 'page').length,\n unfetchable: classified.filter((u) => u.locality === 'unfetchable').length,\n exempted: sameOrigin.exempted,\n failed: 0,\n }\n\n if (network) {\n const remote = await checkRemoteAssets(classified, { projectRoot, allow, debug })\n violations.push(...remote.violations)\n for (const w of remote.warnings) {\n console.warn(`[dcs-seo] emitted-URL honesty (UNKNOWN, not a failure): ${w.url} — ${w.reason}`)\n }\n audit.remoteAssetsChecked = remote.checked ?? 0\n audit.remoteProbed = remote.probed\n audit.remoteCached = remote.cached\n audit.unknown = remote.warnings.length\n audit.exempted += remote.exempted ?? 0\n if (debug) {\n console.log(\n `[dcs-seo] emitted-URL honesty: remote probes — ${remote.probed} network, ${remote.cached} cached`\n )\n }\n } else {\n // Probing off ⇒ every remote asset is UNKNOWN. It must not be silently\n // absorbed into a \"verified\" total.\n audit.unknown = classified.filter(\n (u) => u.locality === 'remote' && u.expectation === 'asset'\n ).length\n console.log(\n '[dcs-seo] emitted-URL honesty: cross-origin probes disabled — ' +\n `${audit.unknown} remote asset URL(s) are UNCHECKED this build; same-origin still enforced`\n )\n }\n\n audit.failed = violations.length\n sink?.(audit, violations)\n const checkedTotal = auditCheckedTotal(audit)\n\n // Execution assertion: collecting nothing means the rail did not run. In\n // `error` mode that is a hard failure, not a green build.\n if (audit.collected === 0) {\n const err = new EmittedUrlNotRunError(\n audit,\n `0 URLs were collected from ${files.length} emitted artifact(s)`\n )\n if (mode === 'error') throw err\n console.warn(err.message)\n console.warn('[dcs-seo] emitted-URL honesty is in WARN mode; this build is NOT gated on the above.')\n return audit\n }\n // Collected-but-checked-nothing is legal (a site can publish only page URLs)\n // but must never read as a pass. Say it in every mode.\n if (checkedTotal === 0) {\n console.warn(\n `[dcs-seo] emitted-URL honesty VERIFIED NOTHING this build: ${audit.collected} URL(s) were ` +\n `collected but none was a checkable asset (${audit.pagesUnverified} page(s), ` +\n `${audit.unknown} unknown, ${audit.exempted} exempted, ${audit.unfetchable} unfetchable).`\n )\n }\n\n if (violations.length === 0) {\n console.log(formatEmittedUrlAudit(audit, routes.length))\n return audit\n }\n\n if (mode === 'warn') {\n console.warn(formatEmittedUrlReport(violations))\n console.warn(formatEmittedUrlAudit(audit, routes.length))\n console.warn('[dcs-seo] emitted-URL honesty is in WARN mode; this build is NOT gated on the above.')\n return audit\n }\n throw new EmittedUrlError(violations)\n}\n\n/**\n * P12 — assert the encoding declaration lands inside the 1024-byte sniffing\n * window in every emitted document.\n *\n * The hoist in `emitStaticSeoHtml`'s shell should make this unfailable; the\n * assert exists so that if the hoist is ever bypassed (a new head injector, a\n * post-processing plugin that runs later, a hand-rolled shell), the build says so\n * instead of shipping a document whose encoding is a coin flip.\n */\nfunction runCharsetBudget(params: {\n outDir: string\n routes: PageRouteEntry[]\n mode: HonestyMode\n debug: boolean\n /** Receives the counts BEFORE any throw, so the report survives a red build. */\n sink?: (summary: { checked: number; failed: number; worstOffset: number }) => void\n}): void {\n const { outDir, routes, mode, debug, sink } = params\n if (mode === 'off') return\n\n const checked: string[] = []\n let failed = 0\n let worstOffset = 0\n const emit = () => sink?.({ checked: checked.length, failed, worstOffset })\n for (const route of routes) {\n const file = routeToOutputFile(outDir, route.path)\n let html: string\n try {\n html = fs.readFileSync(file, 'utf8')\n } catch {\n continue\n }\n const rel = path.relative(outDir, file)\n const result = checkCharsetBudget(html)\n worstOffset = Math.max(worstOffset, result.offset)\n if (!result.ok) {\n failed += 1\n if (mode === 'warn') {\n console.warn(new CharsetBudgetError(rel, result).message)\n continue\n }\n emit()\n throw new CharsetBudgetError(rel, result)\n }\n const warning = formatCharsetHeadroomWarning(rel, result)\n if (warning) console.warn(warning)\n checked.push(rel)\n }\n emit()\n if (debug || checked.length > 0) {\n console.log(\n `[dcs-seo] charset budget: ${checked.length} emitted document(s) declare their encoding ` +\n `inside the first 1024 bytes`\n )\n }\n}\n\n/**\n * Vite plugin that injects .dcs/seo.yaml at build time and, optionally, emits\n * per-route static `<head>` HTML for SPA SEO.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n */\nexport function dcsSeoPlugin(options: DcsSeoPluginOptions = {}): Plugin {\n const {\n seoPath = '.dcs/seo.yaml',\n debug = false,\n emitStaticHtml = false,\n prerenderBody,\n pagesPath = '.dcs/pages.yaml',\n contentPath = '.dcs/content.yaml',\n exclude = [],\n noindex = [],\n // Site files default ON whenever per-route HTML emission is on, so the\n // sites already using emitStaticHtml gain sitemap/robots/llms with no edit.\n emitSiteFiles: emitSiteFilesOpt = emitStaticHtml,\n siteUrl,\n headHonesty: headHonestyOpt,\n urlHonesty: urlHonestyOpt,\n charsetBudget: charsetBudgetOpt,\n robots = {},\n // Self-detect preview builds from DCS_PREVIEW (mirrors dcsEditorPlugin) so a\n // release/** SWA preview build never body-prerenders; an explicit option wins.\n preview = process.env.DCS_PREVIEW === 'true',\n llms = true,\n } = options\n\n let resolvedConfig: ResolvedConfig\n // Captured during configResolved — gates the serve-only inline HTML inject.\n let isDev = false\n\n return {\n name: 'dcs-seo',\n\n configResolved(config) {\n resolvedConfig = config\n isDev = config.command === 'serve'\n },\n\n config(config, env) {\n // ── Dev / preview (serve) ──────────────────────────────────────────────\n // Don't bake seo.yaml into the bundle. Redirect the `__DCS_SEO__` define\n // to a browser global that `transformIndexHtml` (below) refreshes from\n // disk on every HTML request — live preview with NO server.restart().\n // The consumer is untouched: it still reads the bare `__DCS_SEO__`\n // identifier, which this define rewrites to `globalThis.__DCS_SEO__`.\n // (VitePress treats `useSEO` as a no-op and bakes SEO via\n // transformPageData, so this only affects the runtime `useSEO` SPA path.)\n if (env.command === 'serve') {\n return {\n define: {\n __DCS_SEO__: 'globalThis.__DCS_SEO__',\n __DCS_PAGES__: 'globalThis.__DCS_PAGES__',\n },\n }\n }\n\n // ── Production build ───────────────────────────────────────────────────\n // Unchanged: read seo.yaml now and bake it into the bundle as a literal.\n const projectRoot = config.root || process.cwd()\n\n // The ROUTE MANIFEST for the runtime re-assert (`installSeoHead`).\n //\n // It is baked here — not read from `pages.yaml` at runtime — so the router\n // hook resolves against the SAME map the build-time emitter walked. The\n // `noindex` list rides along because it is a plugin OPTION, not a file:\n // without it the runtime cannot know a route was baked `noindex, nofollow`\n // and would republish `index, follow` over it (measured live on iron-oak\n // /account and /projects, C-357).\n //\n // Emitted unconditionally, independent of the seo.yaml branch below —\n // `installSeoHead` needs it in BOTH cases, and a bare `__DCS_PAGES__`\n // identifier with no define is a ReferenceError in a consumer bundle.\n // IT MODELS WHAT THE EMITTER WRITES, NOT WHAT pages.yaml DECLARES\n // (C-361 F5). Routes the emitter SKIPS — `exclude` entries and `excluded:`\n // globs — are omitted here too, through the SAME `routeExclusionReason`\n // predicate the emit loop calls. Without that filter a private `/dev-*`\n // route was never baked (correct) yet still runtime-asserted with a\n // canonical and `index, follow` on the first in-app navigation (wrong):\n // the manifest promised the runtime a page the build had deliberately\n // refused to publish. Omitting is chosen over carrying exclude semantics\n // into the browser because it needs no glob engine at runtime and cannot\n // disagree with the emitter by construction.\n const pagesDefine = ((): string => {\n const routes = loadPagesManifest(projectRoot, pagesPath, debug)\n if (!routes) {\n // No manifest ⇒ no per-route emission and no runtime manifest, so a\n // noindex list here is inert everywhere. Not a hard failure (the\n // missing manifest is its own, already-warned condition), but say so\n // rather than let the operator believe the directive landed.\n if (noindex.length > 0) {\n console.warn(\n `[dcs-seo] noindex has ${noindex.length} entr${noindex.length === 1 ? 'y' : 'ies'} ` +\n `but ${pagesPath} is missing or empty — the list matches nothing and is inert.`\n )\n }\n return 'undefined'\n }\n // C-416: a noindex entry naming no page emits NO document, so the\n // directive silently does nothing and the URL stays crawlable. Checked\n // against the FULL manifest, before exclusion, and thrown from here so\n // it cannot be swallowed by the writeBundle emit try/catch.\n assertNoOrphanNoindexEntries(noindex, routes)\n const excludedGlobs = loadExcludedGlobs(projectRoot, pagesPath)\n const emitted = routes.filter((r) =>\n isRouteEmitted(r, { exclude, excludedGlobs })\n )\n assertNoDuplicateRoutePaths(emitted)\n return JSON.stringify({\n routes: emitted.map((r) => ({\n slug: r.slug,\n path: r.path,\n ...(r.title ? { title: r.title } : {}),\n })),\n noindex,\n })\n })()\n\n const loaded = loadSeoConfig(projectRoot, seoPath, debug)\n if (!loaded) {\n return {\n define: {\n __DCS_SEO__: 'undefined',\n __DCS_PAGES__: pagesDefine,\n },\n }\n }\n\n if (debug) {\n console.log(`[dcs-seo] Loaded ${loaded.foundPath}`)\n console.log(`[dcs-seo] Version: ${loaded.config.version}`)\n console.log(`[dcs-seo] Site Name: ${loaded.config.global?.siteName || '(not set)'}`)\n console.log(\n `[dcs-seo] Pages: ${Object.keys(loaded.config.pages ?? {}).join(', ') || '(none)'}`\n )\n }\n\n return {\n define: {\n __DCS_SEO__: JSON.stringify(loaded.config),\n __DCS_PAGES__: pagesDefine,\n },\n }\n },\n\n /**\n * Build-time static `<head>` emitter (opt-in via `emitStaticHtml`).\n *\n * Runs after the bundle is written so the built `index.html` shell exists\n * on disk. For an SPA, Vite emits a single `index.html`; we fan it out to\n * per-route files with route-specific `<head>` content.\n *\n * Fully defensive: any missing/unparseable input logs a warning and\n * no-ops. Never throws (so it can never break a production build).\n */\n async writeBundle() {\n if (!emitStaticHtml) {\n // The whole emit path is gated on emitStaticHtml. If the operator\n // explicitly asked for site files (`emitSiteFiles: true`) while\n // emitStaticHtml is off, that request is silently inert — warn so it's\n // not mistaken for \"robots/sitemap were emitted\".\n if (options.emitSiteFiles === true) {\n console.warn(\n '[dcs-seo] emitSiteFiles:true is ignored because emitStaticHtml is false; ' +\n 'set emitStaticHtml:true to emit sitemap.xml/robots.txt/llms.txt'\n )\n }\n return\n }\n\n // Captured inside the never-break emit below, consumed by the separately\n // guarded body-prerender step after it (so a fabrication/crash there can\n // fail the build loud instead of being swallowed by the emit try/catch).\n let bodyPrerenderCtx:\n | { outDir: string; routes: PageRouteEntry[]; excludedGlobs: string[] }\n | null = null\n\n /** P12 shell measurement (before/after the charset hoist), for the report. */\n let charsetShell: {\n hoisted: boolean\n beforeOffset: number\n beforeHeadroom: number\n beforeOk: boolean\n afterOffset: number\n afterHeadroom: number\n afterOk: boolean\n } | null = null\n\n // Same pattern for the C-334 honesty rails: gathered inside the\n // never-break emit, EXECUTED outside it. An emitter that fails is a\n // degraded build; a rail that fails is a build that must go red — so the\n // rails must not be swallowed by the emitter's defensive catch. (That\n // swallow is precisely how a check becomes vacuously green.)\n let honestyCtx:\n | {\n outDir: string\n projectRoot: string\n routes: PageRouteEntry[]\n siteUrl: string | undefined\n base: string\n headMode: HonestyMode\n headAllow: string[]\n headCheckDescription: boolean\n urlMode: HonestyMode\n urlAllow: string[]\n urlNetwork: boolean\n charsetMode: HonestyMode\n /** `true` when the render pass runs only to feed the P1 rail. */\n renderForHonestyOnly: boolean\n }\n | null = null\n\n try {\n const projectRoot = resolvedConfig?.root || process.cwd()\n const outDir = resolveOutDir(resolvedConfig)\n\n if (!fs.existsSync(outDir)) {\n console.warn(`[dcs-seo] emitStaticHtml: outDir not found (${outDir}); skipping`)\n return\n }\n\n const shellPath = path.join(outDir, 'index.html')\n if (!fs.existsSync(shellPath)) {\n console.warn(\n `[dcs-seo] emitStaticHtml: no index.html in ${outDir}; nothing to emit`\n )\n return\n }\n\n const routes = loadPagesManifest(projectRoot, pagesPath, debug)\n if (!routes) {\n console.warn(\n `[dcs-seo] emitStaticHtml: ${pagesPath} missing or empty; skipping per-route emission`\n )\n return\n }\n\n const loaded = loadSeoConfig(projectRoot, seoPath, debug)\n if (!loaded) {\n // seo.yaml is optional for emission — routes still get baked with\n // global/runtime defaults (which here means just the shell title and\n // a derived canonical-less head). Warn so the operator knows.\n console.warn(\n `[dcs-seo] emitStaticHtml: ${seoPath} missing or unparseable; emitting with defaults only`\n )\n }\n\n // ── P12: hoist <meta charset> before anything is fanned out ─────────\n // The motion-token <style> is injected `head-prepend`, which lands it\n // ABOVE the site's own <meta charset> and pushes the encoding\n // declaration to byte ~910 of the spec's 1024-byte sniffing window\n // (measured identical on two fleet sites, 2026-07-26). Fixing the SHELL\n // fixes every per-route file the loop below writes from it, in one place,\n // with no per-site edit. `hoistCharsetMeta` is idempotent, so a shell\n // that is already correct is untouched.\n const rawShellHtml = fs.readFileSync(shellPath, 'utf8')\n const shellHtml = hoistCharsetMeta(rawShellHtml)\n // Recorded for the honesty report: the BEFORE/AFTER byte offsets are the\n // P12 measurement the fleet matrix asks for, per site.\n {\n const b = checkCharsetBudget(rawShellHtml)\n const a = checkCharsetBudget(shellHtml)\n charsetShell = {\n hoisted: shellHtml !== rawShellHtml,\n beforeOffset: b.offset,\n beforeHeadroom: b.headroom,\n beforeOk: b.ok,\n afterOffset: a.offset,\n afterHeadroom: a.headroom,\n afterOk: a.ok,\n }\n }\n if (shellHtml !== rawShellHtml) {\n // Write it back too: `dist/index.html` is what the host serves as the\n // SPA fallback for every unmatched path, and a route manifest without\n // `/` would otherwise leave the shell un-hoisted.\n fs.writeFileSync(shellPath, shellHtml, 'utf8')\n const before = checkCharsetBudget(rawShellHtml)\n const after = checkCharsetBudget(shellHtml)\n console.log(\n `[dcs-seo] charset budget: hoisted <meta charset> from byte ${before.offset} ` +\n `to byte ${after.offset} (${after.headroom} bytes of the 1024-byte ` +\n `encoding-sniffing window now free)`\n )\n }\n\n // pages.yaml top-level `excluded:` globs gate BOTH the per-route HTML\n // emission below and the sitemap/llms set inside emitSiteFiles, so an\n // excluded route is never written as a crawlable index.html.\n const excludedGlobs = loadExcludedGlobs(projectRoot, pagesPath)\n // Optional: load content.yaml so REAL reviews can feed the honest\n // Review/aggregateRating on the per-route LocalBusiness node. Absent or\n // unparseable ⇒ undefined ⇒ no reviews emitted (no synthesis).\n const contentConfig = loadContentConfig(projectRoot, contentPath, debug)\n\n const written = emitStaticSeoHtml({\n outDir,\n shellHtml,\n routes,\n seoConfig: loaded?.config,\n contentConfig,\n exclude,\n noindex,\n excludedGlobs,\n debug,\n })\n\n if (debug) {\n console.log(`[dcs-seo] emitStaticHtml: wrote ${written} per-route HTML file(s)`)\n }\n\n // ── site-wide static files (sitemap.xml, robots.txt, llms.txt) ──────\n // Emitted AFTER the per-route HTML loop so canonicals/routes are\n // resolved and the shell exists. Reuses the already-loaded routes +\n // seoConfig (no second manifest read). Same outDir/existsSync guard +\n // the outer try/catch — so it can never break a production build.\n if (emitSiteFilesOpt) {\n const siteFiles = emitSiteFiles({\n outDir,\n projectRoot,\n pagesPath,\n routes,\n seoConfig: loaded?.config,\n siteUrl,\n exclude,\n noindex,\n preview,\n robots,\n llms,\n debug,\n })\n if (debug) {\n console.log(\n `[dcs-seo] emitSiteFiles: wrote ${siteFiles.length} site file(s): ${\n siteFiles.join(', ') || '(none)'\n }`\n )\n }\n }\n\n // Body prerender runs only when enabled for this build (default ON when\n // emitting; off via the `prerenderBody` option, the seo.yaml escape\n // hatch, or preview mode). Stash what it needs so it executes under its\n // own fail-loud guard below, NOT this swallow-catch.\n // Extra guard: the prerender server serves the built assets from root,\n // so a non-root `base` (subpath preview deploys like /kept/) would 404\n // every asset → the app never mounts → a spurious build failure. Skip\n // (loudly) rather than fail — head + JSON-LD are already emitted.\n const buildBase = resolvedConfig?.base ?? '/'\n const baseIsRoot = buildBase === '/' || buildBase === './' || buildBase === ''\n const bodyPrerenderOn = isBodyPrerenderEnabled({\n emitStaticHtml,\n prerenderBodyOption: prerenderBody,\n preview,\n seoPrerenderBody: loaded?.config?.prerenderBody,\n })\n if (bodyPrerenderOn) {\n if (baseIsRoot) {\n bodyPrerenderCtx = { outDir, routes, excludedGlobs }\n } else {\n console.warn(\n `[dcs-seo] body prerender skipped: non-root base \"${buildBase}\" ` +\n '(assets would not resolve through the prerender server; head + JSON-LD still emitted)',\n )\n }\n }\n\n // ── C-334 honesty rails: resolve severities, stash the context ───────\n // Resolution order is env > seo.yaml > plugin option > default `error`,\n // so the most operator-visible signal wins and a lowered gate always\n // shows up either in git (seo.yaml) or in the build log (env).\n // `*OptBlock` = the vite.config option; `seo*Block` = the .dcs/seo.yaml\n // escape hatch. Kept in matching pairs so the precedence below reads at a\n // glance and a future edit cannot silently swap the two sources.\n const headOptBlock = readHonestyBlock(headHonestyOpt)\n const urlOptBlock = readHonestyBlock(urlHonestyOpt)\n const charsetOptBlock = readHonestyBlock(charsetBudgetOpt)\n const seoHeadBlock = readHonestyBlock(loaded?.config?.headHonesty ?? undefined)\n const seoUrlBlock = readHonestyBlock(loaded?.config?.urlHonesty ?? undefined)\n const seoCharsetBlock = readHonestyBlock(loaded?.config?.charsetBudget ?? undefined)\n\n const headMode = resolveHonestyMode({\n env: process.env.DCS_SEO_HEAD_HONESTY,\n seoYaml: seoHeadBlock.mode,\n option: headOptBlock.mode,\n fallback: 'error',\n })\n const urlMode = resolveHonestyMode({\n env: process.env.DCS_SEO_URL_HONESTY,\n seoYaml: seoUrlBlock.mode,\n option: urlOptBlock.mode,\n fallback: 'error',\n })\n const charsetMode = resolveHonestyMode({\n env: process.env.DCS_SEO_CHARSET_BUDGET,\n seoYaml: seoCharsetBlock.mode,\n option: charsetOptBlock.mode,\n fallback: 'error',\n })\n\n // The P1 rail needs a render. If body prerender is already running, it\n // rides along for free; if the site opted out of body prerender we still\n // render — in OBSERVE-ONLY mode, so `dist/` is untouched — because the\n // alternative is a gate that quietly does not exist on that site.\n const renderForHonestyOnly = !bodyPrerenderOn && headMode !== 'off' && baseIsRoot\n if (renderForHonestyOnly) {\n bodyPrerenderCtx = { outDir, routes, excludedGlobs }\n }\n if (headMode !== 'off' && !baseIsRoot) {\n console.warn(\n `[dcs-seo] head honesty SKIPPED: non-root base \"${buildBase}\" (the prerender ` +\n 'server cannot serve this build, so the rendered <head> cannot be observed)'\n )\n }\n\n honestyCtx = {\n outDir,\n projectRoot,\n routes,\n siteUrl: siteUrl || loaded?.config?.global?.siteUrl,\n base: buildBase,\n headMode: baseIsRoot ? headMode : 'off',\n headAllow: [...headOptBlock.allow, ...seoHeadBlock.allow],\n headCheckDescription:\n seoHeadBlock.checkDescription ?? headOptBlock.checkDescription ?? true,\n urlMode,\n urlAllow: [...urlOptBlock.allow, ...seoUrlBlock.allow],\n urlNetwork:\n process.env.DCS_SEO_URL_HONESTY_NETWORK?.toLowerCase() === 'off'\n ? false\n : (seoUrlBlock.network ?? urlOptBlock.network ?? true),\n charsetMode,\n renderForHonestyOnly,\n }\n } catch (error) {\n // Never break the build on SEO emission failure.\n console.warn('[dcs-seo] emitStaticHtml failed; build output left unchanged:', error)\n }\n\n // ── Body prerender + P1 (SEPARATE guard — fail loud) ─────────────────────\n // Deliberately OUTSIDE the swallow-catch above: missing infra (no\n // playwright / no browser) degrades gracefully to a no-op, but a route that\n // crashes or would ship broken content throws BodyPrerenderError and FAILS\n // the build. Head + JSON-LD are already on disk regardless.\n let headObservations: HeadObservation[] = []\n // Execution facts for the P1 assertion below. `rendererAvailable` starts\n // FALSE: if the render pass never ran, the rail must say so rather than\n // inherit an optimistic default.\n let rendererAvailable = false\n let eligibleRoutes: string[] = []\n let filteredRoutes: Array<{ route: string; reason: string }> = []\n if (bodyPrerenderCtx) {\n const outcome = await runBodyPrerender(bodyPrerenderCtx, {\n exclude,\n noindex,\n debug,\n spliceBody: !(honestyCtx?.renderForHonestyOnly ?? false),\n })\n headObservations = outcome.headObservations\n rendererAvailable = outcome.rendererAvailable\n eligibleRoutes = outcome.eligibleRoutes\n filteredRoutes = outcome.filteredRoutes\n }\n\n if (honestyCtx) {\n const ctx = honestyCtx\n // Machine-readable execution record (C-338). Written only when\n // DCS_SEO_HONESTY_REPORT names a path, so normal builds are untouched;\n // the fleet candidate-matrix harness sets it and reads counts from JSON\n // instead of scraping the build log. Written in a `finally` so a RED\n // build still leaves its measurement behind.\n const reportPath = process.env.DCS_SEO_HONESTY_REPORT\n const report: Record<string, unknown> = {\n schema: 1,\n generatedAt: new Date().toISOString(),\n projectRoot: ctx.projectRoot,\n outDir: ctx.outDir,\n siteUrl: ctx.siteUrl ?? null,\n base: ctx.base,\n routes: ctx.routes.length,\n p1: {\n mode: ctx.headMode,\n rendererAvailable,\n eligible: eligibleRoutes.length,\n observed: headObservations.length,\n unobserved: eligibleRoutes.filter(\n (r) => !headObservations.some((o) => o.route === r)\n ),\n filtered: filteredRoutes,\n exempted: ctx.headAllow.length,\n divergent: null as number | null,\n executed: false,\n },\n p12: { mode: ctx.charsetMode, shell: charsetShell, checked: 0, failed: 0, worstOffset: 0 },\n p2: { mode: ctx.urlMode, network: ctx.urlNetwork, audit: null as unknown },\n }\n const p1 = report.p1 as Record<string, unknown>\n try {\n // P1 — baked <head> == rendered <head>, per route. The EXECUTION\n // assertion runs first: in `error` mode a missing renderer, zero\n // eligible routes, or an eligible route with no observation is a hard\n // failure. C-334 warned and skipped here, which is how an\n // error-severity rail passed a build having checked nothing (C-338).\n const shouldCompare = assertHeadHonestyExecuted(\n {\n rendererAvailable,\n eligibleRoutes,\n observedRoutes: headObservations.map((o) => o.route),\n filteredRoutes,\n totalRoutes: ctx.routes.length,\n },\n { mode: ctx.headMode }\n )\n p1.executed = shouldCompare\n if (shouldCompare || ctx.headMode === 'off') {\n // In `error` mode this throws — record the divergences FIRST so a red\n // build still leaves the operator the list of what diverged.\n const found = findHeadHonestyViolations(headObservations, {\n allow: ctx.headAllow,\n checkDescription: ctx.headCheckDescription,\n })\n p1.divergent = found.length\n p1.violations = found\n assertHeadHonesty(headObservations, {\n mode: ctx.headMode,\n allow: ctx.headAllow,\n checkDescription: ctx.headCheckDescription,\n debug,\n })\n }\n\n // P12 — the encoding declaration is inside the sniffing window.\n runCharsetBudget({\n outDir: ctx.outDir,\n routes: ctx.routes,\n mode: ctx.charsetMode,\n debug,\n sink: (s) => Object.assign(report.p12 as object, s),\n })\n\n // P2 — every published URL resolves to what it promises. Runs LAST\n // because it is the only rail that can touch the network.\n await runEmittedUrlHonesty({\n outDir: ctx.outDir,\n projectRoot: ctx.projectRoot,\n routes: ctx.routes,\n siteUrl: ctx.siteUrl,\n base: ctx.base,\n mode: ctx.urlMode,\n allow: ctx.urlAllow,\n network: ctx.urlNetwork,\n debug,\n sink: (a, v) => {\n const p2 = report.p2 as Record<string, unknown>\n p2.audit = a\n p2.violations = v\n },\n })\n } finally {\n if (reportPath) {\n try {\n fs.mkdirSync(path.dirname(path.resolve(reportPath)), { recursive: true })\n fs.writeFileSync(path.resolve(reportPath), JSON.stringify(report, null, 2), 'utf8')\n console.log(`[dcs-seo] honesty report written: ${path.resolve(reportPath)}`)\n } catch (err) {\n console.warn('[dcs-seo] could not write honesty report:', err)\n }\n }\n }\n }\n },\n\n /**\n * Dev-only: inject the FRESH on-disk `seo.yaml` as an inline\n * `globalThis.__DCS_SEO__ = {…}` classic script at the top of `<head>`,\n * mirroring dcsContentPlugin. Lets the runtime `useSEO` SPA path reflect\n * on-disk SEO edits on the portal's `?_hc` hard reload with no restart.\n * Gated on `isDev` so it never runs in `vite`/`vitepress build` (the build\n * path keeps the baked `define` literal + the `writeBundle` emitter).\n *\n * `__DCS_PAGES__` (the `installSeoHead` route manifest) rides the same\n * refresh. It is injected INDEPENDENTLY of seo.yaml: a site can have a\n * pages.yaml and no seo.yaml, and returning early on a missing seo.yaml\n * would leave the router hook manifest-less (and therefore inert) in dev\n * only — a dev/prod behaviour split is exactly the kind of divergence this\n * plan exists to stop.\n */\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n if (!isDev) return html\n\n const projectRoot = resolvedConfig?.root || process.cwd()\n const tags: HtmlTagDescriptor[] = []\n\n const config = readDcsYamlFresh<SeoConfiguration>(projectRoot, seoPath, 'dcs-seo', debug)\n // No file (or unparseable) ⇒ leave the global unset; the consumer's\n // `typeof globalThis.__DCS_SEO__` check falls back to defaults.\n if (config !== undefined) {\n if (debug) {\n console.log('[dcs-seo] serve: injecting fresh __DCS_SEO__ into <head>')\n }\n tags.push({\n tag: 'script',\n children: buildGlobalAssignScript('__DCS_SEO__', config),\n injectTo: 'head-prepend',\n })\n }\n\n const routes = loadPagesManifest(projectRoot, pagesPath, debug)\n if (routes) {\n // SAME exclusion filter as the build define above. A dev-only manifest\n // that carried excluded routes would make `installSeoHead` behave\n // differently in dev than in prod — the dev/prod split this whole\n // inject exists to avoid.\n const excludedGlobs = loadExcludedGlobs(projectRoot, pagesPath)\n const emitted = routes.filter((r) => isRouteEmitted(r, { exclude, excludedGlobs }))\n // Dev REPORTS the colliding manifest; the BUILD is the gate that\n // refuses it. Throwing from `transformIndexHtml` would replace a\n // precise message with a blank dev-server page.\n const dupes = findDuplicateNormalizedPaths(emitted)\n if (dupes.length > 0) {\n console.error(\n `[dcs-seo] serve: ${formatDuplicateNormalizedPaths(dupes)}\\n` +\n `[dcs-seo] serve: this is reported here and FAILS the production build.`\n )\n }\n if (debug) {\n console.log(\n `[dcs-seo] serve: injecting fresh __DCS_PAGES__ (${emitted.length} of ` +\n `${routes.length} route(s), excluded ones omitted) into <head>`\n )\n }\n tags.push({\n tag: 'script',\n children: buildGlobalAssignScript('__DCS_PAGES__', {\n routes: emitted.map((r) => ({\n slug: r.slug,\n path: r.path,\n ...(r.title ? { title: r.title } : {}),\n })),\n noindex,\n }),\n injectTo: 'head-prepend',\n })\n }\n\n if (tags.length === 0) return html\n return { html, tags }\n },\n },\n\n // Watch for changes in development.\n //\n // Downgraded from `server.restart()` to a debounced client `full-reload`:\n // freshness already comes from `transformIndexHtml` re-reading the YAML on\n // every HTML request, so all we need on change is a browser reload. A\n // `server.restart()` would rebuild VitePress's dev MiniSearch index and can\n // throw \"server restart failed\" on a duplicate heading id — the fragility\n // this fix removes. A `full-reload` does neither.\n configureServer(server) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n\n const watchPaths = [\n path.resolve(projectRoot, seoPath),\n path.resolve(projectRoot, '..', seoPath),\n ].map((p) => path.normalize(p))\n\n watchPaths.forEach((watchPath) => {\n if (fs.existsSync(watchPath)) {\n server.watcher.add(watchPath)\n }\n })\n\n let reloadTimer: ReturnType<typeof setTimeout> | undefined\n server.watcher.on('change', (changedPath) => {\n if (!watchPaths.includes(path.normalize(changedPath))) return\n if (reloadTimer) clearTimeout(reloadTimer)\n reloadTimer = setTimeout(() => {\n if (debug) {\n console.log('[dcs-seo] seo.yaml changed, sending full-reload')\n }\n server.ws.send({ type: 'full-reload' })\n }, 100)\n })\n },\n }\n}\n","/**\n * DCS Editor Plugin for Vite\n *\n * Injects the editor bridge script into customer sites when running\n * in portal preview mode (inside the visual editor iframe).\n *\n * The bridge enables:\n * - Inline text editing via contenteditable\n * - Section hover highlights and AI ✨ buttons\n * - postMessage communication with the portal\n *\n * This plugin should only be active in development/preview mode.\n * It's safe to include in production builds — it does nothing unless\n * the page detects it's inside an iframe.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { dcsEditorPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsEditorPlugin()\n * ]\n * })\n * ```\n */\n\nimport type { Plugin } from 'vite'\n\nexport interface DcsEditorPluginOptions {\n /** Enable debug logging */\n debug?: boolean\n}\n\n/**\n * Vite plugin that injects the editor bridge script for portal preview integration.\n *\n * The bridge auto-initializes only when the page detects it's running inside an iframe,\n * so it's safe to include in all builds — it's a no-op in standalone browsing.\n *\n * Uses a virtual module (`/__dcs-editor-bridge.js`) served through Vite's dev server\n * so that bare module specifiers (like `@duffcloudservices/cms/editor`) are properly\n * resolved through Vite's module graph rather than hitting the browser's native ESM\n * resolver, which can't handle bare specifiers.\n */\nexport function dcsEditorPlugin(options: DcsEditorPluginOptions = {}): Plugin {\n const { debug = false } = options\n const VIRTUAL_PATH = '/__dcs-editor-bridge.js'\n const RIBBON_VIRTUAL_PATH = '/__dcs-preview-ribbon.js'\n\n // Captured during configResolved — used to gate HTML injection.\n let isDev = false\n let basePath = '/'\n let isPreview = false\n\n return {\n name: 'dcs-editor-bridge',\n\n // Preview only: stop Vite running an unreachable HMR WebSocket server.\n // Freshness comes from the cms content/seo plugins (fresh transformIndexHtml\n // re-read) + the portal's ?_hc hard reload, not HMR. NOTE: this alone does\n // NOT silence the browser — Vite still injects @vite/client and calls\n // connect(); the client-side stub in transformIndexHtml is what removes the\n // console errors. A normal local `vite dev` (no DCS_PREVIEW) keeps full HMR.\n config(_config, env) {\n if (env.command === 'serve' && process.env.DCS_PREVIEW === 'true') {\n return { server: { hmr: false } }\n }\n },\n\n configResolved(config) {\n isDev = config.command === 'serve'\n basePath = config.base ?? '/'\n isPreview = process.env.DCS_PREVIEW === 'true'\n },\n\n resolveId(id) {\n if (id === VIRTUAL_PATH || id === RIBBON_VIRTUAL_PATH) {\n return id\n }\n },\n\n load(id) {\n if (id === VIRTUAL_PATH) {\n return `\nimport { initEditorBridge } from '@duffcloudservices/cms/editor'\n// The bridge only activates inside an iframe (portal preview)\nif (window.parent !== window) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => setTimeout(initEditorBridge, 200))\n } else {\n setTimeout(initEditorBridge, 200)\n }\n}\n`\n }\n\n if (id === RIBBON_VIRTUAL_PATH) {\n return `\nimport { createApp } from 'vue'\nimport PreviewRibbon from '@duffcloudservices/cms/components'\n\nfunction mountRibbon() {\n // Create a detached mount point\n const el = document.createElement('div')\n el.id = 'dcs-preview-ribbon-root'\n document.body.appendChild(el)\n\n const app = createApp(PreviewRibbon)\n app.mount(el)\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', mountRibbon)\n} else {\n mountRibbon()\n}\n`\n }\n },\n\n /**\n * Only inject the editor bridge and ribbon scripts when:\n * 1. Running in dev mode (Vite dev server), AND\n * 2. The app is served at the root path (base === '/')\n *\n * These are virtual modules resolved via absolute paths (e.g.\n * `/__dcs-editor-bridge.js`). When base !== '/', the app is served at a\n * sub-path (e.g. /iron-oak-contractors/) and the absolute virtual paths are NOT\n * routed to the Vite dev server by the reverse proxy — they hit the main\n * DCS server instead and get redirected to portal.duffcloudservices.com\n * (no CORS headers), causing browser console errors.\n *\n * In production builds, App.vue imports and mounts PreviewRibbon\n * directly, so virtual script injection is always redundant anyway.\n */\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n if (!isDev || basePath !== '/') return html\n\n if (debug) {\n console.log('[dcs-editor] Injecting editor bridge + preview ribbon script tags')\n }\n\n // Inject both the editor bridge and preview ribbon scripts\n const editorTag = `<script type=\"module\" src=\"${VIRTUAL_PATH}\"></script>`\n const ribbonTag = `<script type=\"module\" src=\"${RIBBON_VIRTUAL_PATH}\"></script>`\n const bodyHtml = html.replace('</body>', `${editorTag}\\n${ribbonTag}\\n</body>`)\n\n if (!isPreview) return bodyHtml\n\n // Preview only: neutralise Vite's HMR WebSocket. Vite always injects\n // @vite/client and unconditionally calls transport.connect(); behind the\n // preview proxy chain there is no reachable HMR WS, so it logs\n // WebSocket connection to 'wss://<portal>/?token=…' failed\n // WebSocket connection to 'wss://localhost:<port>/?token=…' failed\n // [vite] failed to connect to websocket\n // server.hmr:false does NOT stop this (the client is still injected). A\n // classic <script> in <head> runs during parse, BEFORE the deferred\n // @vite/client module, so returning an inert socket for the 'vite-hmr'\n // subprotocol silences the attempt with zero effect on app/runtime\n // WebSockets. No-op outside preview / in production builds.\n const wsStub =\n `(function(){try{var N=window.WebSocket;if(!N)return;` +\n `function hmr(p){return p==='vite-hmr'||(Array.isArray(p)&&p.indexOf('vite-hmr')!==-1)}` +\n `function Inert(){this.readyState=0}` +\n `Inert.prototype.send=function(){};Inert.prototype.close=function(){};` +\n `Inert.prototype.addEventListener=function(){};Inert.prototype.removeEventListener=function(){};` +\n `var W=function(u,p){if(hmr(p))return new Inert();return new N(u,p)};` +\n `W.prototype=N.prototype;W.CONNECTING=N.CONNECTING;W.OPEN=N.OPEN;W.CLOSING=N.CLOSING;W.CLOSED=N.CLOSED;` +\n `window.WebSocket=W}catch(e){}})()`\n\n return {\n html: bodyHtml,\n tags: [{ tag: 'script', children: wsStub, injectTo: 'head-prepend' }],\n }\n },\n },\n }\n}\n","/**\n * DCS Preview Plugin for Vue\n *\n * Registers a supplied ribbon component as a global `DcsPreviewRibbon`\n * component. The ribbon handles its own visibility — it only renders on\n * `preview.duffcloudservices.com` and hides everywhere else (localhost,\n * production domains, and inside the visual page editor iframe).\n *\n * Why does the caller pass the component in? Because this file is compiled\n * by tsup (esbuild) which has no `.vue` SFC loader. Keeping the raw `.vue`\n * import out of the compiled plugins bundle avoids the build error while\n * still letting consumer code (which *does* run through Vite) resolve the\n * SFC at dev/build time.\n *\n * @example VitePress theme/index.ts\n * ```typescript\n * import { dcsPreviewPlugin } from '@duffcloudservices/cms/plugins'\n * import PreviewRibbon from '@duffcloudservices/cms/components'\n *\n * export default {\n * Layout,\n * enhanceApp({ app }) {\n * app.use(dcsPreviewPlugin(PreviewRibbon))\n * }\n * }\n * ```\n */\n\nimport { defineComponent, h, type App, type Component, type Plugin } from 'vue'\n\nexport interface DcsPreviewPluginOptions {\n /**\n * Override the version string displayed on the ribbon.\n * If omitted, the ribbon auto-detects from VITE_SITE_VERSION or the API.\n */\n version?: string | null\n}\n\n/**\n * Creates and returns the DCS Preview plugin.\n *\n * When installed, it registers the supplied ribbon component as a global\n * `DcsPreviewRibbon` component. Add `<DcsPreviewRibbon />` to your root\n * Layout, or use the `dcsEditorPlugin` Vite plugin which injects it via\n * `transformIndexHtml`.\n *\n * @param ribbonComponent - The PreviewRibbon SFC (imported by the consumer)\n * @param options - Optional configuration\n */\nexport function dcsPreviewPlugin(\n ribbonComponent: Component,\n options: DcsPreviewPluginOptions = {},\n): Plugin {\n const Wrapper = defineComponent({\n name: 'DcsPreviewRibbon',\n setup() {\n return () => h(ribbonComponent, { version: options.version ?? null })\n },\n })\n\n return {\n install(app: App) {\n app.component('DcsPreviewRibbon', Wrapper)\n\n if (options.version !== undefined) {\n app.provide('__dcs_preview_version__', options.version)\n }\n },\n }\n}\n","/**\n * DCS CDN Image Plugin for Vite\n *\n * Rewrites local static image references (e.g. `/images/staff/photo.jpg`)\n * to CDN URLs at build time using the `.dcs/cdn-image-map.json` mapping file\n * generated by the `image-migrate adopt` CLI command.\n *\n * For raster images with WebP variants, `<img>` elements are transformed into\n * responsive `<picture>` elements with `srcset` for optimised delivery.\n * SVGs receive a simple URL swap with no variant handling.\n *\n * **In development mode this plugin is a no-op** — local `/images/` paths\n * continue to work via Vite's static asset serving so hot-reload is unaffected.\n *\n * The plugin handles two in-pipeline replacement vectors:\n *\n * 1. **Module transform** (`transform` hook) — rewrites `<img>` tags and\n * string literals in Vue SFCs, TS, JS, CSS, MD, and HTML modules.\n * 2. **Chunk rendering** (`renderChunk` hook) — rewrites image paths in\n * final rendered JS/CSS chunks AFTER Vite's `define` substitution.\n * This catches data injected by `dcsContentPlugin` via `__DCS_CONTENT__`\n * (from `.dcs/content.yaml`) which bypasses the `transform` hook.\n *\n * A third vector — **post-build file processing** — is handled by the\n * companion `dcsCdnBuildEnd` hook, which must be registered separately in\n * VitePress config. VitePress generates HTML *after* both Vite builds\n * complete, so Vite plugin hooks (`closeBundle`, `transformIndexHtml`)\n * cannot catch SSR-rendered `<head>` tags or static `public/` files like\n * `service-worker.js`.\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { dcsCdnImagePlugin, dcsCdnBuildEnd } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * vite: {\n * plugins: [\n * dcsCdnImagePlugin()\n * ]\n * },\n * buildEnd: dcsCdnBuildEnd()\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport type { Plugin } from 'vite'\n\n// ────────────────────────────────────────────────────────────────────────────\n// Types\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface CdnImageMapVariant {\n suffix: string\n blobPath: string\n cdnUrl: string\n width: number\n height: number\n size: number\n}\n\nexport interface CdnImageMapEntry {\n localPath: string\n blobPath: string\n cdnUrl: string\n fileName: string\n contentType: string\n originalSize: number\n width?: number\n height?: number\n hash: string\n variants?: CdnImageMapVariant[]\n}\n\nexport interface CdnImageMap {\n generated: string\n site: string\n cdnBase: string\n images: CdnImageMapEntry[]\n}\n\nexport interface DcsCdnImagePluginOptions {\n /** Path to cdn-image-map.json relative to project root (default: '.dcs/cdn-image-map.json') */\n mapPath?: string\n\n /**\n * Patterns to match for replacement. Each must be a **leading-slash path prefix**\n * that appears in source code (e.g. `/images/`). The `localPath` field in the\n * mapping file is compared *without* a leading slash.\n *\n * Default: `['/images/']`\n */\n pathPrefixes?: string[]\n\n /**\n * Default `sizes` attribute for responsive `<picture>` elements.\n * Override per-context via the `data-sizes` attribute on the original `<img>`.\n *\n * Default: `'(max-width: 1024px) 100vw, 1024px'`\n */\n defaultSizes?: string\n\n /** Enable debug logging */\n debug?: boolean\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ────────────────────────────────────────────────────────────────────────────\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\n/**\n * Build the ` width=\"W\" height=\"H\"` attribute fragment for a map entry — the\n * CLS layout-shift hint (Phase 1). Dimensions are read from the entry's\n * intrinsic `width`/`height`; they are aspect-ratio hints, so emitting the\n * source dimensions on an `<img>` whose `src` is a resized variant is correct\n * (the ratio is scale-invariant and CSS still controls the rendered size).\n *\n * Emitted ONLY when both are known and strictly positive. Older image-map\n * entries carry no top-level dimensions (and variant `height` is often `0`) —\n * in that case NO hint is emitted rather than a fabricated/wrong ratio.\n */\nfunction dimensionAttrs(entry: CdnImageMapEntry): string {\n const w = entry.width\n const h = entry.height\n if (typeof w === 'number' && w > 0 && typeof h === 'number' && h > 0) {\n return ` width=\"${w}\" height=\"${h}\"`\n }\n return ''\n}\n\n/**\n * Build a responsive `<picture>` element for a raster image that has\n * WebP variants.\n */\nfunction buildPictureElement(\n entry: CdnImageMapEntry,\n alt: string,\n extraAttrs: string,\n sizes: string\n): string {\n const variants = entry.variants ?? []\n const dims = dimensionAttrs(entry)\n\n // Collect only the display-relevant variants (not thumb, not og)\n const displayVariants = variants.filter(\n (v) => v.suffix === '-sm' || v.suffix === '-md' || v.suffix === '-lg'\n )\n\n if (displayVariants.length === 0) {\n // No display variants — fall back to original CDN URL as plain <img>\n return `<img src=\"${escapeHtml(entry.cdnUrl)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs}${dims} loading=\"lazy\" decoding=\"async\" />`\n }\n\n const srcset = displayVariants\n .map((v) => `${v.cdnUrl} ${v.width}w`)\n .join(', ')\n\n // Pick the -md variant as the default src, or fall through to original\n const mdVariant = variants.find((v) => v.suffix === '-md')\n const fallbackSrc = mdVariant ? mdVariant.cdnUrl : entry.cdnUrl\n\n return [\n '<picture>',\n ` <source srcset=\"${srcset}\" type=\"image/webp\" sizes=\"${escapeHtml(sizes)}\" />`,\n ` <img src=\"${escapeHtml(fallbackSrc)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs}${dims} loading=\"lazy\" decoding=\"async\" />`,\n '</picture>',\n ].join('\\n')\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Shared helpers\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Try to find and load the CDN image map from common locations.\n * VitePress nests docs under a subfolder, so we check parent dirs too.\n */\nfunction loadCdnImageMap(\n projectRoot: string,\n relativeMapPath: string,\n debug: boolean\n): Map<string, CdnImageMapEntry> | null {\n const possiblePaths = [\n path.resolve(projectRoot, relativeMapPath),\n path.resolve(projectRoot, '..', relativeMapPath),\n path.resolve(process.cwd(), relativeMapPath),\n ]\n\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n try {\n const raw = fs.readFileSync(testPath, 'utf8')\n const data: CdnImageMap = JSON.parse(raw)\n\n const map = new Map<string, CdnImageMapEntry>()\n for (const entry of data.images) {\n // Store both with and without leading slash for flexible matching\n map.set(entry.localPath, entry)\n if (!entry.localPath.startsWith('/')) {\n map.set('/' + entry.localPath, entry)\n }\n }\n\n if (debug) {\n console.log(`[dcs-cdn-image] Loaded ${data.images.length} entries from ${testPath}`)\n }\n return map\n } catch (error) {\n console.warn(`[dcs-cdn-image] Failed to parse ${testPath}:`, error)\n }\n }\n }\n\n if (debug) {\n console.log('[dcs-cdn-image] No cdn-image-map.json found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n }\n return null\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Vite Plugin\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Vite plugin that rewrites static `/images/` paths to CDN URLs at build time.\n */\nexport function dcsCdnImagePlugin(options: DcsCdnImagePluginOptions = {}): Plugin {\n const {\n mapPath = '.dcs/cdn-image-map.json',\n pathPrefixes = ['/images/'],\n defaultSizes = '(max-width: 1024px) 100vw, 1024px',\n debug = false,\n } = options\n\n // Lookup map built from the JSON file: key = localPath (no leading slash)\n let imageMap: Map<string, CdnImageMapEntry> | null = null\n let isProduction = false\n\n /**\n * Check whether a source code string contains any of the path prefixes\n * we care about. Quick bail-out for files that don't reference images.\n */\n function hasImageReferences(code: string): boolean {\n return pathPrefixes.some((prefix) => code.includes(prefix))\n }\n\n /**\n * Replace image path references in any string value with CDN URLs.\n * Used for code transforms and renderChunk post-processing.\n */\n function replaceImagePaths(str: string): string {\n if (!imageMap || imageMap.size === 0) return str\n let result = str\n for (const prefix of pathPrefixes) {\n // Global replace: matches prefix followed by a relative path bounded\n // by quotes, parens, or whitespace. Allows spaces in filenames.\n const regex = new RegExp(\n `([\"'\\`(])${escapeRegex(prefix)}([^\"'\\`()]+)`,\n 'g'\n )\n result = result.replace(regex, (match, quote, relPath) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n return `${quote}${entry.cdnUrl}`\n })\n }\n return result\n }\n\n /**\n * Replace standalone URL references in JS/TS string literals and Vue template\n * attributes. This handles patterns like:\n * src=\"/images/foo.jpg\"\n * { src: '/images/foo.jpg' }\n * url(/images/foo.svg)\n *\n * For `<img>` tags with raster images that have variants, the entire tag is\n * replaced with a `<picture>` element.\n */\n function transformCode(code: string): string {\n if (!imageMap || imageMap.size === 0) return code\n\n // ── Pass 1: Replace <img> tags that reference mapped raster images ──────\n // Matches: <img ... src=\"/images/foo.jpg\" ... /> or <img ... src=\"/images/foo.jpg\" ... >\n const imgTagRegex = /<img\\b([^>]*?)src=[\"'](\\/(images\\/[^\"']+))[\"']([^>]*?)\\/?>/gi\n code = code.replace(imgTagRegex, (_match, beforeSrc, _srcWithSlash, localPath, afterSrc) => {\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return _match\n\n // Extract alt from the existing attributes\n const altMatch = (beforeSrc + afterSrc).match(/alt=[\"']([^\"']*)[\"']/)\n const alt = altMatch ? altMatch[1] : ''\n\n // Collect other attributes (not src, alt, loading, decoding — we set those)\n let otherAttrs = (beforeSrc + afterSrc)\n .replace(/\\balt=[\"'][^\"']*[\"']/gi, '')\n .replace(/\\bloading=[\"'][^\"']*[\"']/gi, '')\n .replace(/\\bdecoding=[\"'][^\"']*[\"']/gi, '')\n .trim()\n\n // When the map entry carries intrinsic dimensions, dimensionAttrs() will\n // emit width/height — strip any hand-authored ones (quoted or bare) so the\n // output doesn't carry DUPLICATE width/height attributes (a Vue compiler\n // duplicate-attribute error). Without entry dims, keep the author's.\n if (dimensionAttrs(entry)) {\n otherAttrs = otherAttrs\n .replace(/\\b(?:width|height)=(?:\"[^\"]*\"|'[^']*'|[^\\s\"'>/]+)/gi, '')\n .replace(/\\s{2,}/g, ' ')\n .trim()\n }\n\n const extraAttrs = otherAttrs ? ' ' + otherAttrs : ''\n\n // Extract custom sizes if specified\n const sizesMatch = (beforeSrc + afterSrc).match(/data-sizes=[\"']([^\"']*)[\"']/)\n const sizes = sizesMatch ? sizesMatch[1] : defaultSizes\n\n // SVGs and images without variants → simple URL swap <img>\n if (!entry.variants || entry.variants.length === 0) {\n return `<img src=\"${escapeHtml(entry.cdnUrl)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs}${dimensionAttrs(entry)} loading=\"lazy\" decoding=\"async\" />`\n }\n\n // Raster with variants → <picture>\n return buildPictureElement(entry, alt, extraAttrs, sizes)\n })\n\n // ── Pass 2: Replace remaining string literal / CSS url() references ─────\n code = replaceImagePaths(code)\n\n return code\n }\n\n return {\n name: 'dcs-cdn-image',\n enforce: 'pre',\n\n configResolved(config) {\n isProduction = config.command === 'build'\n if (!isProduction) {\n if (debug) {\n console.log('[dcs-cdn-image] Dev mode — plugin disabled (local images served by Vite)')\n }\n return\n }\n imageMap = loadCdnImageMap(config.root, mapPath, debug)\n },\n\n transform(code, id) {\n // Only run in production builds\n if (!isProduction || !imageMap) return\n\n // Only process relevant file types\n if (!/\\.(vue|ts|tsx|js|jsx|css|scss|md|html)(\\?.*)?$/.test(id)) return\n\n // Quick bail — skip files with no image path references\n if (!hasImageReferences(code)) return\n\n const transformed = transformCode(code)\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n\n /**\n * Replace image paths in final rendered JS/CSS chunks.\n *\n * This fires AFTER Vite's `define` substitution, catching paths that were\n * injected via `define` values (e.g. `__DCS_CONTENT__` set by\n * `dcsContentPlugin` from `.dcs/content.yaml`). Those paths bypass the\n * `transform` hook because `define` replacement happens at bundle time.\n */\n renderChunk(code, _chunk) {\n if (!isProduction || !imageMap) return null\n if (!hasImageReferences(code)) return null\n\n const replaced = replaceImagePaths(code)\n if (replaced !== code) {\n if (debug) {\n console.log(`[dcs-cdn-image] renderChunk: rewrote image paths in ${_chunk.fileName}`)\n }\n return { code: replaced, map: null }\n }\n return null\n },\n\n /**\n * Transform HTML output to catch references injected outside the module\n * pipeline — e.g. VitePress `head` config tags (favicons, OG images).\n *\n * Note: In VitePress, this hook fires for the `index.html` template but\n * NOT for SSR-rendered per-page HTML. Use `dcsCdnBuildEnd` for full\n * coverage of generated HTML files.\n */\n transformIndexHtml(html) {\n if (!isProduction || !imageMap) return html\n if (!hasImageReferences(html)) return html\n\n // Replace href/src/content attributes that reference mapped images\n for (const prefix of pathPrefixes) {\n const attrRegex = new RegExp(\n `((?:href|src|content)=[\"'])${escapeRegex(prefix)}([^\"']+)([\"'])`,\n 'gi'\n )\n html = html.replace(attrRegex, (match, pre, relPath, post) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n return `${pre}${entry.cdnUrl}${post}`\n })\n }\n\n return html\n },\n }\n}\n\n/**\n * Escape a string for use in a RegExp.\n */\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// VitePress buildEnd hook\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface DcsCdnBuildEndOptions {\n /** Path to cdn-image-map.json relative to project root (default: '.dcs/cdn-image-map.json') */\n mapPath?: string\n\n /**\n * Patterns to match for replacement.\n * Default: `['/images/']`\n */\n pathPrefixes?: string[]\n\n /** Enable debug logging */\n debug?: boolean\n\n /**\n * File extensions to process in the output directory.\n * Default: `['.html', '.js']`\n */\n extensions?: string[]\n}\n\n/**\n * VitePress `buildEnd` hook factory that post-processes generated HTML and\n * static files in the output directory to replace remaining `/images/` paths\n * with CDN URLs.\n *\n * VitePress generates HTML **after** both Vite builds complete, so Vite\n * plugin hooks (`closeBundle`, `transformIndexHtml`) cannot catch\n * SSR-rendered `<head>` tags (favicons, OG images). This hook runs after all\n * HTML files are written to disk.\n *\n * Also rewrites static files (e.g. `service-worker.js`) copied from\n * `public/` that are not part of Vite's module pipeline.\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { dcsCdnBuildEnd } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * buildEnd: dcsCdnBuildEnd({ debug: true })\n * })\n * ```\n */\nexport function dcsCdnBuildEnd(options: DcsCdnBuildEndOptions = {}) {\n const {\n mapPath = '.dcs/cdn-image-map.json',\n pathPrefixes = ['/images/'],\n debug = false,\n extensions = ['.html', '.js'],\n } = options\n\n return async (siteConfig: { root: string; outDir: string }) => {\n const imageMap = loadCdnImageMap(siteConfig.root, mapPath, debug)\n if (!imageMap) {\n if (debug) {\n console.log('[dcs-cdn-image] buildEnd: no image map found')\n }\n return\n }\n\n const outDir = siteConfig.outDir\n if (!fs.existsSync(outDir)) return\n\n let filesProcessed = 0\n let refsReplaced = 0\n\n function walkDir(dir: string) {\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n walkDir(fullPath)\n } else if (extensions.some((ext) => entry.name.endsWith(ext))) {\n processFile(fullPath)\n }\n }\n }\n\n function processFile(filePath: string) {\n let content = fs.readFileSync(filePath, 'utf8')\n if (!pathPrefixes.some((p) => content.includes(p))) return\n\n let changed = false\n\n if (filePath.endsWith('.html')) {\n // HTML: replace href/src/content attribute values\n for (const prefix of pathPrefixes) {\n const attrRegex = new RegExp(\n `((?:href|src|content)=[\"'])${escapeRegex(prefix)}([^\"']+)([\"'])`,\n 'gi'\n )\n content = content.replace(attrRegex, (match, pre, relPath, post) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const mapEntry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!mapEntry) return match\n changed = true\n refsReplaced++\n return `${pre}${mapEntry.cdnUrl}${post}`\n })\n }\n } else {\n // JS/other: replace quoted string paths\n for (const prefix of pathPrefixes) {\n const regex = new RegExp(\n `([\"'\\`(])${escapeRegex(prefix)}([^\"'\\`()]+)`,\n 'g'\n )\n content = content.replace(regex, (match, quote, relPath) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n changed = true\n refsReplaced++\n return `${quote}${entry.cdnUrl}`\n })\n }\n }\n\n if (changed) {\n fs.writeFileSync(filePath, content, 'utf8')\n filesProcessed++\n }\n }\n\n walkDir(outDir)\n\n if (debug) {\n console.log(\n `[dcs-cdn-image] buildEnd: post-processed ${filesProcessed} files, replaced ${refsReplaced} references`\n )\n }\n }\n}","/**\n * markdown-it plugin that transforms standard `![alt](url)` image syntax\n * into responsive `<picture>` elements when the URL matches the DCS CDN\n * asset pattern.\n *\n * Non-CDN images are rendered with the default image renderer (plain `<img>`).\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { responsiveImagePlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * markdown: {\n * config: (md) => {\n * md.use(responsiveImagePlugin)\n * },\n * },\n * })\n * ```\n *\n * Input markdown:\n * ```md\n * ![Physical therapy session](https://files.duffcloudservices.com/kept/assets/blog/abc-123.jpg)\n * ```\n *\n * Rendered HTML:\n * ```html\n * <picture>\n * <source srcset=\"...abc-123-sm.webp 640w, ...abc-123-md.webp 1024w, ...abc-123-lg.webp 1920w\"\n * type=\"image/webp\"\n * sizes=\"(max-width: 1024px) 100vw, 1024px\" />\n * <img src=\"...abc-123-md.webp\" alt=\"Physical therapy session\" loading=\"lazy\" decoding=\"async\" />\n * </picture>\n * ```\n */\n\nimport type MarkdownIt from 'markdown-it'\n\n/**\n * Matches DCS CDN asset URLs: base path / UUID . extension.\n *\n * Aligned with `@duffcloudservices/cms-core` `responsive-image.ts`: the\n * `(?:content\\/)?` group covers the content-container shape\n * `https://files.duffcloudservices.com/content/{slug}/assets/...` in addition\n * to the branding-container shape `.../{slug}/assets/...`. The group is\n * non-capturing, so the captured base path / UUID / extension are unchanged.\n */\nconst CDN_PATTERN = /^(https?:\\/\\/files\\.[^/]+\\/(?:content\\/)?[^/]+\\/assets\\/(?:[^/]+\\/)*)([a-f0-9-]+)\\.(\\w+)$/\n\n/**\n * Escapes HTML special characters for safe attribute embedding.\n * Falls back to a simple replace chain when `md.utils.escapeHtml` is unavailable.\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\nexport function responsiveImagePlugin(md: MarkdownIt): void {\n const defaultImageRenderer = md.renderer.rules.image\n\n md.renderer.rules.image = (tokens, idx, options, env, self) => {\n const token = tokens[idx]\n const src = token.attrGet('src') ?? ''\n const alt = token.content ?? ''\n\n const match = src.match(CDN_PATTERN)\n if (!match) {\n // Not a CDN image — render normally\n if (defaultImageRenderer) {\n return defaultImageRenderer(tokens, idx, options, env, self)\n }\n return self.renderToken(tokens, idx, options)\n }\n\n const [, basePath, uuid] = match\n\n const srcset = [\n `${basePath}${uuid}-sm.webp 640w`,\n `${basePath}${uuid}-md.webp 1024w`,\n `${basePath}${uuid}-lg.webp 1920w`,\n ].join(', ')\n\n const escapedAlt = escapeHtml(alt)\n\n // Self-heal: this plugin emits a STATIC HTML string at build time (no Vue\n // runtime), so the fallback must be an inline onerror. A fit-variant\n // (`-md`/`-lg`) may legitimately be absent (internal/imaging variants.go\n // skips variants by DIMENSION). On the first error, swap the `<img>` to the\n // ORIGINAL url AND strip the sibling `<source>` elements — a `<picture>`\n // does NOT re-resolve after a `<source>` 404s, so the variant set must be\n // removed for the original to display. (`this.onerror=null` prevents loops.)\n const escapedOriginal = escapeHtml(src)\n const onError =\n `this.onerror=null;this.src='${escapedOriginal}';` +\n `this.closest('picture')?.querySelectorAll('source').forEach(s=>s.remove())`\n\n return [\n '<picture>',\n ` <source srcset=\"${srcset}\" type=\"image/webp\" sizes=\"(max-width: 1024px) 100vw, 1024px\" />`,\n ` <img src=\"${basePath}${uuid}-md.webp\" alt=\"${escapedAlt}\" loading=\"lazy\" decoding=\"async\" onerror=\"${onError}\" />`,\n '</picture>',\n ].join('\\n')\n }\n}\n","/**\n * Build-time responsive-variant lint for DCS customer sites.\n *\n * The 2026-07-03 flagship-excellence review (P7) found desktop pages shipping\n * un-suffixed **full-size** CDN images (e.g. a 321.9 KB hero `47af1e8f….webp`)\n * instead of a `-md`/`-lg` responsive step. That happens when an image reaches\n * the output HTML through a path that does NOT run the `<img>` → `<picture>`\n * transform — content injected via `__DCS_CONTENT__` and then swapped to the\n * base `cdnUrl` by `dcsCdnBuildEnd`, or a hand-authored `<img src>` pointing at\n * a base CDN asset URL.\n *\n * A DCS CDN asset URL has the shape\n * `https://files.<host>/[content/]<slug>/assets/[<dir>/]<uuid>.<ext>`\n * Responsive variants insert a `-sm`/`-md`/`-lg` suffix before the extension\n * (`…<uuid>-md.webp`). Those suffixes contain non-hex letters, so a URL that\n * still matches the *base* pattern below is, by definition, an un-suffixed\n * full-size asset. This lint scans the built HTML's `<img src>` and\n * `<source srcset>` attributes for such URLs and reports (or fails on) them.\n *\n * It is intentionally a **static-output** lint (VitePress generates HTML after\n * both Vite builds finish, so a Vite plugin hook cannot see the SSG output) and\n * is registered via VitePress `buildEnd`.\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { dcsCdnBuildEnd, dcsResponsiveImageLint, chainBuildEnd } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * // run the CDN rewrite first, then lint the result\n * buildEnd: chainBuildEnd(dcsCdnBuildEnd(), dcsResponsiveImageLint()),\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\n\n// ────────────────────────────────────────────────────────────────────────────\n// Detector (pure — unit-testable without a build)\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Anchored matcher for a **base** (un-suffixed, full-size) DCS CDN asset URL.\n * Kept aligned with `responsiveImagePlugin`'s `CDN_PATTERN`: the filename stem\n * is `[a-f0-9-]+` (hex UUID), so a responsive variant — `…-sm.webp`,\n * `…-md.webp`, `…-lg.webp` — never matches (each suffix carries a non-hex\n * letter). Query strings / fragments are tolerated after the extension.\n */\nconst BASE_CDN_ASSET =\n /^https?:\\/\\/files\\.[^/]+\\/(?:content\\/)?[^/]+\\/assets\\/(?:[^/]+\\/)*[a-f0-9-]+\\.\\w+(?:[?#].*)?$/i\n\nexport interface ResponsiveImageViolation {\n /** The un-suffixed full-size CDN URL that should have been a responsive variant. */\n url: string\n /** Which element referenced it. */\n tag: 'img' | 'source'\n}\n\n/** True when `url` is a base (un-suffixed, full-size) DCS CDN asset URL. */\nexport function isFullSizeCdnUrl(url: string): boolean {\n return BASE_CDN_ASSET.test(url.trim())\n}\n\nfunction readAttr(tag: string, name: string): string | null {\n const m = tag.match(new RegExp(`\\\\b${name}=(\"([^\"]*)\"|'([^']*)')`, 'i'))\n if (!m) return null\n return m[2] ?? m[3] ?? ''\n}\n\n/**\n * Scan an HTML string and return every `<img src>` / `<source srcset>` reference\n * that resolves to a base (un-suffixed) full-size DCS CDN asset. `srcset`\n * candidates are split on commas and only the URL portion (before the width /\n * density descriptor) is tested. `onerror` and other attributes are ignored —\n * only the actual `src` / `srcset` sources are inspected.\n */\nexport function findFullSizeCdnImages(html: string): ResponsiveImageViolation[] {\n const violations: ResponsiveImageViolation[] = []\n\n for (const m of html.matchAll(/<img\\b[^>]*>/gi)) {\n const src = readAttr(m[0], 'src')\n if (src && isFullSizeCdnUrl(src)) {\n violations.push({ url: src.trim(), tag: 'img' })\n }\n }\n\n for (const m of html.matchAll(/<source\\b[^>]*>/gi)) {\n const srcset = readAttr(m[0], 'srcset')\n if (!srcset) continue\n for (const candidate of srcset.split(',')) {\n const url = candidate.trim().split(/\\s+/)[0]\n if (url && isFullSizeCdnUrl(url)) {\n violations.push({ url, tag: 'source' })\n }\n }\n }\n\n return violations\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// VitePress buildEnd hook\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface DcsResponsiveImageLintOptions {\n /**\n * Throw (fail the build) when full-size images are found. Default `false`\n * (warn only) so the guard can land ahead of the per-site cleanup; flip to\n * `true` in CI once a site's output is clean to prevent regressions.\n */\n failOnViolation?: boolean\n /** File extensions to scan in the output directory. Default `['.html']`. */\n extensions?: string[]\n /** Enable per-file debug logging. */\n debug?: boolean\n}\n\nexport interface ResponsiveImageLintReport {\n filesScanned: number\n violations: Array<ResponsiveImageViolation & { file: string }>\n}\n\n/**\n * VitePress `buildEnd` factory that lints the generated HTML for un-suffixed\n * full-size CDN images. Returns the report (also useful for tests) and either\n * warns or throws depending on `failOnViolation`.\n */\nexport function dcsResponsiveImageLint(options: DcsResponsiveImageLintOptions = {}) {\n const { failOnViolation = false, extensions = ['.html'], debug = false } = options\n\n return async (siteConfig: { outDir: string }): Promise<ResponsiveImageLintReport> => {\n const report: ResponsiveImageLintReport = { filesScanned: 0, violations: [] }\n const outDir = siteConfig.outDir\n if (!outDir || !fs.existsSync(outDir)) return report\n\n const walk = (dir: string) => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const full = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n walk(full)\n } else if (extensions.some((ext) => entry.name.endsWith(ext))) {\n report.filesScanned++\n const html = fs.readFileSync(full, 'utf8')\n if (!html.includes('/assets/')) continue\n const rel = path.relative(outDir, full)\n for (const v of findFullSizeCdnImages(html)) {\n report.violations.push({ ...v, file: rel })\n if (debug) {\n console.log(`[dcs-responsive-lint] ${rel}: <${v.tag}> full-size ${v.url}`)\n }\n }\n }\n }\n }\n\n walk(outDir)\n\n if (report.violations.length > 0) {\n const lines = report.violations\n .map((v) => ` ${v.file}: <${v.tag}> ${v.url}`)\n .join('\\n')\n const summary =\n `[dcs-responsive-lint] ${report.violations.length} full-size CDN image(s) ` +\n `shipped without a -sm/-md/-lg responsive variant across ${report.filesScanned} file(s):\\n${lines}`\n if (failOnViolation) {\n throw new Error(summary)\n }\n console.warn(summary)\n } else if (debug) {\n console.log(\n `[dcs-responsive-lint] clean — scanned ${report.filesScanned} file(s), no full-size images.`\n )\n }\n\n return report\n }\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// buildEnd composition helper\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Compose multiple VitePress `buildEnd` hooks into one (VitePress accepts a\n * single `buildEnd`). Hooks run sequentially in the order given, so a rewrite\n * hook (`dcsCdnBuildEnd`) can precede a lint (`dcsResponsiveImageLint`).\n */\nexport function chainBuildEnd<T>(\n ...hooks: Array<((siteConfig: T) => unknown | Promise<unknown>) | undefined | null>\n): (siteConfig: T) => Promise<void> {\n const active = hooks.filter(\n (h): h is (siteConfig: T) => unknown | Promise<unknown> => typeof h === 'function'\n )\n return async (siteConfig: T) => {\n for (const hook of active) {\n await hook(siteConfig)\n }\n }\n}\n"]}