@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.
- package/README.md +244 -8
- package/dist/chunk-A5F4C72F.js +500 -0
- package/dist/chunk-A5F4C72F.js.map +1 -0
- package/dist/{chunk-F3EIWEZD.js → chunk-HVSF23P7.js} +971 -73
- package/dist/chunk-HVSF23P7.js.map +1 -0
- package/dist/editor/editorBridge.d.ts +53 -1
- package/dist/editor/editorBridge.js +141 -5
- package/dist/editor/editorBridge.js.map +1 -1
- package/dist/headHonesty-OzxvLuwd.d.ts +222 -0
- package/dist/index.d.ts +369 -22
- package/dist/index.js +424 -22
- package/dist/index.js.map +1 -1
- package/dist/installSeoHead-kWQwObez.d.ts +627 -0
- package/dist/plugins/index.d.ts +173 -6
- package/dist/plugins/index.js +628 -54
- package/dist/plugins/index.js.map +1 -1
- package/dist/seo/index.d.ts +763 -4
- package/dist/seo/index.js +2 -2
- package/dist/{vitepressTransform-DfmABXmK.d.ts → vitepressTransform-JG_zlaux.d.ts} +99 -6
- package/package.json +26 -16
- package/src/components/DcsCallButton.test.ts +58 -0
- package/src/components/DcsCallButton.vue +19 -4
- package/src/components/DcsReviewShowcase.vue +5 -1
- package/src/components/LiteMediaEmbed.vue +3 -3
- package/src/components/ManagedImage.test.ts +94 -0
- package/src/components/ManagedImage.vue +58 -6
- package/src/components/PreviewRibbon.vue +4 -1
- package/src/composables/useConversionTracking.test.ts +492 -0
- package/src/composables/useConversionTracking.ts +770 -0
- package/src/composables/useReleaseNotes.ts +7 -1
- package/src/composables/useResponsiveImage.ts +6 -0
- package/src/composables/useSEO.applyHead.test.ts +150 -0
- package/src/composables/useSEO.ts +63 -17
- package/src/composables/useSiteVersion.ts +4 -1
- package/src/composables/useSiteVisitorSession.test.ts +56 -0
- package/src/composables/useSiteVisitorSession.ts +39 -3
- package/src/composables/useTextContent.ts +9 -1
- package/dist/chunk-DAYLLSEE.js +0 -3
- package/dist/chunk-DAYLLSEE.js.map +0 -1
- package/dist/chunk-F3EIWEZD.js.map +0 -1
- package/dist/spliceHeadHtml-CsBEucGy.d.ts +0 -254
package/dist/index.js
CHANGED
|
@@ -1,11 +1,398 @@
|
|
|
1
|
-
import './chunk-
|
|
2
|
-
import { resolvePageSeo, generateJsonLd, buildHeadTags } from './chunk-
|
|
3
|
-
export { AI_BOTS, absolutizeUrl, breadcrumbTrailFromRoute, buildBlogPosting, buildBreadcrumbList, buildFaqPage, buildGlobalGraph, buildHeadTags, buildLlmsTxt, buildReviewSchemaParts, buildRobotsTxt, buildSitemapXml, buildVitePressSeoHead, createSeoTransformPageData, defaultRelativePathToRoute, escapeJsonLd, filterRealFaq, filterRealReviews, findReviewItemsForPage, generateJsonLd, generateOpenGraphMeta, generateTwitterMeta, isRouteIndexable, renderHeadTags, resolvePageSeo, slugToTitle, spliceHeadHtml, stripManagedHeadTags } from './chunk-
|
|
4
|
-
import { shallowRef, ref, computed,
|
|
5
|
-
import {
|
|
6
|
-
import { isCdnAssetUrl, resolveResponsiveImage } from '@duffcloudservices/cms-core';
|
|
1
|
+
import './chunk-A5F4C72F.js';
|
|
2
|
+
import { resolvePageSeo, generateJsonLd, buildHeadTags } from './chunk-HVSF23P7.js';
|
|
3
|
+
export { AI_BOTS, absolutizeUrl, breadcrumbTrailFromRoute, buildBlogPosting, buildBreadcrumbList, buildFaqPage, buildGlobalGraph, buildHeadTags, buildLlmsTxt, buildReviewSchemaParts, buildRobotsTxt, buildSeoHeadRouteMap, buildSitemapXml, buildVitePressSeoHead, createSeoTransformPageData, defaultRelativePathToRoute, escapeJsonLd, filterRealFaq, filterRealReviews, findReviewItemsForPage, generateJsonLd, generateOpenGraphMeta, generateTwitterMeta, installSeoHead, isRouteIndexable, normalizeSeoHeadPath, renderHeadTags, resolvePageSeo, slugToTitle, spliceHeadHtml, stripManagedHeadTags } from './chunk-HVSF23P7.js';
|
|
4
|
+
import { shallowRef, getCurrentInstance, onMounted, onBeforeUnmount, ref, computed, readonly, toValue, onUnmounted } from 'vue';
|
|
5
|
+
import { isCdnAssetUrl, resolveResponsiveImage, platformFetch, isPlatformFetchError } from '@duffcloudservices/cms-core';
|
|
7
6
|
export { isCdnAssetUrl, resolveResponsiveImage } from '@duffcloudservices/cms-core';
|
|
7
|
+
import { useHead } from '@unhead/vue';
|
|
8
8
|
|
|
9
|
+
var CONVERSION_INTERACTION_TYPES = [
|
|
10
|
+
"booking",
|
|
11
|
+
"phone",
|
|
12
|
+
"email",
|
|
13
|
+
"form_submit"
|
|
14
|
+
];
|
|
15
|
+
function isConversionType(type) {
|
|
16
|
+
return CONVERSION_INTERACTION_TYPES.includes(type);
|
|
17
|
+
}
|
|
18
|
+
var CONVERSION_CAPTURE_VERSION = "2";
|
|
19
|
+
var DEFAULT_BOOKING_HOSTS = [
|
|
20
|
+
"stridethera.com",
|
|
21
|
+
// KEPT — physical therapy
|
|
22
|
+
"momence.com",
|
|
23
|
+
// KEPT — gym / recovery
|
|
24
|
+
"vagaro.com",
|
|
25
|
+
// Just Posh
|
|
26
|
+
"acuityscheduling.com",
|
|
27
|
+
"booksy.com",
|
|
28
|
+
"calendly.com",
|
|
29
|
+
"fresha.com",
|
|
30
|
+
"janeapp.com",
|
|
31
|
+
"mindbodyonline.com",
|
|
32
|
+
"schedulicity.com",
|
|
33
|
+
"setmore.com",
|
|
34
|
+
"simplepractice.com",
|
|
35
|
+
"square.site",
|
|
36
|
+
"squareup.com"
|
|
37
|
+
];
|
|
38
|
+
var DEFAULT_SOCIAL_HOSTS = [
|
|
39
|
+
"facebook.com",
|
|
40
|
+
"instagram.com",
|
|
41
|
+
"linkedin.com",
|
|
42
|
+
"pinterest.com",
|
|
43
|
+
"threads.net",
|
|
44
|
+
"tiktok.com",
|
|
45
|
+
"x.com",
|
|
46
|
+
"youtube.com"
|
|
47
|
+
];
|
|
48
|
+
var LABEL_MAX_LENGTH = 120;
|
|
49
|
+
var DEFAULT_BUFFER_LIMIT = 50;
|
|
50
|
+
var DEDUPE_WINDOW_MS = 400;
|
|
51
|
+
function hostMatches(host, patterns) {
|
|
52
|
+
const h = host.toLowerCase();
|
|
53
|
+
return patterns.some((p) => {
|
|
54
|
+
const needle = p.toLowerCase();
|
|
55
|
+
return h === needle || h.endsWith(`.${needle}`);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function sanitizeUrl(raw) {
|
|
59
|
+
const trimmed = raw.trim();
|
|
60
|
+
if (!trimmed) return "";
|
|
61
|
+
const cut = trimmed.split("#")[0].split("?")[0];
|
|
62
|
+
return cut.slice(0, 300);
|
|
63
|
+
}
|
|
64
|
+
var CONTACT_SCHEMES = ["tel:", "sms:", "mailto:"];
|
|
65
|
+
function hashTarget(value) {
|
|
66
|
+
let h = 2166136261;
|
|
67
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
68
|
+
h ^= value.charCodeAt(i);
|
|
69
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
70
|
+
}
|
|
71
|
+
return h.toString(36);
|
|
72
|
+
}
|
|
73
|
+
function hrefScheme(href, pageHost) {
|
|
74
|
+
const raw = href.trim();
|
|
75
|
+
if (!raw) return "";
|
|
76
|
+
const lower = raw.toLowerCase();
|
|
77
|
+
const contact = CONTACT_SCHEMES.find((s) => lower.startsWith(s));
|
|
78
|
+
if (contact) return contact;
|
|
79
|
+
if (lower.startsWith("#")) return "";
|
|
80
|
+
try {
|
|
81
|
+
return new URL(raw, `https://${pageHost || "localhost"}/`).protocol;
|
|
82
|
+
} catch {
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function redactHref(href, pageHost) {
|
|
87
|
+
const raw = href.trim();
|
|
88
|
+
if (!raw) return { href: "", scheme: "", hash: "" };
|
|
89
|
+
const lower = raw.toLowerCase();
|
|
90
|
+
const contact = CONTACT_SCHEMES.find((s) => lower.startsWith(s));
|
|
91
|
+
if (contact) {
|
|
92
|
+
const target = raw.slice(contact.length).split("?")[0].trim().toLowerCase();
|
|
93
|
+
const normalized = contact === "mailto:" ? target : target.replace(/[^0-9+]/gu, "");
|
|
94
|
+
const hash = normalized ? hashTarget(normalized) : "";
|
|
95
|
+
return { href: `${contact}#${hash}`, scheme: contact, hash };
|
|
96
|
+
}
|
|
97
|
+
return { href: sanitizeUrl(raw), scheme: hrefScheme(raw, pageHost), hash: "" };
|
|
98
|
+
}
|
|
99
|
+
function isSubmitControl(el) {
|
|
100
|
+
const tag = el.tagName.toLowerCase();
|
|
101
|
+
if (tag !== "button" && tag !== "input") return false;
|
|
102
|
+
const type = (el.getAttribute("type") ?? "").toLowerCase();
|
|
103
|
+
if (type === "submit") return true;
|
|
104
|
+
if (type) return false;
|
|
105
|
+
return tag === "button" && !!el.closest("form");
|
|
106
|
+
}
|
|
107
|
+
function doNotTrackEnabled() {
|
|
108
|
+
if (typeof navigator === "undefined" && typeof window === "undefined") return false;
|
|
109
|
+
const nav = typeof navigator !== "undefined" ? navigator : void 0;
|
|
110
|
+
const win = typeof window !== "undefined" ? window : void 0;
|
|
111
|
+
const signal = nav?.doNotTrack ?? win?.doNotTrack ?? nav?.msDoNotTrack;
|
|
112
|
+
return signal === "1" || signal === "yes";
|
|
113
|
+
}
|
|
114
|
+
function labelFor(element) {
|
|
115
|
+
const aria = element.getAttribute("aria-label");
|
|
116
|
+
const source = aria && aria.trim() || (element.textContent ?? "").replace(/\s+/gu, " ").trim() || element.getAttribute("title") || element.getAttribute("href") || element.tagName.toLowerCase();
|
|
117
|
+
return source.slice(0, LABEL_MAX_LENGTH);
|
|
118
|
+
}
|
|
119
|
+
function classifyHref(href, opts) {
|
|
120
|
+
const raw = href.trim();
|
|
121
|
+
if (!raw) return "button";
|
|
122
|
+
const lower = raw.toLowerCase();
|
|
123
|
+
if (lower.startsWith("tel:") || lower.startsWith("sms:")) return "phone";
|
|
124
|
+
if (lower.startsWith("mailto:")) return "email";
|
|
125
|
+
if (lower.startsWith("#") || lower.startsWith("javascript:")) return "button";
|
|
126
|
+
let url;
|
|
127
|
+
try {
|
|
128
|
+
url = new URL(raw, `https://${opts.pageHost || "localhost"}/`);
|
|
129
|
+
} catch {
|
|
130
|
+
return "internal";
|
|
131
|
+
}
|
|
132
|
+
if (hostMatches(url.hostname, opts.bookingHosts)) return "booking";
|
|
133
|
+
const sameOrigin = !!opts.pageHost && url.hostname.toLowerCase() === opts.pageHost.toLowerCase();
|
|
134
|
+
if (sameOrigin && opts.bookingPaths.some((p) => url.pathname.startsWith(p))) {
|
|
135
|
+
return "booking";
|
|
136
|
+
}
|
|
137
|
+
if (hostMatches(url.hostname, opts.socialHosts)) return "social";
|
|
138
|
+
if (sameOrigin) return "internal";
|
|
139
|
+
if (url.protocol === "http:" || url.protocol === "https:") return "external";
|
|
140
|
+
return "external";
|
|
141
|
+
}
|
|
142
|
+
var registeredTrackers = /* @__PURE__ */ new Set();
|
|
143
|
+
var boundDocuments = /* @__PURE__ */ new WeakSet();
|
|
144
|
+
var warnedDoubleBind = false;
|
|
145
|
+
function attachConversionSink(sink) {
|
|
146
|
+
let count = 0;
|
|
147
|
+
for (const tracker of registeredTrackers) {
|
|
148
|
+
tracker.attachSink(sink);
|
|
149
|
+
count += 1;
|
|
150
|
+
}
|
|
151
|
+
return count;
|
|
152
|
+
}
|
|
153
|
+
function createConversionTracker(options = {}) {
|
|
154
|
+
const eventName = options.eventName ?? "site_interaction";
|
|
155
|
+
const bookingHosts = [...DEFAULT_BOOKING_HOSTS, ...options.bookingHosts ?? []];
|
|
156
|
+
const socialHosts = [...DEFAULT_SOCIAL_HOSTS, ...options.socialHosts ?? []];
|
|
157
|
+
const bookingPaths = options.bookingPaths ?? [];
|
|
158
|
+
const bufferLimit = Math.max(0, options.bufferLimit ?? DEFAULT_BUFFER_LIMIT);
|
|
159
|
+
const requireTrusted = options.requireTrusted ?? false;
|
|
160
|
+
const captureFormSubmits = options.captureFormSubmits ?? true;
|
|
161
|
+
const respectDoNotTrack = options.respectDoNotTrack ?? true;
|
|
162
|
+
const force = options.force ?? false;
|
|
163
|
+
const mirrors = options.mirrors ?? [];
|
|
164
|
+
let sink = options.sink ?? null;
|
|
165
|
+
let buffer = [];
|
|
166
|
+
let listening = false;
|
|
167
|
+
let lastKey = "";
|
|
168
|
+
let lastAt = 0;
|
|
169
|
+
const doc = () => options.target ?? (typeof document !== "undefined" ? document : null);
|
|
170
|
+
function pageHost() {
|
|
171
|
+
return typeof window !== "undefined" ? window.location.hostname : "";
|
|
172
|
+
}
|
|
173
|
+
function pagePath() {
|
|
174
|
+
return typeof window !== "undefined" ? window.location.pathname : "";
|
|
175
|
+
}
|
|
176
|
+
function emit(event) {
|
|
177
|
+
for (const mirror of mirrors) {
|
|
178
|
+
try {
|
|
179
|
+
mirror(event);
|
|
180
|
+
} catch {
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (sink) {
|
|
184
|
+
try {
|
|
185
|
+
sink(event);
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (bufferLimit === 0) return;
|
|
191
|
+
buffer.push(event);
|
|
192
|
+
if (buffer.length > bufferLimit) buffer.shift();
|
|
193
|
+
}
|
|
194
|
+
function compose(actionable, interactionType, rawHref) {
|
|
195
|
+
const host = pageHost();
|
|
196
|
+
const { href, scheme, hash } = redactHref(rawHref, host);
|
|
197
|
+
let hrefHost = "";
|
|
198
|
+
if (href && !hash) {
|
|
199
|
+
try {
|
|
200
|
+
hrefHost = new URL(href, `https://${host || "localhost"}/`).hostname;
|
|
201
|
+
} catch {
|
|
202
|
+
hrefHost = "";
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
name: eventName,
|
|
207
|
+
properties: {
|
|
208
|
+
interaction_type: interactionType,
|
|
209
|
+
is_conversion: isConversionType(interactionType) ? "true" : "false",
|
|
210
|
+
label: labelFor(actionable),
|
|
211
|
+
href,
|
|
212
|
+
href_scheme: scheme,
|
|
213
|
+
href_host: hrefHost,
|
|
214
|
+
href_hash: hash,
|
|
215
|
+
page_path: pagePath(),
|
|
216
|
+
host,
|
|
217
|
+
capture_version: CONVERSION_CAPTURE_VERSION
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function build(element) {
|
|
222
|
+
const anchor = element.closest("a");
|
|
223
|
+
const actionable = anchor ?? element.closest("button");
|
|
224
|
+
if (!actionable) return null;
|
|
225
|
+
if (captureFormSubmits && !anchor && isSubmitControl(actionable)) return null;
|
|
226
|
+
const rawHref = anchor?.getAttribute("href") ?? "";
|
|
227
|
+
const interactionType = anchor ? classifyHref(rawHref, { pageHost: pageHost(), bookingHosts, bookingPaths, socialHosts }) : "button";
|
|
228
|
+
const resolved = anchor && !CONTACT_SCHEMES.some((s) => rawHref.trim().toLowerCase().startsWith(s)) ? anchor.href || rawHref : rawHref;
|
|
229
|
+
return compose(actionable, interactionType, resolved);
|
|
230
|
+
}
|
|
231
|
+
function buildSubmit(form) {
|
|
232
|
+
const control = form.querySelector('button[type="submit"], input[type="submit"]') ?? form.querySelector("button") ?? form;
|
|
233
|
+
return compose(control, "form_submit", "");
|
|
234
|
+
}
|
|
235
|
+
function emitDeduped(event) {
|
|
236
|
+
const key = `${event.properties.interaction_type}|${event.properties.href}|${event.properties.label}`;
|
|
237
|
+
const now = Date.now();
|
|
238
|
+
if (key === lastKey && now - lastAt < DEDUPE_WINDOW_MS) return null;
|
|
239
|
+
lastKey = key;
|
|
240
|
+
lastAt = now;
|
|
241
|
+
emit(event);
|
|
242
|
+
return event;
|
|
243
|
+
}
|
|
244
|
+
function capture(element) {
|
|
245
|
+
const event = build(element);
|
|
246
|
+
if (!event) return null;
|
|
247
|
+
return emitDeduped(event);
|
|
248
|
+
}
|
|
249
|
+
const onClick = (event) => {
|
|
250
|
+
if (requireTrusted && !event.isTrusted) return;
|
|
251
|
+
const target = event.target;
|
|
252
|
+
if (!target || !target.closest) return;
|
|
253
|
+
try {
|
|
254
|
+
capture(target);
|
|
255
|
+
} catch {
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
const onSubmit = (event) => {
|
|
259
|
+
if (requireTrusted && !event.isTrusted) return;
|
|
260
|
+
const form = event.target;
|
|
261
|
+
if (!form || !form.tagName) return;
|
|
262
|
+
try {
|
|
263
|
+
emitDeduped(buildSubmit(form));
|
|
264
|
+
} catch {
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
const tracker = {
|
|
268
|
+
start() {
|
|
269
|
+
if (listening) return;
|
|
270
|
+
const d = doc();
|
|
271
|
+
if (!d) return;
|
|
272
|
+
if (respectDoNotTrack && doNotTrackEnabled()) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (boundDocuments.has(d) && !force) {
|
|
276
|
+
if (!warnedDoubleBind) {
|
|
277
|
+
warnedDoubleBind = true;
|
|
278
|
+
console.warn(
|
|
279
|
+
"[dcs-conversion] start() REFUSED: a delegated conversion listener is already bound to this document. Binding a second one counts every click twice \u2014 the defect class that put 52% duplicate page views in a live owner report (C-288). Since C-326 the tracker auto-installs with @duffcloudservices/cms, so a manual useConversionTracking() in App.vue is no longer needed; remove it, or pass { force: true } if you have genuinely torn the first one down."
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
d.addEventListener("click", onClick, { capture: true });
|
|
285
|
+
if (captureFormSubmits) d.addEventListener("submit", onSubmit, { capture: true });
|
|
286
|
+
listening = true;
|
|
287
|
+
boundDocuments.add(d);
|
|
288
|
+
registeredTrackers.add(tracker);
|
|
289
|
+
},
|
|
290
|
+
stop() {
|
|
291
|
+
const d = doc();
|
|
292
|
+
if (d && listening) {
|
|
293
|
+
d.removeEventListener("click", onClick, { capture: true });
|
|
294
|
+
if (captureFormSubmits) d.removeEventListener("submit", onSubmit, { capture: true });
|
|
295
|
+
boundDocuments.delete(d);
|
|
296
|
+
}
|
|
297
|
+
listening = false;
|
|
298
|
+
buffer = [];
|
|
299
|
+
registeredTrackers.delete(tracker);
|
|
300
|
+
},
|
|
301
|
+
attachSink(next) {
|
|
302
|
+
sink = next;
|
|
303
|
+
const pending = buffer;
|
|
304
|
+
buffer = [];
|
|
305
|
+
for (const event of pending) {
|
|
306
|
+
try {
|
|
307
|
+
next(event);
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
buffered() {
|
|
313
|
+
return [...buffer];
|
|
314
|
+
},
|
|
315
|
+
capture
|
|
316
|
+
};
|
|
317
|
+
return tracker;
|
|
318
|
+
}
|
|
319
|
+
function useConversionTracking(options = {}) {
|
|
320
|
+
const tracker = createConversionTracker(options);
|
|
321
|
+
if (getCurrentInstance()) {
|
|
322
|
+
onMounted(() => tracker.start());
|
|
323
|
+
onBeforeUnmount(() => tracker.stop());
|
|
324
|
+
} else {
|
|
325
|
+
tracker.start();
|
|
326
|
+
}
|
|
327
|
+
return tracker;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/conversionAutoInstall.ts
|
|
331
|
+
var installed = null;
|
|
332
|
+
function conversionWindow() {
|
|
333
|
+
return typeof window !== "undefined" ? window : null;
|
|
334
|
+
}
|
|
335
|
+
function conversionOptedOut() {
|
|
336
|
+
const w = conversionWindow();
|
|
337
|
+
if (w?.__dcsConversionOptOut === true) return true;
|
|
338
|
+
if (typeof document !== "undefined") {
|
|
339
|
+
const flag = document.documentElement?.getAttribute("data-dcs-analytics");
|
|
340
|
+
if (flag === "off" || flag === "false") return true;
|
|
341
|
+
}
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
function inFrame() {
|
|
345
|
+
const w = conversionWindow();
|
|
346
|
+
if (!w) return false;
|
|
347
|
+
try {
|
|
348
|
+
return w.parent !== w || w.top !== w.self;
|
|
349
|
+
} catch {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function gtagSink(event) {
|
|
354
|
+
const w = conversionWindow();
|
|
355
|
+
if (typeof w?.gtag !== "function") return;
|
|
356
|
+
w.gtag("event", event.name, { ...event.properties });
|
|
357
|
+
}
|
|
358
|
+
function installConversionCapture(options = {}) {
|
|
359
|
+
const skip = (reason) => ({
|
|
360
|
+
installed: false,
|
|
361
|
+
reason,
|
|
362
|
+
tracker: null
|
|
363
|
+
});
|
|
364
|
+
if (typeof document === "undefined") return skip("no-document");
|
|
365
|
+
const w = conversionWindow();
|
|
366
|
+
if (installed || w?.__dcsConversionInstalled === true) return skip("already-installed");
|
|
367
|
+
if (inFrame()) return skip("in-frame");
|
|
368
|
+
if ((options.respectDoNotTrack ?? true) && doNotTrackEnabled()) return skip("do-not-track");
|
|
369
|
+
if (conversionOptedOut()) return skip("opted-out");
|
|
370
|
+
const useGtag = options.gtag ?? true;
|
|
371
|
+
const tracker = createConversionTracker({
|
|
372
|
+
...options,
|
|
373
|
+
sink: void 0,
|
|
374
|
+
mirrors: useGtag ? [gtagSink] : []
|
|
375
|
+
});
|
|
376
|
+
tracker.start();
|
|
377
|
+
installed = tracker;
|
|
378
|
+
if (w) {
|
|
379
|
+
w.__dcsConversionInstalled = true;
|
|
380
|
+
w.__dcsConversionAttach = (sink) => attachConversionSink(sink);
|
|
381
|
+
}
|
|
382
|
+
return { installed: true, reason: null, tracker };
|
|
383
|
+
}
|
|
384
|
+
function installedConversionTracker() {
|
|
385
|
+
return installed;
|
|
386
|
+
}
|
|
387
|
+
function resetConversionCapture() {
|
|
388
|
+
installed?.stop();
|
|
389
|
+
installed = null;
|
|
390
|
+
const w = conversionWindow();
|
|
391
|
+
if (w) {
|
|
392
|
+
delete w.__dcsConversionInstalled;
|
|
393
|
+
delete w.__dcsConversionAttach;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
9
396
|
var fetchCache = /* @__PURE__ */ new Map();
|
|
10
397
|
function getBuildTimeContent() {
|
|
11
398
|
try {
|
|
@@ -102,7 +489,7 @@ function useTextContent(config) {
|
|
|
102
489
|
error.value = null;
|
|
103
490
|
try {
|
|
104
491
|
const url = `${apiBaseUrl}/api/v1/pages/${pageSlug}/text`;
|
|
105
|
-
const response = await
|
|
492
|
+
const response = await platformFetch(url, {
|
|
106
493
|
headers: {
|
|
107
494
|
Accept: "application/json"
|
|
108
495
|
}
|
|
@@ -163,6 +550,15 @@ function getBuildTimeSeo() {
|
|
|
163
550
|
}
|
|
164
551
|
return void 0;
|
|
165
552
|
}
|
|
553
|
+
var HEAD_OVERRIDE_REFUSED_MESSAGE = "[dcs-seo] applyHead() was called WITH AN ARGUMENT and the argument was IGNORED. .dcs/seo.yaml is the only writer of title/description/keywords/canonical/OG/Twitter/JSON-LD (head-authority contract, C-356). A hardcoded value here overwrites the owner-approved baked head for Google and every human while non-JS crawlers keep receiving the approved one. Move the value into .dcs/seo.yaml and call applyHead() with no arguments.";
|
|
554
|
+
function reportHeadOverrideRefused(pageSlug, refused) {
|
|
555
|
+
const keys = refused && typeof refused === "object" ? Object.keys(refused).join(", ") : String(refused);
|
|
556
|
+
console.error(
|
|
557
|
+
`${HEAD_OVERRIDE_REFUSED_MESSAGE}
|
|
558
|
+
page slug: ${pageSlug}
|
|
559
|
+
ignored keys: ${keys}`
|
|
560
|
+
);
|
|
561
|
+
}
|
|
166
562
|
function useSEO(pageSlug, pagePath) {
|
|
167
563
|
const seoConfig = getBuildTimeSeo();
|
|
168
564
|
const hasBuildTimeSeo = seoConfig !== void 0;
|
|
@@ -175,14 +571,11 @@ function useSEO(pageSlug, pagePath) {
|
|
|
175
571
|
function getCanonical() {
|
|
176
572
|
return config.value.canonical;
|
|
177
573
|
}
|
|
178
|
-
function
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
schemas: overrides?.schemas,
|
|
184
|
-
meta: overrides?.meta
|
|
185
|
-
});
|
|
574
|
+
function applyHeadImpl(...refused) {
|
|
575
|
+
if (refused.length > 0 && refused[0] !== void 0 && refused[0] !== null) {
|
|
576
|
+
reportHeadOverrideRefused(pageSlug, refused[0]);
|
|
577
|
+
}
|
|
578
|
+
const { title, meta, link, script } = buildHeadTags(pageSlug, pagePath, seoConfig);
|
|
186
579
|
useHead({
|
|
187
580
|
title,
|
|
188
581
|
meta,
|
|
@@ -190,6 +583,7 @@ function useSEO(pageSlug, pagePath) {
|
|
|
190
583
|
script
|
|
191
584
|
});
|
|
192
585
|
}
|
|
586
|
+
const applyHead = applyHeadImpl;
|
|
193
587
|
return {
|
|
194
588
|
config,
|
|
195
589
|
applyHead,
|
|
@@ -233,7 +627,7 @@ function useReleaseNotes(version, options = {}) {
|
|
|
233
627
|
error.value = null;
|
|
234
628
|
try {
|
|
235
629
|
const url = `${apiBaseUrl}/api/v1/release-notes/${version}`;
|
|
236
|
-
const response = await
|
|
630
|
+
const response = await platformFetch(url, {
|
|
237
631
|
headers: {
|
|
238
632
|
Accept: "application/json"
|
|
239
633
|
}
|
|
@@ -313,7 +707,7 @@ function useSiteVersion(options = {}) {
|
|
|
313
707
|
isLoading.value = true;
|
|
314
708
|
try {
|
|
315
709
|
const url = `${apiBaseUrl}/api/v1/release-notes/latest`;
|
|
316
|
-
const response = await
|
|
710
|
+
const response = await platformFetch(url, {
|
|
317
711
|
headers: {
|
|
318
712
|
Accept: "application/json"
|
|
319
713
|
}
|
|
@@ -397,7 +791,9 @@ function useResponsiveImage(options) {
|
|
|
397
791
|
alt: toValue(options.alt),
|
|
398
792
|
context: toValue(options.context),
|
|
399
793
|
sizes: toValue(options.sizes),
|
|
400
|
-
original: toValue(options.original) ?? void 0
|
|
794
|
+
original: toValue(options.original) ?? void 0,
|
|
795
|
+
width: toValue(options.width) ?? void 0,
|
|
796
|
+
height: toValue(options.height) ?? void 0
|
|
401
797
|
})
|
|
402
798
|
);
|
|
403
799
|
return {
|
|
@@ -538,13 +934,16 @@ async function fetchSiteVisitorSession(options = {}) {
|
|
|
538
934
|
const base = apiBase(options.apiBaseUrl);
|
|
539
935
|
let response;
|
|
540
936
|
try {
|
|
541
|
-
response = await
|
|
937
|
+
response = await platformFetch(`${base}/site-auth/session`, {
|
|
542
938
|
method: "GET",
|
|
543
939
|
credentials: "include",
|
|
544
940
|
headers: { Accept: "application/json" },
|
|
545
941
|
signal: options.signal
|
|
546
942
|
});
|
|
547
|
-
} catch {
|
|
943
|
+
} catch (e) {
|
|
944
|
+
if (isPlatformFetchError(e)) {
|
|
945
|
+
return { visitor: null, authenticated: false, apiUnreachable: true };
|
|
946
|
+
}
|
|
548
947
|
return SIGNED_OUT;
|
|
549
948
|
}
|
|
550
949
|
if (!response.ok) {
|
|
@@ -563,12 +962,14 @@ function useSiteVisitorSession(options = {}) {
|
|
|
563
962
|
const { fetchOnMount = true, ...fetchOptions } = options;
|
|
564
963
|
const visitor = ref(null);
|
|
565
964
|
const isLoading = ref(false);
|
|
965
|
+
const apiUnreachable = ref(false);
|
|
566
966
|
const isAuthenticated = computed(() => visitor.value !== null);
|
|
567
967
|
async function refresh() {
|
|
568
968
|
isLoading.value = true;
|
|
569
969
|
try {
|
|
570
970
|
const result = await fetchSiteVisitorSession(fetchOptions);
|
|
571
971
|
visitor.value = result.visitor;
|
|
972
|
+
apiUnreachable.value = result.apiUnreachable === true;
|
|
572
973
|
} finally {
|
|
573
974
|
isLoading.value = false;
|
|
574
975
|
}
|
|
@@ -578,9 +979,10 @@ function useSiteVisitorSession(options = {}) {
|
|
|
578
979
|
void refresh();
|
|
579
980
|
});
|
|
580
981
|
}
|
|
581
|
-
return { visitor, isAuthenticated, isLoading, refresh };
|
|
982
|
+
return { visitor, isAuthenticated, isLoading, apiUnreachable, refresh };
|
|
582
983
|
}
|
|
984
|
+
installConversionCapture();
|
|
583
985
|
|
|
584
|
-
export { createSiteSEO, fetchSiteVisitorSession, useMediaCarousel, useReleaseNotes, useResponsiveImage, useReviewContent, useSEO, useSiteVersion, useSiteVisitorSession, useTextContent };
|
|
986
|
+
export { CONVERSION_CAPTURE_VERSION, CONVERSION_INTERACTION_TYPES, DEFAULT_BOOKING_HOSTS, DEFAULT_SOCIAL_HOSTS, attachConversionSink, classifyHref, conversionOptedOut, createConversionTracker, createSiteSEO, doNotTrackEnabled, fetchSiteVisitorSession, hashTarget, hrefScheme, inFrame, installConversionCapture, installedConversionTracker, isConversionType, isSubmitControl, redactHref, resetConversionCapture, useConversionTracking, useMediaCarousel, useReleaseNotes, useResponsiveImage, useReviewContent, useSEO, useSiteVersion, useSiteVisitorSession, useTextContent };
|
|
585
987
|
//# sourceMappingURL=index.js.map
|
|
586
988
|
//# sourceMappingURL=index.js.map
|