@guidelytech/sdk-core 0.5.0 → 0.6.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.
- package/dist/capture-C3faAYb7.d.cts +99 -0
- package/dist/capture-Dqd7up2n.d.ts +99 -0
- package/dist/capture.cjs +313 -0
- package/dist/capture.cjs.map +1 -0
- package/dist/capture.d.cts +3 -0
- package/dist/capture.d.ts +3 -0
- package/dist/capture.js +235 -0
- package/dist/capture.js.map +1 -0
- package/dist/{chunk-GJT7CFAC.js → chunk-AKNE7D5M.js} +14 -5
- package/dist/chunk-AKNE7D5M.js.map +1 -0
- package/dist/chunk-CYAG6ZYX.js +85 -0
- package/dist/chunk-CYAG6ZYX.js.map +1 -0
- package/dist/{chunk-HLR3W63Z.js → chunk-GDF6RCHQ.js} +6 -79
- package/dist/chunk-GDF6RCHQ.js.map +1 -0
- package/dist/headless.cjs +18 -5
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.d.cts +3 -49
- package/dist/headless.d.ts +3 -49
- package/dist/headless.js +6 -4
- package/dist/http-47tiAlOM.d.cts +86 -0
- package/dist/http-47tiAlOM.d.ts +86 -0
- package/dist/index.cjs +309 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -4
- package/dist/index.d.ts +23 -4
- package/dist/index.js +273 -39
- package/dist/index.js.map +1 -1
- package/dist/recorder.cjs +1 -1
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.js +2 -2
- package/dist/recorder.js.map +1 -1
- package/dist/{types-BZ9r3Fs9.d.cts → types-RyeRQd9f.d.ts} +9 -30
- package/dist/{types-BZ9r3Fs9.d.ts → types-d4eOerxi.d.cts} +9 -30
- package/package.json +8 -3
- package/dist/chunk-GJT7CFAC.js.map +0 -1
- package/dist/chunk-HLR3W63Z.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -33,6 +33,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
33
33
|
var index_exports = {};
|
|
34
34
|
__export(index_exports, {
|
|
35
35
|
AUTHORING_PARAM: () => AUTHORING_PARAM,
|
|
36
|
+
CAPTURE_PARAM: () => import_runtime2.CAPTURE_PARAM,
|
|
36
37
|
DEFAULT_API_URL: () => DEFAULT_API_URL,
|
|
37
38
|
EventQueue: () => EventQueue,
|
|
38
39
|
Guidely: () => Guidely,
|
|
@@ -56,9 +57,9 @@ __export(index_exports, {
|
|
|
56
57
|
module.exports = __toCommonJS(index_exports);
|
|
57
58
|
|
|
58
59
|
// src/client.ts
|
|
59
|
-
var
|
|
60
|
-
var import_guide_runtime3 = require("@guidelytech/guide-runtime");
|
|
60
|
+
var import_runtime13 = require("@guidelytech/shared/runtime");
|
|
61
61
|
var import_guide_runtime4 = require("@guidelytech/guide-runtime");
|
|
62
|
+
var import_guide_runtime5 = require("@guidelytech/guide-runtime");
|
|
62
63
|
|
|
63
64
|
// src/authoring.ts
|
|
64
65
|
var import_guide_runtime = require("@guidelytech/guide-runtime");
|
|
@@ -207,8 +208,13 @@ var KEYS = {
|
|
|
207
208
|
session: "guidely:session",
|
|
208
209
|
active: "guidely:active",
|
|
209
210
|
authoring: "guidely:authoring",
|
|
211
|
+
/** How-to capture session marker ({ token, … }) and its not-yet-uploaded steps. */
|
|
212
|
+
capture: "guidely:capture",
|
|
213
|
+
capturePending: "guidely:capture:pending",
|
|
210
214
|
/** Messenger conversations this browser can read: [{ id, token }]. */
|
|
211
|
-
conversations: (key) => `guidely:conversations:${key}
|
|
215
|
+
conversations: (key) => `guidely:conversations:${key}`,
|
|
216
|
+
/** Announcement ids opened in the messenger's News tab. */
|
|
217
|
+
newsSeen: (key) => `guidely:news:${key}`
|
|
212
218
|
};
|
|
213
219
|
|
|
214
220
|
// src/authoring.ts
|
|
@@ -440,6 +446,43 @@ var AuthoringBridge = class {
|
|
|
440
446
|
}
|
|
441
447
|
};
|
|
442
448
|
|
|
449
|
+
// src/capture-token.ts
|
|
450
|
+
var import_runtime2 = require("@guidelytech/shared/runtime");
|
|
451
|
+
function takeCaptureToken(sessionStore) {
|
|
452
|
+
if (typeof window === "undefined") return null;
|
|
453
|
+
let token = null;
|
|
454
|
+
let authoringLink = false;
|
|
455
|
+
try {
|
|
456
|
+
const url = new URL(window.location.href);
|
|
457
|
+
token = url.searchParams.get(import_runtime2.CAPTURE_PARAM);
|
|
458
|
+
authoringLink = url.searchParams.has(AUTHORING_PARAM);
|
|
459
|
+
if (token) {
|
|
460
|
+
url.searchParams.delete(import_runtime2.CAPTURE_PARAM);
|
|
461
|
+
window.history.replaceState(window.history.state, "", url.toString());
|
|
462
|
+
}
|
|
463
|
+
} catch {
|
|
464
|
+
token = null;
|
|
465
|
+
}
|
|
466
|
+
const saved = readJson(sessionStore, KEYS.capture, null);
|
|
467
|
+
let marker = null;
|
|
468
|
+
if (token) marker = saved?.token === token ? saved : { token };
|
|
469
|
+
else if (saved?.token && !authoringLink) marker = saved;
|
|
470
|
+
if (!marker) {
|
|
471
|
+
if (saved) {
|
|
472
|
+
sessionStore.remove(KEYS.capture);
|
|
473
|
+
sessionStore.remove(KEYS.capturePending);
|
|
474
|
+
}
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
if (token && saved?.token !== token) sessionStore.remove(KEYS.capturePending);
|
|
478
|
+
writeJson(sessionStore, KEYS.capture, marker);
|
|
479
|
+
sessionStore.remove(KEYS.authoring);
|
|
480
|
+
return marker;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// src/load-capture.ts
|
|
484
|
+
var loadCapture = async () => (await import("./capture.cjs")).capture;
|
|
485
|
+
|
|
443
486
|
// src/autocapture.ts
|
|
444
487
|
var import_guide_runtime2 = require("@guidelytech/guide-runtime");
|
|
445
488
|
var INTERACTIVE_SELECTOR = "a,button,input[type=button],input[type=submit],input[type=reset],input[type=checkbox],input[type=radio],select,summary,label,[role=button],[role=link],[role=tab],[role=menuitem],[role=checkbox],[role=switch],[role=option],[onclick],[data-guidely-track]";
|
|
@@ -701,7 +744,7 @@ var HeatmapBuffer = class {
|
|
|
701
744
|
};
|
|
702
745
|
|
|
703
746
|
// src/errors.ts
|
|
704
|
-
var
|
|
747
|
+
var import_runtime5 = require("@guidelytech/shared/runtime");
|
|
705
748
|
var MAX_FRAMES_BYTES = 6e3;
|
|
706
749
|
function fitFrames(frames) {
|
|
707
750
|
const out = frames.slice(0, 30);
|
|
@@ -722,7 +765,7 @@ function exceptionFromError(error, source, handled, fallbackType = "Error") {
|
|
|
722
765
|
return {
|
|
723
766
|
type: isError && e.name || fallbackType,
|
|
724
767
|
message: (isError ? e.message : describe(error)).slice(0, 1e3),
|
|
725
|
-
frames: isError ? fitFrames((0,
|
|
768
|
+
frames: isError ? fitFrames((0, import_runtime5.parseStack)(e.stack)) : [],
|
|
726
769
|
source,
|
|
727
770
|
handled
|
|
728
771
|
};
|
|
@@ -869,7 +912,8 @@ var Api = class {
|
|
|
869
912
|
const f = this.fetcher;
|
|
870
913
|
if (!f) throw new Error("fetch is not available");
|
|
871
914
|
const headers = { ...options.headers, "X-Guidely-Key": this.key, Accept: "application/json" };
|
|
872
|
-
|
|
915
|
+
const form = typeof FormData !== "undefined" && options.body instanceof FormData;
|
|
916
|
+
if (options.body !== void 0 && !form) headers["Content-Type"] = "application/json";
|
|
873
917
|
if (options.etag) headers["If-None-Match"] = options.etag;
|
|
874
918
|
const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
875
919
|
const timer = ac ? setTimeout(() => ac.abort(), options.timeoutMs ?? 1e4) : null;
|
|
@@ -877,7 +921,7 @@ var Api = class {
|
|
|
877
921
|
const res = await f(this.url(path, options.params), {
|
|
878
922
|
method,
|
|
879
923
|
headers,
|
|
880
|
-
body: options.body === void 0 ? void 0 : JSON.stringify(options.body),
|
|
924
|
+
body: options.body === void 0 ? void 0 : form ? options.body : JSON.stringify(options.body),
|
|
881
925
|
keepalive: options.keepalive,
|
|
882
926
|
signal: ac?.signal,
|
|
883
927
|
credentials: "omit",
|
|
@@ -902,6 +946,10 @@ var Api = class {
|
|
|
902
946
|
post(path, body, options) {
|
|
903
947
|
return this.request("POST", path, { body, ...options });
|
|
904
948
|
}
|
|
949
|
+
/** Multipart POST (file uploads). No Content-Type header: the browser adds it with the boundary. */
|
|
950
|
+
postForm(path, form, options) {
|
|
951
|
+
return this.request("POST", path, { body: form, ...options });
|
|
952
|
+
}
|
|
905
953
|
/**
|
|
906
954
|
* POST that may answer with server-sent events. Each event is passed to `onEvent`; a JSON answer is
|
|
907
955
|
* returned instead. No timeout: Scout replies can take a while and the server keeps the stream alive.
|
|
@@ -944,10 +992,10 @@ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
944
992
|
function backoffMs(attempt, baseMs = 1e3, maxMs = 3e4) {
|
|
945
993
|
return Math.min(maxMs, baseMs * 2 ** Math.max(0, attempt));
|
|
946
994
|
}
|
|
947
|
-
async function
|
|
995
|
+
async function withRetry(send, attempts = 3, baseMs = 500) {
|
|
948
996
|
for (let i = 0; i < attempts; i++) {
|
|
949
997
|
try {
|
|
950
|
-
const res = await
|
|
998
|
+
const res = await send();
|
|
951
999
|
if (res.ok || res.status >= 400 && res.status < 500 && res.status !== 429) return res;
|
|
952
1000
|
} catch {
|
|
953
1001
|
}
|
|
@@ -955,6 +1003,9 @@ async function postWithRetry(api, path, body, attempts = 3, baseMs = 500) {
|
|
|
955
1003
|
}
|
|
956
1004
|
return null;
|
|
957
1005
|
}
|
|
1006
|
+
function postWithRetry(api, path, body, attempts = 3, baseMs = 500) {
|
|
1007
|
+
return withRetry(() => api.post(path, body), attempts, baseMs);
|
|
1008
|
+
}
|
|
958
1009
|
async function readEventStream(body, onEvent) {
|
|
959
1010
|
const reader = body.getReader();
|
|
960
1011
|
const decoder = new TextDecoder();
|
|
@@ -992,6 +1043,15 @@ async function readEventStream(body, onEvent) {
|
|
|
992
1043
|
// src/messenger-api.ts
|
|
993
1044
|
var MAX_STORED = 50;
|
|
994
1045
|
var TOKEN_HEADER = "X-Guidely-Conversation";
|
|
1046
|
+
function pagePath(url) {
|
|
1047
|
+
if (!url) return void 0;
|
|
1048
|
+
try {
|
|
1049
|
+
const u = new URL(url);
|
|
1050
|
+
return (u.pathname + (/^#!?\//.test(u.hash) ? u.hash.split("?")[0] : "")).slice(0, 2e3);
|
|
1051
|
+
} catch {
|
|
1052
|
+
return void 0;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
995
1055
|
function storedConversations(store, projectId) {
|
|
996
1056
|
const list = readJson(store, KEYS.conversations(projectId), []);
|
|
997
1057
|
return Array.isArray(list) ? list.filter((c) => c && typeof c.id === "string" && typeof c.token === "string") : [];
|
|
@@ -1066,7 +1126,7 @@ function createMessengerTransport(opts) {
|
|
|
1066
1126
|
return exchange("/conversations", { anonymousId: opts.anonymousId(), userId: opts.userId(), message: text, pageUrl: opts.pageUrl() }, {}, h, (r) => remember(r.conversation.id, r.token));
|
|
1067
1127
|
},
|
|
1068
1128
|
send(id, text, h) {
|
|
1069
|
-
return exchange(`/conversations/${encodeURIComponent(id)}/messages`, { message: text }, { [TOKEN_HEADER]: tokenFor(id) }, h);
|
|
1129
|
+
return exchange(`/conversations/${encodeURIComponent(id)}/messages`, { message: text, pageUrl: opts.pageUrl() }, { [TOKEN_HEADER]: tokenFor(id) }, h);
|
|
1070
1130
|
},
|
|
1071
1131
|
async handoff(id) {
|
|
1072
1132
|
const res = await api.post(`/conversations/${encodeURIComponent(id)}/handoff`, {}, { headers: { [TOKEN_HEADER]: tokenFor(id) } });
|
|
@@ -1086,13 +1146,98 @@ function createMessengerTransport(opts) {
|
|
|
1086
1146
|
const res = await api.get(`/articles/${encodeURIComponent(id)}`);
|
|
1087
1147
|
return res.ok && res.data ? res.data.article : null;
|
|
1088
1148
|
},
|
|
1149
|
+
async pageContent(path) {
|
|
1150
|
+
const res = await api.post("/messenger/page", { anonymousId: opts.anonymousId(), userId: opts.userId(), path: path.split(/[?#]/)[0] || "/" });
|
|
1151
|
+
if (!res.ok || !res.data) throw new HttpError(res.status);
|
|
1152
|
+
return res.data;
|
|
1153
|
+
},
|
|
1089
1154
|
async searchArticles(q) {
|
|
1090
1155
|
const res = await api.get("/resource-center/search", { params: { q } });
|
|
1091
1156
|
return res.ok && res.data ? res.data.articles : [];
|
|
1157
|
+
},
|
|
1158
|
+
async listHowTos() {
|
|
1159
|
+
const res = await api.get("/how-tos", { params: { url: pagePath(opts.pageUrl()) } });
|
|
1160
|
+
if (!res.ok || !res.data) throw new HttpError(res.status);
|
|
1161
|
+
return res.data;
|
|
1162
|
+
},
|
|
1163
|
+
async getHowTo(id) {
|
|
1164
|
+
const res = await api.get(`/how-tos/${encodeURIComponent(id)}`);
|
|
1165
|
+
return res.ok && res.data ? res.data.howTo : null;
|
|
1092
1166
|
}
|
|
1093
1167
|
};
|
|
1094
1168
|
}
|
|
1095
1169
|
|
|
1170
|
+
// src/messenger-visitor.ts
|
|
1171
|
+
var import_runtime10 = require("@guidelytech/shared/runtime");
|
|
1172
|
+
var import_guide_runtime3 = require("@guidelytech/guide-runtime");
|
|
1173
|
+
var PAGE_GUIDE_KINDS = /* @__PURE__ */ new Set(["flow", "tooltip", "modal"]);
|
|
1174
|
+
var CATCH_ALL = /* @__PURE__ */ new Set(["", "*", "**", "/*", "/**", "/"]);
|
|
1175
|
+
var RESUME_WITHIN_MS = 14 * 864e5;
|
|
1176
|
+
var NEWS_WITHIN_MS = 90 * 864e5;
|
|
1177
|
+
var MAX_NEWS = 10;
|
|
1178
|
+
function pathOf(url) {
|
|
1179
|
+
try {
|
|
1180
|
+
return new URL(url, "http://x.invalid").pathname || "/";
|
|
1181
|
+
} catch {
|
|
1182
|
+
return null;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
function targetsPage(guide, url) {
|
|
1186
|
+
const c = guide.config;
|
|
1187
|
+
const checks = [...c.urlPatterns, ...c.trigger.urlPattern ? [c.trigger.urlPattern] : [], ...c.trigger.type === "url_match" && c.trigger.value ? [c.trigger.value] : [], ...c.steps[0]?.url ? [c.steps[0].url] : []];
|
|
1188
|
+
const specific = checks.filter((p) => !CATCH_ALL.has(p.trim()));
|
|
1189
|
+
return specific.length > 0 && checks.every((p) => (0, import_guide_runtime3.urlMatches)(p, url));
|
|
1190
|
+
}
|
|
1191
|
+
function plainText(html, max = 280) {
|
|
1192
|
+
const text = html.replace(/<(br|\/p|\/div|\/li|\/h[1-6])\s*\/?>/gi, " ").replace(/<[^>]*>/g, "").replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&").replace(/\s+/g, " ").trim();
|
|
1193
|
+
return text.length > max ? `${text.slice(0, max - 1).trimEnd()}\u2026` : text;
|
|
1194
|
+
}
|
|
1195
|
+
function buildMessengerVisitor(input) {
|
|
1196
|
+
const now = input.now ?? Date.now();
|
|
1197
|
+
const { guides, state } = input;
|
|
1198
|
+
const done = (g) => !!state.guides[g.id]?.completedAt;
|
|
1199
|
+
const pageGuides = guides.filter((g) => PAGE_GUIDE_KINDS.has(g.kind) && g.config.steps.length > 0 && !done(g) && targetsPage(g, input.url) && input.isTargeted(g)).slice(0, 3).map((g) => ({ id: g.id, key: g.key, name: g.name, kind: g.kind }));
|
|
1200
|
+
let resumeGuide = null;
|
|
1201
|
+
let resumeAt = 0;
|
|
1202
|
+
for (const g of guides) {
|
|
1203
|
+
const s = state.guides[g.id];
|
|
1204
|
+
if (g.kind !== "flow" || !s?.lastStepId || s.completedAt || g.config.steps.length < 2) continue;
|
|
1205
|
+
const at = Date.parse(s.lastShownAt ?? "");
|
|
1206
|
+
if (!Number.isFinite(at) || now - at > RESUME_WITHIN_MS || at <= resumeAt) continue;
|
|
1207
|
+
const stepIndex = g.config.steps.findIndex((st) => st.id === s.lastStepId);
|
|
1208
|
+
if (stepIndex < 1 || !isWithinSchedule(g)) continue;
|
|
1209
|
+
resumeGuide = { id: g.id, name: g.name, stepId: s.lastStepId, stepIndex, stepCount: g.config.steps.length };
|
|
1210
|
+
resumeAt = at;
|
|
1211
|
+
}
|
|
1212
|
+
let checklist = null;
|
|
1213
|
+
for (const g of guides) {
|
|
1214
|
+
const tasks = g.config.checklist?.tasks;
|
|
1215
|
+
if (g.kind !== "checklist" || !tasks?.length) continue;
|
|
1216
|
+
const p = state.checklists[g.id];
|
|
1217
|
+
if (p?.completedAt || p?.dismissedAt || !input.isAudienceTargeted(g)) continue;
|
|
1218
|
+
const completed = new Set(p?.completedTaskIds ?? []);
|
|
1219
|
+
const doneCount = tasks.filter((t) => completed.has(t.id)).length;
|
|
1220
|
+
if (doneCount >= tasks.length) continue;
|
|
1221
|
+
checklist = { id: g.id, name: g.name, done: doneCount, total: tasks.length, nextTask: tasks.find((t) => !completed.has(t.id))?.title ?? null };
|
|
1222
|
+
break;
|
|
1223
|
+
}
|
|
1224
|
+
const seen = new Set(input.newsSeen);
|
|
1225
|
+
const news = guides.filter((g) => g.kind === "announcement" && g.config.steps.length > 0 && now - Date.parse(g.publishedAt) <= NEWS_WITHIN_MS && input.isAudienceTargeted(g)).sort((a, b) => b.publishedAt.localeCompare(a.publishedAt)).slice(0, MAX_NEWS).map((g) => {
|
|
1226
|
+
const step = g.config.steps[0];
|
|
1227
|
+
return {
|
|
1228
|
+
id: g.id,
|
|
1229
|
+
key: g.key,
|
|
1230
|
+
title: step.title || g.name,
|
|
1231
|
+
body: plainText(step.body ?? ""),
|
|
1232
|
+
imageUrl: step.imageUrl || null,
|
|
1233
|
+
publishedAt: g.publishedAt,
|
|
1234
|
+
// Read once opened in the News tab, or once the announcement itself was shown.
|
|
1235
|
+
unread: !seen.has(g.id) && !(state.guides[g.id]?.impressions ?? 0)
|
|
1236
|
+
};
|
|
1237
|
+
});
|
|
1238
|
+
return { firstName: (0, import_runtime10.firstNameFromTraits)(input.traits), path: pathOf(input.url), pageGuides, resumeGuide, checklist, news };
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1096
1241
|
// src/navigation.ts
|
|
1097
1242
|
function watchNavigation(listener, debounceMs = 50) {
|
|
1098
1243
|
if (typeof window === "undefined") return () => {
|
|
@@ -1311,7 +1456,7 @@ var loadRecorder = async () => (await import("./recorder.cjs")).recorder;
|
|
|
1311
1456
|
|
|
1312
1457
|
// src/client.ts
|
|
1313
1458
|
var SDK_NAME = "@guidelytech/sdk-core";
|
|
1314
|
-
var SDK_VERSION = "0.
|
|
1459
|
+
var SDK_VERSION = "0.6.1";
|
|
1315
1460
|
var DEFAULT_API_URL = "https://api.guidely.app";
|
|
1316
1461
|
var PAGE_SCOPED = /* @__PURE__ */ new Set(["page_load", "url_match", "element_click", "element_visible", "delay"]);
|
|
1317
1462
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -1326,7 +1471,7 @@ function emptyState() {
|
|
|
1326
1471
|
return { guides: {}, checklists: {}, surveysSubmitted: [], audienceIds: [] };
|
|
1327
1472
|
}
|
|
1328
1473
|
function newSession(now = Date.now()) {
|
|
1329
|
-
return { id: `s_${(0,
|
|
1474
|
+
return { id: `s_${(0, import_runtime13.randomId)(16)}`, startedAt: new Date(now).toISOString(), shownGuides: [], events: [], lastActivityAt: now };
|
|
1330
1475
|
}
|
|
1331
1476
|
var GuidelyClient = class _GuidelyClient {
|
|
1332
1477
|
constructor(options) {
|
|
@@ -1362,6 +1507,9 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1362
1507
|
__publicField(this, "firstVisit");
|
|
1363
1508
|
__publicField(this, "lastUrl");
|
|
1364
1509
|
__publicField(this, "authoring", null);
|
|
1510
|
+
__publicField(this, "capture", null);
|
|
1511
|
+
/** A capture token was found and the (lazily loaded) capture bridge is starting. */
|
|
1512
|
+
__publicField(this, "captureStarting", false);
|
|
1365
1513
|
__publicField(this, "lastAudiences", /* @__PURE__ */ new Set());
|
|
1366
1514
|
/** Set once the app calls identify()/reset(); bootstrap then leaves identity alone instead of re-identifying. */
|
|
1367
1515
|
__publicField(this, "identitySettled", false);
|
|
@@ -1412,7 +1560,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1412
1560
|
this.sessionStore = this.options.storage === "memory" ? this.store : createStorage("session");
|
|
1413
1561
|
this.anonymousId = this.store.get(KEYS.anon) || "";
|
|
1414
1562
|
if (!this.anonymousId) {
|
|
1415
|
-
this.anonymousId = `anon_${(0,
|
|
1563
|
+
this.anonymousId = `anon_${(0, import_runtime13.randomId)(20)}`;
|
|
1416
1564
|
this.store.set(KEYS.anon, this.anonymousId);
|
|
1417
1565
|
}
|
|
1418
1566
|
this.firstVisit = !this.store.get(KEYS.seen);
|
|
@@ -1470,10 +1618,11 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1470
1618
|
this.isReady = true;
|
|
1471
1619
|
if (this.config) this.emitter.emit("ready", this.config);
|
|
1472
1620
|
this.resolveReady?.();
|
|
1621
|
+
const capturing = !this.options.disableAuthoring && this.maybeStartCapture();
|
|
1473
1622
|
this.renderPersistentWidgets();
|
|
1474
1623
|
this.resumeActiveGuide();
|
|
1475
1624
|
this.evaluate("ready");
|
|
1476
|
-
if (!this.options.disableAuthoring) this.maybeStartAuthoring();
|
|
1625
|
+
if (!this.options.disableAuthoring && !capturing) this.maybeStartAuthoring();
|
|
1477
1626
|
this.pageTracker.start(location.href);
|
|
1478
1627
|
this.touchSession();
|
|
1479
1628
|
this.syncCapture();
|
|
@@ -1571,7 +1720,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1571
1720
|
const refresh = setInterval(() => void this.refresh(), this.options.configRefreshMs);
|
|
1572
1721
|
this.cleanups.push(() => clearInterval(refresh));
|
|
1573
1722
|
const poll = setInterval(() => {
|
|
1574
|
-
if (!this.isReady || !this.config || this.
|
|
1723
|
+
if (!this.isReady || !this.config || this.editing() || doc.visibilityState === "hidden") return;
|
|
1575
1724
|
const url = location.href;
|
|
1576
1725
|
const ctx = this.getContext();
|
|
1577
1726
|
for (const guide of this.config.guides) {
|
|
@@ -1629,12 +1778,14 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1629
1778
|
this.cleanups.splice(0).forEach((fn) => fn());
|
|
1630
1779
|
this.authoring?.stop();
|
|
1631
1780
|
this.authoring = null;
|
|
1781
|
+
this.capture?.destroy();
|
|
1782
|
+
this.capture = null;
|
|
1632
1783
|
this.replay?.destroy();
|
|
1633
1784
|
this.replay = null;
|
|
1634
1785
|
this.queue.flushSync();
|
|
1635
1786
|
this.queue.destroy();
|
|
1636
1787
|
this.emitter.clear();
|
|
1637
|
-
(0,
|
|
1788
|
+
(0, import_guide_runtime4.destroyHost)();
|
|
1638
1789
|
if (current === this) current = null;
|
|
1639
1790
|
}
|
|
1640
1791
|
// ---------------------------------------------------------------------------
|
|
@@ -1646,7 +1797,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1646
1797
|
this.identitySettled = true;
|
|
1647
1798
|
this.user = { id: String(userId), traits: { ...this.user?.id === String(userId) ? this.user.traits : {}, ...traits || {} }, account: account ?? (this.user?.id === String(userId) ? this.user.account : void 0) };
|
|
1648
1799
|
writeJson(this.store, KEYS.user(this.options.projectId), this.user);
|
|
1649
|
-
this.enqueue("identify", { properties: { traits: (0,
|
|
1800
|
+
this.enqueue("identify", { properties: { traits: (0, import_runtime13.sanitizeProperties)(this.user.traits) } });
|
|
1650
1801
|
await this.syncIdentify();
|
|
1651
1802
|
this.emitter.emit("identify", { userId: this.user.id, anonymousId: this.anonymousId });
|
|
1652
1803
|
if (this.isReady) this.evaluate("identify");
|
|
@@ -1661,6 +1812,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1661
1812
|
for (const [id, s] of Object.entries(data.guideState || {})) {
|
|
1662
1813
|
const local = guides[id];
|
|
1663
1814
|
guides[id] = {
|
|
1815
|
+
...local,
|
|
1664
1816
|
impressions: Math.max(local?.impressions ?? 0, s.impressions ?? 0),
|
|
1665
1817
|
completedAt: local?.completedAt ?? s.completedAt ?? null,
|
|
1666
1818
|
dismissedAt: local?.dismissedAt ?? s.dismissedAt ?? null,
|
|
@@ -1689,6 +1841,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1689
1841
|
const g = this.findGuide(id);
|
|
1690
1842
|
if (w.checklist && g) w.checklist.setProgress(this.getChecklistProgress(g.id));
|
|
1691
1843
|
});
|
|
1844
|
+
this.messenger?.refresh();
|
|
1692
1845
|
}
|
|
1693
1846
|
/** Sets or replaces the account (company/organisation) for the current user. */
|
|
1694
1847
|
async setAccount(account) {
|
|
@@ -1713,7 +1866,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1713
1866
|
this.stopGuide();
|
|
1714
1867
|
this.user = null;
|
|
1715
1868
|
this.store.remove(KEYS.user(this.options.projectId));
|
|
1716
|
-
this.anonymousId = `anon_${(0,
|
|
1869
|
+
this.anonymousId = `anon_${(0, import_runtime13.randomId)(20)}`;
|
|
1717
1870
|
this.store.set(KEYS.anon, this.anonymousId);
|
|
1718
1871
|
this.state = emptyState();
|
|
1719
1872
|
this.persistState();
|
|
@@ -1755,7 +1908,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1755
1908
|
if (this.consent === "denied" || !this.options.analytics) return null;
|
|
1756
1909
|
if (name !== "page_leave" && name !== "heatmap") this.touchSession();
|
|
1757
1910
|
const event = {
|
|
1758
|
-
eventId: (0,
|
|
1911
|
+
eventId: (0, import_runtime13.randomId)(24),
|
|
1759
1912
|
name,
|
|
1760
1913
|
occurredAt: nowIso(),
|
|
1761
1914
|
anonymousId: this.anonymousId,
|
|
@@ -1765,7 +1918,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1765
1918
|
url: this.collectUrls() ? location.href : void 0,
|
|
1766
1919
|
...extra
|
|
1767
1920
|
};
|
|
1768
|
-
if (extra.properties) event.properties = (0,
|
|
1921
|
+
if (extra.properties) event.properties = (0, import_runtime13.sanitizeProperties)(extra.properties);
|
|
1769
1922
|
this.queue.enqueue(event);
|
|
1770
1923
|
this.replay?.addCustomEvent("guidely", { name, customName: event.customName, guideId: event.guideId });
|
|
1771
1924
|
this.emitter.emit("track", event);
|
|
@@ -1810,16 +1963,17 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1810
1963
|
const st = this.active.controller.getState();
|
|
1811
1964
|
if (st.status === "running" && !this.active.manual) {
|
|
1812
1965
|
const g = this.active.guide;
|
|
1813
|
-
if (g.config.urlPatterns.length && !g.config.urlPatterns.some((p) => (0,
|
|
1966
|
+
if (g.config.urlPatterns.length && !g.config.urlPatterns.some((p) => (0, import_guide_runtime4.urlMatches)(p, url))) this.active.controller.destroy();
|
|
1814
1967
|
}
|
|
1815
1968
|
}
|
|
1816
1969
|
this.widgets.forEach((w, id) => {
|
|
1817
1970
|
if (!w.controller || w.manual) return;
|
|
1818
1971
|
const g = this.findGuide(id);
|
|
1819
|
-
if (g?.config.urlPatterns.length && !g.config.urlPatterns.some((p) => (0,
|
|
1972
|
+
if (g?.config.urlPatterns.length && !g.config.urlPatterns.some((p) => (0, import_guide_runtime4.urlMatches)(p, url))) w.controller.destroy();
|
|
1820
1973
|
});
|
|
1821
1974
|
this.firedDelays.clear();
|
|
1822
1975
|
if (this.isReady) this.evaluate("navigation");
|
|
1976
|
+
if (changed) this.messenger?.refresh();
|
|
1823
1977
|
if (changed) void this.queue.flush();
|
|
1824
1978
|
}
|
|
1825
1979
|
handleDocumentClick(e) {
|
|
@@ -1850,9 +2004,13 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1850
2004
|
if (this.replay?.activeSessionId) this.replay.stop();
|
|
1851
2005
|
this.syncReplay();
|
|
1852
2006
|
}
|
|
1853
|
-
/** Autocapture, error tracking and replay need analytics on, consent granted and no authoring session. */
|
|
2007
|
+
/** Autocapture, error tracking and replay need analytics on, consent granted and no authoring or how-to capture session. */
|
|
1854
2008
|
captureAllowed() {
|
|
1855
|
-
return !this.destroyed && this.analyticsEnabled() && !this.
|
|
2009
|
+
return !this.destroyed && this.analyticsEnabled() && !this.editing();
|
|
2010
|
+
}
|
|
2011
|
+
/** Authoring or how-to capture is running: no guides, widgets, replay or autocapture. */
|
|
2012
|
+
editing() {
|
|
2013
|
+
return !!this.authoring?.isActive() || this.captureStarting || !!this.capture?.isActive();
|
|
1856
2014
|
}
|
|
1857
2015
|
/** A capability is on when the project enables it (absent on old servers → off) and no option turns it off. */
|
|
1858
2016
|
autocaptureOn(key) {
|
|
@@ -1862,7 +2020,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1862
2020
|
/** Page views on SPA navigation predate autocapture: only an explicit opt-out stops them. */
|
|
1863
2021
|
pageviewsOn() {
|
|
1864
2022
|
const o = this.options.autocapture;
|
|
1865
|
-
return this.config?.settings?.autocapture?.pageviews !== false && o !== false && !(typeof o === "object" && o.pageviews === false) && !this.
|
|
2023
|
+
return this.config?.settings?.autocapture?.pageviews !== false && o !== false && !(typeof o === "object" && o.pageviews === false) && !this.editing();
|
|
1866
2024
|
}
|
|
1867
2025
|
trackPageView(url) {
|
|
1868
2026
|
if (url === this.pageViewUrl || !this.pageviewsOn()) return;
|
|
@@ -1872,7 +2030,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1872
2030
|
const device = detectDevice(navigator.userAgent, window.innerWidth);
|
|
1873
2031
|
const props = { deviceType: device.type, browser: device.browser, os: device.os, screenWidth: window.screen?.width, viewportWidth: window.innerWidth };
|
|
1874
2032
|
if (collect) {
|
|
1875
|
-
props.path =
|
|
2033
|
+
props.path = pathOf2(url);
|
|
1876
2034
|
props.title = (document.title || "").slice(0, 200);
|
|
1877
2035
|
const ref = !this.session.pageViews ? externalReferrer() : void 0;
|
|
1878
2036
|
if (ref) props.referrer = ref;
|
|
@@ -1889,7 +2047,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1889
2047
|
if (!this.pageLeft && this.autocaptureOn("pageLeave")) {
|
|
1890
2048
|
const info = t.leave();
|
|
1891
2049
|
const collect = this.collectUrls();
|
|
1892
|
-
this.enqueue("page_leave", { url: collect ? info.url : void 0, properties: { ...collect ? { path:
|
|
2050
|
+
this.enqueue("page_leave", { url: collect ? info.url : void 0, properties: { ...collect ? { path: pathOf2(info.url) } : {}, durationMs: info.durationMs, scrollDepth: info.scrollDepth } });
|
|
1893
2051
|
}
|
|
1894
2052
|
if (nextUrl !== null) {
|
|
1895
2053
|
t.start(nextUrl);
|
|
@@ -1926,7 +2084,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1926
2084
|
if (!batch || !this.captureAllowed()) return;
|
|
1927
2085
|
const collect = this.collectUrls();
|
|
1928
2086
|
const de = document.documentElement;
|
|
1929
|
-
const properties = { path: collect ?
|
|
2087
|
+
const properties = { path: collect ? pathOf2(batch.url) : void 0, viewportWidth: window.innerWidth, viewportHeight: window.innerHeight, pageHeight: Math.max(de.scrollHeight, document.body?.scrollHeight || 0), points: batch.points };
|
|
1930
2088
|
if (!collect) delete properties.path;
|
|
1931
2089
|
this.enqueue("heatmap", { url: collect ? batch.url : void 0, properties });
|
|
1932
2090
|
}
|
|
@@ -2005,7 +2163,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2005
2163
|
syncReplay() {
|
|
2006
2164
|
const cfg = this.isReady && this.captureAllowed() ? this.replayConfig() : null;
|
|
2007
2165
|
if (cfg && this.session.replay === void 0) {
|
|
2008
|
-
this.session.replay = (0,
|
|
2166
|
+
this.session.replay = (0, import_runtime13.rolloutBucket)(this.session.id, "replay") < cfg.sampleRate;
|
|
2009
2167
|
this.persistSession();
|
|
2010
2168
|
}
|
|
2011
2169
|
if (!cfg || !this.session.replay) {
|
|
@@ -2100,12 +2258,12 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2100
2258
|
}
|
|
2101
2259
|
isTargeted(guide, ctx, url) {
|
|
2102
2260
|
const c = guide.config;
|
|
2103
|
-
if (c.urlPatterns.length && !c.urlPatterns.some((p) => (0,
|
|
2104
|
-
if (c.trigger.urlPattern && !(0,
|
|
2261
|
+
if (c.urlPatterns.length && !c.urlPatterns.some((p) => (0, import_guide_runtime4.urlMatches)(p, url))) return false;
|
|
2262
|
+
if (c.trigger.urlPattern && !(0, import_guide_runtime4.urlMatches)(c.trigger.urlPattern, url)) return false;
|
|
2105
2263
|
if (!isWithinSchedule(guide)) return false;
|
|
2106
2264
|
if (guide.kind === "survey" && (this.state.surveysSubmitted.includes(guide.id) || this.state.surveysSubmitted.includes(guide.key))) return false;
|
|
2107
2265
|
ctx.rolloutSalt = guide.id;
|
|
2108
|
-
return (0,
|
|
2266
|
+
return (0, import_runtime13.evaluateRules)(c.targeting, ctx);
|
|
2109
2267
|
}
|
|
2110
2268
|
isEligible(guide, ctx, url) {
|
|
2111
2269
|
if (!this.isTargeted(guide, ctx, url)) return false;
|
|
@@ -2123,7 +2281,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2123
2281
|
if (t.delayMs > 0) return reason === "timer" && detail.guideId === guide.id;
|
|
2124
2282
|
return reason === "ready" || reason === "navigation" || reason === "identify";
|
|
2125
2283
|
case "url_match":
|
|
2126
|
-
return (reason === "ready" || reason === "navigation" || reason === "identify") && (0,
|
|
2284
|
+
return (reason === "ready" || reason === "navigation" || reason === "identify") && (0, import_guide_runtime4.urlMatches)(t.value, location.href);
|
|
2127
2285
|
case "event":
|
|
2128
2286
|
return reason === "event" && !!t.value && detail.eventName === t.value;
|
|
2129
2287
|
case "element_click":
|
|
@@ -2147,7 +2305,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2147
2305
|
}
|
|
2148
2306
|
}
|
|
2149
2307
|
evaluate(reason, detail = {}) {
|
|
2150
|
-
if (!this.config || this.destroyed || this.
|
|
2308
|
+
if (!this.config || this.destroyed || this.editing()) return;
|
|
2151
2309
|
const url = location.href;
|
|
2152
2310
|
const ctx = this.getContext();
|
|
2153
2311
|
if (reason === "identify") {
|
|
@@ -2199,7 +2357,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2199
2357
|
this.visibilityObservers.delete(guide.id);
|
|
2200
2358
|
};
|
|
2201
2359
|
this.visibilityObservers.set(guide.id, stop);
|
|
2202
|
-
(0,
|
|
2360
|
+
(0, import_guide_runtime4.waitForTarget)([{ kind: "css", value: selector }], { timeoutMs: 0, signal: ac.signal }).then((el) => {
|
|
2203
2361
|
if (ac.signal.aborted) return;
|
|
2204
2362
|
const fire = () => {
|
|
2205
2363
|
stop();
|
|
@@ -2292,10 +2450,10 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2292
2450
|
}
|
|
2293
2451
|
}
|
|
2294
2452
|
showFlow(guide, manual, stepId) {
|
|
2295
|
-
if (this.
|
|
2453
|
+
if (this.editing()) return;
|
|
2296
2454
|
const exclusive = isExclusiveGuide(guide);
|
|
2297
2455
|
const entry = { guide, manual, startUrl: location.href };
|
|
2298
|
-
const controller = (0,
|
|
2456
|
+
const controller = (0, import_guide_runtime4.runGuide)(guide, {
|
|
2299
2457
|
theme: this.theme(),
|
|
2300
2458
|
ctx: () => this.getContext(),
|
|
2301
2459
|
startAtStep: stepId,
|
|
@@ -2310,6 +2468,10 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2310
2468
|
}
|
|
2311
2469
|
if (e.name === "step_viewed" && step) {
|
|
2312
2470
|
if (exclusive) writeJson(this.sessionStore, KEYS.active, { guideId: guide.id, stepId: step.id, manual });
|
|
2471
|
+
if (guide.kind === "flow" && guide.config.steps.length > 1) {
|
|
2472
|
+
this.guideState(guide.id).lastStepId = step.id;
|
|
2473
|
+
this.persistState();
|
|
2474
|
+
}
|
|
2313
2475
|
this.emitter.emit("guide:step", { guide, stepId: step.id, stepIndex: e.stepIndex ?? 0 });
|
|
2314
2476
|
}
|
|
2315
2477
|
if (e.name === "guide_completed") {
|
|
@@ -2349,13 +2511,13 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2349
2511
|
}
|
|
2350
2512
|
}
|
|
2351
2513
|
showChecklist(guide) {
|
|
2352
|
-
if (this.
|
|
2514
|
+
if (this.editing()) return;
|
|
2353
2515
|
const config = guide.config.checklist;
|
|
2354
2516
|
if (!config || this.widgets.has(guide.id)) return;
|
|
2355
|
-
const scope = (0,
|
|
2517
|
+
const scope = (0, import_guide_runtime5.createScope)(this.theme(), guide.config.appearance);
|
|
2356
2518
|
const progress = this.getChecklistProgress(guide.id);
|
|
2357
2519
|
const first = !this.state.guides[guide.id]?.impressions;
|
|
2358
|
-
const widget = (0,
|
|
2520
|
+
const widget = (0, import_guide_runtime4.renderChecklist)({
|
|
2359
2521
|
scope: scope.el,
|
|
2360
2522
|
config,
|
|
2361
2523
|
progress,
|
|
@@ -2371,10 +2533,10 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2371
2533
|
if (first) this.recordImpression(guide);
|
|
2372
2534
|
}
|
|
2373
2535
|
showSurvey(guide, manual) {
|
|
2374
|
-
if (this.
|
|
2536
|
+
if (this.editing()) return;
|
|
2375
2537
|
const config = guide.config.survey;
|
|
2376
2538
|
if (!config) return;
|
|
2377
|
-
const scope = (0,
|
|
2539
|
+
const scope = (0, import_guide_runtime5.createScope)(this.theme(), guide.config.appearance);
|
|
2378
2540
|
const entry = { guide, manual, startUrl: location.href, scope };
|
|
2379
2541
|
const finish = (reason) => {
|
|
2380
2542
|
widget.destroy();
|
|
@@ -2387,13 +2549,13 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2387
2549
|
}
|
|
2388
2550
|
this.setTimer(() => this.evaluate("after_guide", { endedGuide: guide }), 0);
|
|
2389
2551
|
};
|
|
2390
|
-
const widget = (0,
|
|
2552
|
+
const widget = (0, import_guide_runtime4.renderSurvey)({
|
|
2391
2553
|
scope: scope.el,
|
|
2392
2554
|
config,
|
|
2393
2555
|
title: guide.name,
|
|
2394
2556
|
onDismiss: () => finish(this.state.surveysSubmitted.includes(guide.id) ? "submitted" : "dismissed"),
|
|
2395
2557
|
onSubmit: (answers) => {
|
|
2396
|
-
const responseId = (0,
|
|
2558
|
+
const responseId = (0, import_runtime13.randomId)(24);
|
|
2397
2559
|
this.state.surveysSubmitted.push(guide.id);
|
|
2398
2560
|
this.persistState();
|
|
2399
2561
|
this.recordEnd(guide, "completed");
|
|
@@ -2422,7 +2584,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2422
2584
|
}
|
|
2423
2585
|
}
|
|
2424
2586
|
renderPersistentWidgets() {
|
|
2425
|
-
if (!this.config) return;
|
|
2587
|
+
if (!this.config || this.captureStarting || this.capture?.isActive()) return;
|
|
2426
2588
|
const rc = this.config.resourceCenter;
|
|
2427
2589
|
if (this.config.messenger?.enabled) {
|
|
2428
2590
|
if (this.resourceCenter) {
|
|
@@ -2435,9 +2597,9 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2435
2597
|
}
|
|
2436
2598
|
this.destroyMessenger();
|
|
2437
2599
|
if (rc && rc.enabled && !this.resourceCenter) {
|
|
2438
|
-
const scope = (0,
|
|
2600
|
+
const scope = (0, import_guide_runtime5.createScope)(this.theme());
|
|
2439
2601
|
this.resourceCenterScope = scope;
|
|
2440
|
-
this.resourceCenter = (0,
|
|
2602
|
+
this.resourceCenter = (0, import_guide_runtime4.renderResourceCenter)({
|
|
2441
2603
|
scope: scope.el,
|
|
2442
2604
|
config: rc,
|
|
2443
2605
|
guides: this.config.guides.map((g) => ({ id: g.id, key: g.key, name: g.name, kind: g.kind })),
|
|
@@ -2473,7 +2635,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2473
2635
|
this.messengerLoading = import("@guidelytech/guide-runtime/messenger").then(({ renderMessenger }) => {
|
|
2474
2636
|
const cfg = this.config?.messenger;
|
|
2475
2637
|
if (this.destroyed || !this.config || !cfg?.enabled || this.messenger) return this.messenger;
|
|
2476
|
-
const scope = (0,
|
|
2638
|
+
const scope = (0, import_guide_runtime5.createScope)(this.theme());
|
|
2477
2639
|
this.messengerScope = scope;
|
|
2478
2640
|
this.messenger = renderMessenger({
|
|
2479
2641
|
scope: scope.el,
|
|
@@ -2486,6 +2648,13 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2486
2648
|
userId: () => this.user?.id || null,
|
|
2487
2649
|
pageUrl: () => this.collectUrls() ? location.href : void 0
|
|
2488
2650
|
}),
|
|
2651
|
+
visitor: () => this.messengerVisitor(),
|
|
2652
|
+
brand: { logoUrl: this.theme().logoUrl ?? null, name: this.theme().brandName ?? null },
|
|
2653
|
+
onResumeGuide: (guideId, stepId) => this.startGuide(guideId, { stepId }),
|
|
2654
|
+
onNewsSeen: (ids) => {
|
|
2655
|
+
const seen = readJson(this.store, KEYS.newsSeen(this.options.projectId), []);
|
|
2656
|
+
writeJson(this.store, KEYS.newsSeen(this.options.projectId), Array.from(/* @__PURE__ */ new Set([...ids, ...Array.isArray(seen) ? seen : []])).slice(0, 200));
|
|
2657
|
+
},
|
|
2489
2658
|
resourceCenter: this.config.resourceCenter,
|
|
2490
2659
|
audienceIds: () => this.state.audienceIds,
|
|
2491
2660
|
guides: this.config.guides.map((g) => ({ id: g.id, key: g.key, name: g.name, kind: g.kind })),
|
|
@@ -2507,6 +2676,25 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2507
2676
|
});
|
|
2508
2677
|
return this.messengerLoading;
|
|
2509
2678
|
}
|
|
2679
|
+
/** What the messenger personalises from, computed fresh on every render. */
|
|
2680
|
+
messengerVisitor() {
|
|
2681
|
+
const ctx = this.getContext();
|
|
2682
|
+
const url = location.href;
|
|
2683
|
+
const seen = readJson(this.store, KEYS.newsSeen(this.options.projectId), []);
|
|
2684
|
+
return buildMessengerVisitor({
|
|
2685
|
+
guides: this.config?.guides ?? [],
|
|
2686
|
+
state: this.state,
|
|
2687
|
+
traits: this.user?.traits,
|
|
2688
|
+
url,
|
|
2689
|
+
isTargeted: (g) => this.isTargeted(g, ctx, url),
|
|
2690
|
+
isAudienceTargeted: (g) => {
|
|
2691
|
+
if (!isWithinSchedule(g)) return false;
|
|
2692
|
+
ctx.rolloutSalt = g.id;
|
|
2693
|
+
return (0, import_runtime13.evaluateRules)(g.config.targeting, ctx);
|
|
2694
|
+
},
|
|
2695
|
+
newsSeen: Array.isArray(seen) ? seen : []
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
2510
2698
|
destroyMessenger() {
|
|
2511
2699
|
this.messenger?.destroy();
|
|
2512
2700
|
this.messengerScope?.destroy();
|
|
@@ -2553,7 +2741,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2553
2741
|
if (progress.completedTaskIds.includes(task.id)) return;
|
|
2554
2742
|
progress.completedTaskIds.push(task.id);
|
|
2555
2743
|
this.enqueue("checklist_task_completed", { guideId: guide.id, guideVersion: guide.version, properties: { taskId: task.id } });
|
|
2556
|
-
const complete = (0,
|
|
2744
|
+
const complete = (0, import_guide_runtime4.isChecklistComplete)(config, progress) && !progress.completedAt;
|
|
2557
2745
|
if (complete) {
|
|
2558
2746
|
progress.completedAt = nowIso();
|
|
2559
2747
|
this.enqueue("checklist_completed", { guideId: guide.id, guideVersion: guide.version });
|
|
@@ -2576,7 +2764,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2576
2764
|
if (task.completion !== kind || !task.completionValue) continue;
|
|
2577
2765
|
let hit = false;
|
|
2578
2766
|
if (kind === "event") hit = value === task.completionValue;
|
|
2579
|
-
else if (kind === "url_visited") hit = (0,
|
|
2767
|
+
else if (kind === "url_visited") hit = (0, import_guide_runtime4.urlMatches)(task.completionValue, value);
|
|
2580
2768
|
else if (kind === "guide_completed") hit = value.id === task.completionValue || value.key === task.completionValue;
|
|
2581
2769
|
else if (kind === "click") {
|
|
2582
2770
|
try {
|
|
@@ -2720,7 +2908,8 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2720
2908
|
audienceIds: this.state.audienceIds,
|
|
2721
2909
|
session: this.session,
|
|
2722
2910
|
pendingEvents: this.queue.size,
|
|
2723
|
-
authoring: !!this.authoring?.isActive()
|
|
2911
|
+
authoring: !!this.authoring?.isActive(),
|
|
2912
|
+
capture: !!this.capture?.isActive()
|
|
2724
2913
|
};
|
|
2725
2914
|
}
|
|
2726
2915
|
// ---------------------------------------------------------------------------
|
|
@@ -2735,22 +2924,78 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2735
2924
|
ctx: () => this.getContext(),
|
|
2736
2925
|
maskSelectors: [...this.options.maskSelectors || [], ...this.config?.settings?.maskSelectors || []],
|
|
2737
2926
|
onError: (err) => this.reportError(err),
|
|
2738
|
-
onActive: (active) =>
|
|
2739
|
-
this.emitter.emit("authoring", { active });
|
|
2740
|
-
if (active) this.replay?.stop({ discard: true });
|
|
2741
|
-
else this.syncCapture();
|
|
2742
|
-
if (active) {
|
|
2743
|
-
this.stopGuide();
|
|
2744
|
-
this.widgets.forEach((w) => w.destroy());
|
|
2745
|
-
this.widgets.clear();
|
|
2746
|
-
} else this.evaluate("ready");
|
|
2747
|
-
}
|
|
2927
|
+
onActive: (active) => this.setEditing("authoring", active)
|
|
2748
2928
|
});
|
|
2749
2929
|
if (bridge.detect()) this.authoring = bridge;
|
|
2750
2930
|
} catch (err) {
|
|
2751
2931
|
this.reportError(err);
|
|
2752
2932
|
}
|
|
2753
2933
|
}
|
|
2934
|
+
/** Authoring or capture started / ended: pause everything a visitor would see or be recorded by. */
|
|
2935
|
+
setEditing(kind, active) {
|
|
2936
|
+
this.emitter.emit(kind, { active });
|
|
2937
|
+
if (active) {
|
|
2938
|
+
this.replay?.stop({ discard: true });
|
|
2939
|
+
this.stopGuide();
|
|
2940
|
+
this.widgets.forEach((w) => w.destroy());
|
|
2941
|
+
this.widgets.clear();
|
|
2942
|
+
if (kind === "capture") this.hidePersistentWidgets();
|
|
2943
|
+
} else {
|
|
2944
|
+
this.syncCapture();
|
|
2945
|
+
this.renderPersistentWidgets();
|
|
2946
|
+
this.evaluate("ready");
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
hidePersistentWidgets() {
|
|
2950
|
+
this.resourceCenter?.destroy();
|
|
2951
|
+
this.resourceCenterScope?.destroy();
|
|
2952
|
+
this.resourceCenter = null;
|
|
2953
|
+
this.resourceCenterScope = null;
|
|
2954
|
+
this.destroyMessenger();
|
|
2955
|
+
}
|
|
2956
|
+
// ---------------------------------------------------------------------------
|
|
2957
|
+
// How-to capture
|
|
2958
|
+
// ---------------------------------------------------------------------------
|
|
2959
|
+
/**
|
|
2960
|
+
* Starts how-to capture when the URL carries `guidely_capture` (or this tab is mid-recording).
|
|
2961
|
+
* The bridge, toolbar and screenshot code are a separate chunk loaded only then.
|
|
2962
|
+
*/
|
|
2963
|
+
maybeStartCapture() {
|
|
2964
|
+
let marker = null;
|
|
2965
|
+
try {
|
|
2966
|
+
marker = takeCaptureToken(this.sessionStore);
|
|
2967
|
+
} catch (err) {
|
|
2968
|
+
this.reportError(err);
|
|
2969
|
+
}
|
|
2970
|
+
if (!marker) return false;
|
|
2971
|
+
const found = marker;
|
|
2972
|
+
this.captureStarting = true;
|
|
2973
|
+
this.replay?.stop({ discard: true });
|
|
2974
|
+
loadCapture().then(async (capture) => {
|
|
2975
|
+
if (this.destroyed) return;
|
|
2976
|
+
const bridge = capture.createBridge({
|
|
2977
|
+
api: this.api,
|
|
2978
|
+
sessionStore: this.sessionStore,
|
|
2979
|
+
marker: found,
|
|
2980
|
+
maskSelector: this.maskSelector(),
|
|
2981
|
+
onError: (err) => this.reportError(err),
|
|
2982
|
+
onActive: (active) => {
|
|
2983
|
+
this.captureStarting = false;
|
|
2984
|
+
this.setEditing("capture", active);
|
|
2985
|
+
}
|
|
2986
|
+
});
|
|
2987
|
+
this.capture = bridge;
|
|
2988
|
+
await bridge.start();
|
|
2989
|
+
}).catch((err) => this.reportError(err)).finally(() => {
|
|
2990
|
+
if (!this.captureStarting) return;
|
|
2991
|
+
this.captureStarting = false;
|
|
2992
|
+
if (this.destroyed) return;
|
|
2993
|
+
this.syncCapture();
|
|
2994
|
+
this.renderPersistentWidgets();
|
|
2995
|
+
this.evaluate("ready");
|
|
2996
|
+
});
|
|
2997
|
+
return true;
|
|
2998
|
+
}
|
|
2754
2999
|
// ---------------------------------------------------------------------------
|
|
2755
3000
|
// Internals
|
|
2756
3001
|
// ---------------------------------------------------------------------------
|
|
@@ -2790,9 +3035,9 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2790
3035
|
};
|
|
2791
3036
|
var current = null;
|
|
2792
3037
|
function webOnly(config) {
|
|
2793
|
-
return config.guides.every((g) => (0,
|
|
3038
|
+
return config.guides.every((g) => (0, import_runtime13.supportsPlatform)(g.config, "web")) ? config : { ...config, guides: config.guides.filter((g) => (0, import_runtime13.supportsPlatform)(g.config, "web")) };
|
|
2794
3039
|
}
|
|
2795
|
-
function
|
|
3040
|
+
function pathOf2(url) {
|
|
2796
3041
|
try {
|
|
2797
3042
|
return new URL(url, "http://localhost").pathname;
|
|
2798
3043
|
} catch {
|
|
@@ -2826,6 +3071,7 @@ var index_default = Guidely;
|
|
|
2826
3071
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2827
3072
|
0 && (module.exports = {
|
|
2828
3073
|
AUTHORING_PARAM,
|
|
3074
|
+
CAPTURE_PARAM,
|
|
2829
3075
|
DEFAULT_API_URL,
|
|
2830
3076
|
EventQueue,
|
|
2831
3077
|
Guidely,
|