@duffcloudservices/cms 0.12.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 (39) 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 +4 -1
  27. package/src/composables/useConversionTracking.test.ts +492 -0
  28. package/src/composables/useConversionTracking.ts +770 -0
  29. package/src/composables/useReleaseNotes.ts +7 -1
  30. package/src/composables/useSEO.applyHead.test.ts +150 -0
  31. package/src/composables/useSEO.ts +63 -17
  32. package/src/composables/useSiteVersion.ts +4 -1
  33. package/src/composables/useSiteVisitorSession.test.ts +56 -0
  34. package/src/composables/useSiteVisitorSession.ts +39 -3
  35. package/src/composables/useTextContent.ts +9 -1
  36. package/dist/chunk-DAYLLSEE.js +0 -3
  37. package/dist/chunk-DAYLLSEE.js.map +0 -1
  38. package/dist/chunk-F3EIWEZD.js.map +0 -1
  39. package/dist/spliceHeadHtml-CsBEucGy.d.ts +0 -254
@@ -1,254 +0,0 @@
1
- import { H as SeoConfiguration, x as BreadcrumbCrumb, z as ReviewSource, F as FaqSource, y as BlogMeta, T as ResolvedPageSeo, N as SeoSchemaConfig, G as GlobalSeoConfig, L as SeoOpenGraphConfig, M as SeoTwitterConfig } from './vitepressTransform-DfmABXmK.js';
2
-
3
- /**
4
- * Framework-agnostic SEO head-tag resolution.
5
- *
6
- * This module is the single source of truth for turning a
7
- * (`pageSlug`, `pagePath`, `SeoConfiguration`) triple into a plain,
8
- * serialisable description of the `<head>` tags a page should carry:
9
- * resolved title, meta[], link[], and JSON-LD script[].
10
- *
11
- * It is consumed by:
12
- * - `useSEO` (runtime, via `@unhead/vue`) — see `../composables/useSEO.ts`
13
- * - `dcsSeoPlugin`'s build-time static-HTML emitter — see
14
- * `../plugins/dcsSeoPlugin.ts`
15
- *
16
- * Keeping the resolution here (rather than inside the Vue composable) means
17
- * the runtime and the build-time emitter produce byte-identical tags from the
18
- * same `seo.yaml`, with no Vue/unhead dependency required at build time.
19
- *
20
- * Runtime behaviour is intentionally identical to the previous in-composable
21
- * logic **except** for one corrected bug: `og:title` now falls back to the
22
- * fully-resolved (template-applied) page title instead of the raw, untemplated
23
- * `page.title`. Previously `og:title` could diverge from the `<title>` element
24
- * (e.g. `<title>Iron Oak Contractors | Our Services</title>` but
25
- * `og:title = "Our Services"`).
26
- */
27
-
28
- /** A `<meta>` tag — either a `name=`/`content=` or `property=`/`content=` pair. */
29
- interface HeadMetaTag {
30
- name?: string;
31
- property?: string;
32
- content: string;
33
- }
34
- /** A `<link>` tag (canonical, alternate, etc.). */
35
- interface HeadLinkTag {
36
- rel: string;
37
- href: string;
38
- hreflang?: string;
39
- }
40
- /** A `<script type="application/ld+json">` tag carrying serialised JSON-LD. */
41
- interface HeadScriptTag {
42
- type: string;
43
- /** Pre-serialised JSON-LD string (already `JSON.stringify`-ed). */
44
- children: string;
45
- }
46
- /**
47
- * The complete, framework-agnostic set of resolved `<head>` tags for a page.
48
- *
49
- * - `title` is the final, template-applied title (what goes in `<title>`).
50
- * - `meta` covers description, keywords, robots, verification, OG, Twitter.
51
- * - `link` covers canonical + hreflang alternates.
52
- * - `script` covers JSON-LD (global + page schemas).
53
- * - `jsonLd` is the same JSON-LD as parsed objects, for callers that want the
54
- * structured form (e.g. `useSEO().getSchema()`).
55
- */
56
- interface ResolvedHeadTags {
57
- title: string;
58
- meta: HeadMetaTag[];
59
- link: HeadLinkTag[];
60
- script: HeadScriptTag[];
61
- jsonLd: object[];
62
- /** The fully-resolved page SEO (merged global + page) used to build tags. */
63
- resolved: ResolvedPageSeo;
64
- }
65
- /** Optional overrides applied on top of the resolved config when building tags. */
66
- interface HeadTagOverrides {
67
- /** Override the resolved `<title>`. */
68
- title?: string;
69
- /**
70
- * Page-specific title fallback used when `seo.yaml` has no `title` for this
71
- * page (e.g. the route `title` from `pages.yaml`). Unlike `global.defaultTitle`
72
- * this IS run through `titleTemplate`, so un-configured routes (blog posts,
73
- * etc.) get unique titles rather than the global default.
74
- */
75
- fallbackTitle?: string;
76
- /** Override the resolved meta description. */
77
- description?: string;
78
- /** Override the meta keywords value. */
79
- keywords?: string;
80
- /**
81
- * Force the robots directive (e.g. `'noindex, nofollow'`). When supplied this
82
- * wins over both page- and global-level robots.
83
- */
84
- robots?: string;
85
- /** Replace the JSON-LD schema objects entirely (already-built objects). */
86
- schemas?: object[];
87
- /** Extra meta tags appended after the generated ones. */
88
- meta?: HeadMetaTag[];
89
- /**
90
- * Emit a `<meta name="keywords">` tag from the page's `keywords` field.
91
- *
92
- * Defaults to `false` so the `useSEO` runtime path stays byte-identical to
93
- * its historical output (which never emitted keywords). The static-HTML
94
- * emitter opts in (`true`) to surface page keywords in the baked `<head>`.
95
- */
96
- includeKeywords?: boolean;
97
- /**
98
- * Emit the global `@graph` spine (Organization + WebSite + the promoted
99
- * LocalBusiness node, cross-linked by `@id`) PREPENDED before the per-schema
100
- * JSON-LD. When true, the LocalBusiness subtype already present in
101
- * `global.schemas` is ABSORBED into the graph (not emitted a second time).
102
- * Default `false`.
103
- */
104
- emitGraph?: boolean;
105
- /**
106
- * Ordered Home → … → current breadcrumb trail (absolute item URLs). When it
107
- * has more than one hop a `BreadcrumbList` is emitted; the home page (≤ 1 hop)
108
- * emits none.
109
- */
110
- breadcrumbTrail?: BreadcrumbCrumb[];
111
- /**
112
- * REAL review items (from `.dcs/content.yaml`) for the LocalBusiness node.
113
- * Honest Review[] + aggregateRating are emitted ONLY for items with a numeric
114
- * rating + non-empty text + authorName; empty/missing ⇒ nothing.
115
- */
116
- reviews?: ReviewSource[];
117
- /**
118
- * Free-text trade/occupational license (from the `business.license`
119
- * content.yaml key). When non-empty a `hasCredential`
120
- * (`EducationalOccupationalCredential`, `credentialCategory: "license"`) is
121
- * added to the LocalBusiness node in the `@graph`; empty/missing ⇒ nothing.
122
- * Only consulted when `emitGraph` is true.
123
- */
124
- license?: string;
125
- /**
126
- * Structured FAQ pairs (frontmatter `faq:` / `.dcs/faq.yaml`). A `FAQPage` is
127
- * emitted ONLY when at least one entry has a non-empty question AND answer.
128
- */
129
- faq?: FaqSource[];
130
- /**
131
- * Blog-post metadata. When `headline` is present a `BlogPosting` is emitted —
132
- * UNLESS the page's own schemas already hand-author a `BlogPosting` (then the
133
- * builder defers to the authored copy to avoid duplication).
134
- */
135
- blogMeta?: BlogMeta;
136
- }
137
- /**
138
- * Generate Open Graph meta tags from config.
139
- *
140
- * @param resolvedTitle - the final, template-applied page title. Used as the
141
- * `og:title` fallback so OG stays consistent with `<title>`.
142
- */
143
- declare function generateOpenGraphMeta(og: SeoOpenGraphConfig, global: GlobalSeoConfig, resolvedTitle: string, pageDescription: string, canonical: string): Array<{
144
- property: string;
145
- content: string;
146
- }>;
147
- /**
148
- * Generate Twitter Card meta tags from config.
149
- *
150
- * @param resolvedTitle - the final, template-applied page title (Twitter title
151
- * fallback), mirroring the OG behaviour.
152
- */
153
- declare function generateTwitterMeta(twitter: SeoTwitterConfig, global: GlobalSeoConfig, resolvedTitle: string, pageDescription: string): Array<{
154
- name: string;
155
- content: string;
156
- }>;
157
- /**
158
- * Generate JSON-LD schema objects from schema configs, auto-populating common
159
- * WebSite properties from global config.
160
- */
161
- declare function generateJsonLd(schemas: SeoSchemaConfig[], global: GlobalSeoConfig): object[];
162
- /**
163
- * Resolve page SEO by merging global defaults with page-specific config.
164
- *
165
- * Behavioural changes from the historical in-composable version (both bug
166
- * fixes):
167
- * 1. `openGraph.title` falls back to the **template-applied** page title, not
168
- * the raw `page.title`, so `og:title` matches `<title>`.
169
- * 2. The `titleTemplate` is applied **only** to a page-specific title
170
- * (`page.title`, or the `fallbackTitle` arg). It is no longer applied to
171
- * `global.defaultTitle`, which is already the complete brand title —
172
- * templating it produced `"Brand | Default Title | Brand"` doubling on any
173
- * page without its own `seo.yaml` entry.
174
- *
175
- * @param fallbackTitle - a page-specific title to use when `seo.yaml` has no
176
- * `title` for this page (e.g. the route `title` from `pages.yaml`). It IS run
177
- * through `titleTemplate`; `global.defaultTitle` is the last resort and is not.
178
- */
179
- declare function resolvePageSeo(pageSlug: string, pagePath: string | undefined, seoConfig: SeoConfiguration | undefined, fallbackTitle?: string): ResolvedPageSeo;
180
- /**
181
- * Build the complete, framework-agnostic set of `<head>` tags for a page.
182
- *
183
- * This is the function both the `useSEO` runtime and the build-time emitter
184
- * call, guaranteeing identical output. Pass `overrides` to mirror the
185
- * composable's `applyHead(overrides)` behaviour, or to force `robots` (used by
186
- * the emitter's `noindex` option).
187
- */
188
- declare function buildHeadTags(pageSlug: string, pagePath: string | undefined, seoConfig: SeoConfiguration | undefined, overrides?: HeadTagOverrides): ResolvedHeadTags;
189
-
190
- /**
191
- * Pure, framework-free `<head>` splicing for the build-time SEO emitter.
192
- *
193
- * Given a built `index.html` shell and a set of resolved head tags (from
194
- * `buildHeadTags`), this produces a new HTML string where the SEO-managed
195
- * tags — `<title>`, `description`, `keywords`, `robots`, `canonical`,
196
- * verification, all `og:*` / `article:*` properties, all `twitter:*` names,
197
- * and `application/ld+json` scripts — have been **replaced** (not duplicated)
198
- * with the resolved set.
199
- *
200
- * Design goals:
201
- * - **Idempotent**: running it twice yields the same output (it strips the
202
- * managed tags first, then re-inserts the canonical set).
203
- * - **Deterministic**: tag order is fixed by `renderHeadTags`.
204
- * - **Conservative**: only tags we own are touched. Charset, viewport, CSP,
205
- * theme-color, favicons, stylesheets, and the app script are left intact.
206
- *
207
- * This is intentionally regex-based (no DOM dependency) to mirror the existing
208
- * `dcsCdnImagePlugin` post-build HTML rewriting and to keep the emitter free of
209
- * heavy parser deps at build time.
210
- */
211
-
212
- /**
213
- * Escape a JSON-LD string for safe inclusion inside an HTML `<script>` element.
214
- *
215
- * A `<script type="application/ld+json">` is a DATA block, but the HTML parser
216
- * still scans its raw text for `</script` (and `<!--`) — so untrusted CMS free
217
- * text (review bodies, FAQ Q&A, blog descriptions) carrying `</script>` could
218
- * break out of the script and inject markup (stored XSS).
219
- *
220
- * Per OWASP "JSON in an HTML context", we escape the three HTML-significant
221
- * characters as their `\uXXXX` JSON escapes. Because these characters only ever
222
- * appear INSIDE JSON string literals (never as JSON structure), the result is
223
- * still byte-for-byte valid JSON that `JSON.parse` round-trips:
224
- * `<` → `<` (defeats `</script>` and `<!--` breakout)
225
- * `>` → `>`
226
- * `&` → `&`
227
- *
228
- * Exported so BOTH static-SEO sinks (the SPA `spliceHeadHtml` path AND the
229
- * VitePress `transformPageData` head emit) share one guard.
230
- */
231
- declare function escapeJsonLd(json: string): string;
232
- /**
233
- * Render the resolved head tags to a deterministic HTML fragment.
234
- * Order: title, meta (in the order produced by buildHeadTags), link, script.
235
- */
236
- declare function renderHeadTags(tags: ResolvedHeadTags, indent?: string): string;
237
- /**
238
- * Strip the SEO-managed tags from a `<head>` block so they can be re-inserted
239
- * without duplication. Operates only within `<head>...</head>` to avoid
240
- * touching body content.
241
- */
242
- declare function stripManagedHeadTags(html: string): string;
243
- /**
244
- * Splice resolved SEO head tags into an HTML document.
245
- *
246
- * Strips the existing managed tags, then inserts the rendered canonical set
247
- * immediately before `</head>`. If no `<head>` is present the HTML is returned
248
- * unchanged (defensive — the emitter logs and no-ops in that case).
249
- *
250
- * Idempotent: applying twice produces identical output.
251
- */
252
- declare function spliceHeadHtml(html: string, tags: ResolvedHeadTags): string;
253
-
254
- export { type HeadMetaTag as H, type ResolvedHeadTags as R, generateOpenGraphMeta as a, buildHeadTags as b, generateTwitterMeta as c, renderHeadTags as d, stripManagedHeadTags as e, escapeJsonLd as f, generateJsonLd as g, type HeadLinkTag as h, type HeadScriptTag as i, type HeadTagOverrides as j, resolvePageSeo as r, spliceHeadHtml as s };