@duffcloudservices/cms 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +244 -8
  2. package/dist/chunk-A5F4C72F.js +500 -0
  3. package/dist/chunk-A5F4C72F.js.map +1 -0
  4. package/dist/{chunk-F3EIWEZD.js → chunk-HVSF23P7.js} +971 -73
  5. package/dist/chunk-HVSF23P7.js.map +1 -0
  6. package/dist/editor/editorBridge.d.ts +13 -1
  7. package/dist/editor/editorBridge.js +75 -5
  8. package/dist/editor/editorBridge.js.map +1 -1
  9. package/dist/headHonesty-OzxvLuwd.d.ts +222 -0
  10. package/dist/index.d.ts +365 -22
  11. package/dist/index.js +421 -21
  12. package/dist/index.js.map +1 -1
  13. package/dist/installSeoHead-kWQwObez.d.ts +627 -0
  14. package/dist/plugins/index.d.ts +90 -6
  15. package/dist/plugins/index.js +530 -49
  16. package/dist/plugins/index.js.map +1 -1
  17. package/dist/seo/index.d.ts +763 -4
  18. package/dist/seo/index.js +2 -2
  19. package/dist/{vitepressTransform-DfmABXmK.d.ts → vitepressTransform-JG_zlaux.d.ts} +99 -6
  20. package/package.json +17 -6
  21. package/src/components/DcsCallButton.test.ts +58 -0
  22. package/src/components/DcsCallButton.vue +19 -4
  23. package/src/components/LiteMediaEmbed.vue +3 -3
  24. package/src/components/ManagedImage.test.ts +34 -0
  25. package/src/components/ManagedImage.vue +5 -0
  26. package/src/components/PreviewRibbon.vue +25 -6
  27. package/src/components/raw-source-imports.test.ts +44 -0
  28. package/src/composables/useConversionTracking.test.ts +492 -0
  29. package/src/composables/useConversionTracking.ts +770 -0
  30. package/src/composables/useReleaseNotes.ts +7 -1
  31. package/src/composables/useSEO.applyHead.test.ts +150 -0
  32. package/src/composables/useSEO.ts +63 -17
  33. package/src/composables/useSiteVersion.ts +4 -1
  34. package/src/composables/useSiteVisitorSession.test.ts +56 -0
  35. package/src/composables/useSiteVisitorSession.ts +39 -3
  36. package/src/composables/useTextContent.ts +9 -1
  37. package/dist/chunk-DAYLLSEE.js +0 -3
  38. package/dist/chunk-DAYLLSEE.js.map +0 -1
  39. package/dist/chunk-F3EIWEZD.js.map +0 -1
  40. package/dist/spliceHeadHtml-CsBEucGy.d.ts +0 -254
@@ -1,6 +1,7 @@
1
1
  import { Plugin, HtmlTagDescriptor } from 'vite';
2
- import { D as DcsRobotsOptions, Y as PageRouteEntry, H as SeoConfiguration, E as ContentConfig } from '../vitepressTransform-DfmABXmK.js';
3
- export { C as CreateSeoTransformPageDataOptions, R as ResolvedPageOverrides, S as SeoPageContext, r as SeoPageTypeRule, t as VitePressHeadConfig, V as VitePressPageData, b as buildVitePressSeoHead, c as createSeoTransformPageData, d as defaultRelativePathToRoute } from '../vitepressTransform-DfmABXmK.js';
2
+ import { D as DcsRobotsOptions, Y as SeoHonestyBlock, Z as PageRouteEntry, H as SeoConfiguration, E as ContentConfig } from '../vitepressTransform-JG_zlaux.js';
3
+ export { C as CreateSeoTransformPageDataOptions, R as ResolvedPageOverrides, S as SeoPageContext, r as SeoPageTypeRule, t as VitePressHeadConfig, V as VitePressPageData, b as buildVitePressSeoHead, c as createSeoTransformPageData, d as defaultRelativePathToRoute } from '../vitepressTransform-JG_zlaux.js';
4
+ import { H as HonestyMode } from '../headHonesty-OzxvLuwd.js';
4
5
  import { Component, Plugin as Plugin$1 } from 'vue';
5
6
  import MarkdownIt from 'markdown-it';
6
7
 
@@ -146,6 +147,14 @@ interface DcsSeoPluginOptions {
146
147
  * Routes that should receive `robots: noindex, nofollow`. Matched against the
147
148
  * route `path` OR `slug`. The home route (`/`) still overwrites
148
149
  * `dist/index.html`.
150
+ *
151
+ * This is a PREDICATE OVER `pages.yaml`, not a standalone directive about a
152
+ * URL. An entry that matches no page is a **build error**
153
+ * (`NoindexOrphanError`, C-416) — with no matching page nothing is emitted for
154
+ * it, so the entry would silently do nothing while the URL stayed
155
+ * `index, follow`. Matched raw, so `'/account/'` does not match a `'/account'`
156
+ * route; matched against the full manifest, so an entry that is also in
157
+ * `exclude` is fine.
149
158
  */
150
159
  noindex?: string[];
151
160
  /**
@@ -177,6 +186,63 @@ interface DcsSeoPluginOptions {
177
186
  preview?: boolean;
178
187
  /** Emit `dist/llms.txt` (default `true`; always off in preview mode). */
179
188
  llms?: boolean;
189
+ /**
190
+ * P1 — assert that each route's BAKED `<title>`/description equals what the
191
+ * app leaves in the `<head>` after it mounts. Rides the prerender browser, so
192
+ * the marginal cost is one `page.evaluate()` per route.
193
+ *
194
+ * Env override: `DCS_SEO_HEAD_HONESTY=error|warn|off`.
195
+ * seo.yaml override: `headHonesty: warn` or `headHonesty: { mode, allow }`.
196
+ */
197
+ headHonesty?: boolean | HonestyMode | SeoHonestyBlock;
198
+ /**
199
+ * P2 — assert that every URL the factory publishes (JSON-LD logo/image IRIs,
200
+ * `og:image`, icon links, sitemap `<loc>`s, llms.txt links) resolves to what it
201
+ * promises. Same-origin assets are proven against `dist/` with no network;
202
+ * cross-origin assets are probed under a cache + time budget and only a
203
+ * DEFINITIVE wrong answer fails the build.
204
+ *
205
+ * Env override: `DCS_SEO_URL_HONESTY=error|warn|off`,
206
+ * `DCS_SEO_URL_HONESTY_NETWORK=off` to skip the cross-origin probes.
207
+ */
208
+ urlHonesty?: boolean | HonestyMode | SeoHonestyBlock;
209
+ /**
210
+ * P12 — hoist `<meta charset>` to the top of `<head>` and assert it lands
211
+ * inside the spec's 1024-byte encoding-sniffing window.
212
+ *
213
+ * Env override: `DCS_SEO_CHARSET_BUDGET=error|warn|off`.
214
+ */
215
+ charsetBudget?: boolean | HonestyMode | SeoHonestyBlock;
216
+ }
217
+ /**
218
+ * Every key a `noindex` entry can legitimately match.
219
+ *
220
+ * The plugin's contract for `noindex` is a PREDICATE OVER THE ROUTE MANIFEST,
221
+ * never a directive about a bare URL. Every consumer asks the question
222
+ * route-first and identically —
223
+ * `noindexSet.has(route.path) || (route.slug && noindexSet.has(route.slug))` —
224
+ * in {@link emitStaticSeoHtml}, in the sitemap/llms `isRouteIndexable`
225
+ * predicate, and in the baked `__DCS_PAGES__` manifest the runtime re-assert
226
+ * reads. So the set of entries that can ever do anything is exactly the union
227
+ * of the manifest's paths and slugs, and this function is the exact inverse of
228
+ * that same relation rather than a second, drifting implementation of it.
229
+ */
230
+ declare function collectNoindexMatchKeys(routes: PageRouteEntry[]): Set<string>;
231
+ /**
232
+ * `noindex` entries that match no route in the manifest — i.e. entries that
233
+ * are, today, silent no-ops.
234
+ *
235
+ * Matched with the SAME raw string equality the consumers use, deliberately:
236
+ * a `'/account/'` entry against a `'/account'` route does not match in
237
+ * `emitStaticSeoHtml`, so it must not "match" here either, or the check would
238
+ * bless the very entry the emitter is about to ignore.
239
+ */
240
+ declare function findOrphanNoindexEntries(noindex: string[], routes: PageRouteEntry[]): string[];
241
+ /** Thrown when a `noindex` entry names nothing in `pages.yaml` (C-416). */
242
+ declare class NoindexOrphanError extends Error {
243
+ readonly orphans: string[];
244
+ readonly knownKeys: string[];
245
+ constructor(orphans: string[], knownKeys: string[]);
180
246
  }
181
247
  /**
182
248
  * Core site-file emitter, separated from Vite so it is unit-testable.
@@ -583,14 +649,32 @@ declare function chainBuildEnd<T>(...hooks: Array<((siteConfig: T) => unknown |
583
649
  */
584
650
  declare 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}";
585
651
  /**
586
- * The `<head>` tag descriptor for the motion-token block. `head-prepend` keeps
587
- * it above the site's own bundled CSS so a per-site `:root` override still wins.
652
+ * The `<head>` tag descriptor for the motion-token block.
653
+ *
654
+ * `injectTo: 'head'`, NOT `'head-prepend'` — C-414. Vite's `head-prepend`
655
+ * inserts immediately after the `<head>` open tag, i.e. AHEAD of the site's own
656
+ * `<meta charset>`. This block is 858 bytes, so it pushed the encoding
657
+ * declaration to byte 910 of the HTML spec's 1024-byte encoding-sniffing window
658
+ * on every site in the fleet (measured on Iron Oak and Bryan's 2026-07-27, and
659
+ * re-measured identical here), leaving ~90 bytes of headroom before every DCS
660
+ * site silently mojibakes.
661
+ *
662
+ * `'head'` appends to the END of the `<head>` the site authored — which is
663
+ * still ABOVE Vite's own build-time `<link rel=stylesheet>` / entry `<script>`,
664
+ * because those are injected AFTER this `order: 'pre'` hook's tags. So the
665
+ * per-site `:root` override still wins the cascade (that was the original
666
+ * reason for `head-prepend`) AND the charset lands at byte ~46 with ~954 bytes
667
+ * of headroom. Measured, not assumed — see `verify-honesty-rails.mjs` P12.
668
+ *
669
+ * If you ever move this back to `head-prepend`, the P12 rail in
670
+ * `scripts/verify-honesty-rails.mjs` will fail: it asserts the charset comes
671
+ * FIRST straight out of `dcsContentPlugin`, with no `dcsSeoPlugin` hoist.
588
672
  */
589
673
  declare function dcsMotionTokensStyleTag(): HtmlTagDescriptor;
590
674
 
591
675
  /**
592
676
  * DCS AI-assistant widget loader — the cms head-inject seam for Phase 3 of the
593
- * Managed Site AI Agents plan (.docs/plans/managed-site-ai-agents/plan.md).
677
+ * Managed Site AI Agents plan (.docs/archive/plans/managed-site-ai-agents/plan.md).
594
678
  *
595
679
  * This rides the same always-on `dcsContentPlugin.transformIndexHtml` seam that
596
680
  * ships the motion tokens: every DCS site already wires `dcsContentPlugin`, so a
@@ -660,4 +744,4 @@ interface DcsAgentWidgetTagOptions {
660
744
  */
661
745
  declare function dcsAgentWidgetLoaderTag(options: DcsAgentWidgetTagOptions): HtmlTagDescriptor | null;
662
746
 
663
- export { DCS_MOTION_TOKENS_CSS, DEFAULT_AGENT_API_BASE_URL, DEFAULT_AGENT_WIDGET_SCRIPT_URL, type DcsAgentWidgetLoaderOptions, type DcsAgentWidgetTagOptions, type DcsCdnBuildEndOptions, type DcsCdnImagePluginOptions, type DcsContentPluginOptions, type DcsEditorPluginOptions, type DcsPreviewPluginOptions, type DcsResponsiveImageLintOptions, type DcsSeoPluginOptions, type ResponsiveImageLintReport, type ResponsiveImageViolation, buildAgentWidgetLoaderScript, chainBuildEnd, dcsAgentWidgetLoaderTag, dcsCdnBuildEnd, dcsCdnImagePlugin, dcsContentPlugin, dcsEditorPlugin, dcsMotionTokensStyleTag, dcsPreviewPlugin, dcsResponsiveImageLint, dcsSeoPlugin, emitSiteFiles, emitStaticSeoHtml, findFullSizeCdnImages, isFullSizeCdnUrl, readSiteSlug, responsiveImagePlugin };
747
+ export { DCS_MOTION_TOKENS_CSS, DEFAULT_AGENT_API_BASE_URL, DEFAULT_AGENT_WIDGET_SCRIPT_URL, type DcsAgentWidgetLoaderOptions, type DcsAgentWidgetTagOptions, type DcsCdnBuildEndOptions, type DcsCdnImagePluginOptions, type DcsContentPluginOptions, type DcsEditorPluginOptions, type DcsPreviewPluginOptions, type DcsResponsiveImageLintOptions, type DcsSeoPluginOptions, NoindexOrphanError, type ResponsiveImageLintReport, type ResponsiveImageViolation, buildAgentWidgetLoaderScript, chainBuildEnd, collectNoindexMatchKeys, dcsAgentWidgetLoaderTag, dcsCdnBuildEnd, dcsCdnImagePlugin, dcsContentPlugin, dcsEditorPlugin, dcsMotionTokensStyleTag, dcsPreviewPlugin, dcsResponsiveImageLint, dcsSeoPlugin, emitSiteFiles, emitStaticSeoHtml, findFullSizeCdnImages, findOrphanNoindexEntries, isFullSizeCdnUrl, readSiteSlug, responsiveImagePlugin };