@guidelytech/sdk-core 0.8.0 → 0.10.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 +15 -4
- package/dist/{capture-C3faAYb7.d.cts → capture-369s4YYT.d.ts} +2 -1
- package/dist/{capture-Dqd7up2n.d.ts → capture-DVJyQ2JZ.d.cts} +2 -1
- package/dist/capture.cjs +2 -0
- package/dist/capture.cjs.map +1 -1
- package/dist/capture.d.cts +3 -2
- package/dist/capture.d.ts +3 -2
- package/dist/capture.js +4 -6
- package/dist/capture.js.map +1 -1
- package/dist/chunk-C4WWSF67.js +374 -0
- package/dist/chunk-C4WWSF67.js.map +1 -0
- package/dist/{chunk-GDF6RCHQ.js → chunk-GDJB466J.js} +121 -19
- package/dist/chunk-GDJB466J.js.map +1 -0
- package/dist/headless.cjs +281 -40
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.d.cts +43 -3
- package/dist/headless.d.ts +43 -3
- package/dist/headless.js +16 -8
- package/dist/health-DFHUstWd.d.cts +88 -0
- package/dist/health-DFHUstWd.d.ts +88 -0
- package/dist/{http-47tiAlOM.d.ts → http-D0dCcpLN.d.cts} +10 -35
- package/dist/{http-47tiAlOM.d.cts → http-DJd8jRhK.d.ts} +10 -35
- package/dist/index.cjs +380 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -5
- package/dist/index.d.ts +15 -5
- package/dist/index.js +106 -36
- package/dist/index.js.map +1 -1
- package/dist/recorder.cjs +70 -9
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts +9 -0
- package/dist/recorder.d.ts +9 -0
- package/dist/recorder.js +57 -11
- package/dist/recorder.js.map +1 -1
- package/dist/{types-DqhNl5Am.d.ts → types-B5ecgHUV.d.cts} +26 -4
- package/dist/{types-DbKQPvFt.d.cts → types-BtPovimB.d.ts} +26 -4
- package/package.json +7 -7
- package/dist/chunk-AKNE7D5M.js +0 -160
- package/dist/chunk-AKNE7D5M.js.map +0 -1
- package/dist/chunk-CYAG6ZYX.js +0 -85
- package/dist/chunk-CYAG6ZYX.js.map +0 -1
- package/dist/chunk-GDF6RCHQ.js.map +0 -1
- package/dist/types-RyeRQd9f.d.ts +0 -226
- package/dist/types-d4eOerxi.d.cts +0 -226
package/dist/index.d.cts
CHANGED
|
@@ -4,14 +4,15 @@ export { AppSnapshot, AutocaptureSettings, ExceptionProperties, GuideConfig, Gui
|
|
|
4
4
|
import { FlagsClient, JsonValue, FlagValue, FlagDetails } from '@guidelytech/flags';
|
|
5
5
|
export { Attributes, EvaluationResult, FlagDetails, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsPayload, JsonValue } from '@guidelytech/flags';
|
|
6
6
|
import { MessengerTab } from '@guidelytech/guide-runtime/messenger';
|
|
7
|
-
import {
|
|
8
|
-
export { E as EventQueue, b as EventQueueOptions,
|
|
9
|
-
export {
|
|
10
|
-
export { C as CaptureMarker, a as CaptureOutgoingMessage } from './capture-
|
|
7
|
+
import { k as GuidelyOptions, c as GuidelyAccount, a as ConsentState, C as ChecklistProgressState, l as GuidelyEvents, e as GuidelyUser, G as GuideLocalState, S as SessionState } from './types-B5ecgHUV.cjs';
|
|
8
|
+
export { E as EventQueue, b as EventQueueOptions, m as GuideStepInfo, d as GuidelyEnvironment, n as GuidelyEventName, P as PersistedState, f as areMutuallyExclusive, i as isExclusiveGuide, h as isFrequencyEligible, j as isWithinSchedule } from './types-B5ecgHUV.cjs';
|
|
9
|
+
export { a as KEYS, K as KeyValueStore, c as StorageKind, d as createStorage } from './health-DFHUstWd.cjs';
|
|
10
|
+
export { C as CaptureMarker, a as CaptureOutgoingMessage } from './capture-DVJyQ2JZ.cjs';
|
|
11
11
|
export { CAPTURE_PARAM } from '@guidelytech/shared/runtime';
|
|
12
|
+
import './http-D0dCcpLN.cjs';
|
|
12
13
|
|
|
13
14
|
declare const SDK_NAME = "@guidelytech/sdk-core";
|
|
14
|
-
declare const SDK_VERSION = "0.
|
|
15
|
+
declare const SDK_VERSION = "0.10.0";
|
|
15
16
|
declare const DEFAULT_API_URL = "https://api.guidely.app";
|
|
16
17
|
/**
|
|
17
18
|
* Framework-independent Guidely client. Create one per page with {@link createGuidely} or
|
|
@@ -24,6 +25,8 @@ declare class GuidelyClient {
|
|
|
24
25
|
private readonly sessionStore;
|
|
25
26
|
private readonly emitter;
|
|
26
27
|
private readonly queue;
|
|
28
|
+
/** Survey answers and guide/checklist progress, kept until the server accepts them. */
|
|
29
|
+
private readonly outbox;
|
|
27
30
|
private config;
|
|
28
31
|
private etag;
|
|
29
32
|
private anonymousId;
|
|
@@ -85,6 +88,8 @@ declare class GuidelyClient {
|
|
|
85
88
|
private wireErrorListeners;
|
|
86
89
|
/** Re-fetches the published config and re-evaluates triggers. */
|
|
87
90
|
refresh(): Promise<void>;
|
|
91
|
+
/** Re-renders after a config change (refresh, or a config that arrived after start). */
|
|
92
|
+
private applyConfigChange;
|
|
88
93
|
/** Tears everything down: guides, widgets, listeners, timers, host element. Re-init is allowed via a new client. */
|
|
89
94
|
destroy(): void;
|
|
90
95
|
/** Identifies the end user and loads server-held state (audiences, guide history, checklist progress). */
|
|
@@ -214,6 +219,11 @@ declare class GuidelyClient {
|
|
|
214
219
|
/** Opens the Scout messenger (when enabled for the project), optionally on a tab. */
|
|
215
220
|
openMessenger(tab?: MessengerTab): void;
|
|
216
221
|
closeMessenger(): void;
|
|
222
|
+
/**
|
|
223
|
+
* Opens the complaint form in the messenger, optionally with a category picked (its id from
|
|
224
|
+
* Complaints → Categories). Does nothing when the project doesn't take complaints.
|
|
225
|
+
*/
|
|
226
|
+
openComplaint(categoryId?: string): void;
|
|
217
227
|
/** Opens a new messenger conversation, optionally pre-filling the message. */
|
|
218
228
|
showNewMessage(text?: string): void;
|
|
219
229
|
/** Boolean flag value for the current user; `fallback` (default `false`) until loaded or when missing. */
|
package/dist/index.d.ts
CHANGED
|
@@ -4,14 +4,15 @@ export { AppSnapshot, AutocaptureSettings, ExceptionProperties, GuideConfig, Gui
|
|
|
4
4
|
import { FlagsClient, JsonValue, FlagValue, FlagDetails } from '@guidelytech/flags';
|
|
5
5
|
export { Attributes, EvaluationResult, FlagDetails, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsPayload, JsonValue } from '@guidelytech/flags';
|
|
6
6
|
import { MessengerTab } from '@guidelytech/guide-runtime/messenger';
|
|
7
|
-
import {
|
|
8
|
-
export { E as EventQueue, b as EventQueueOptions,
|
|
9
|
-
export {
|
|
10
|
-
export { C as CaptureMarker, a as CaptureOutgoingMessage } from './capture-
|
|
7
|
+
import { k as GuidelyOptions, c as GuidelyAccount, a as ConsentState, C as ChecklistProgressState, l as GuidelyEvents, e as GuidelyUser, G as GuideLocalState, S as SessionState } from './types-BtPovimB.js';
|
|
8
|
+
export { E as EventQueue, b as EventQueueOptions, m as GuideStepInfo, d as GuidelyEnvironment, n as GuidelyEventName, P as PersistedState, f as areMutuallyExclusive, i as isExclusiveGuide, h as isFrequencyEligible, j as isWithinSchedule } from './types-BtPovimB.js';
|
|
9
|
+
export { a as KEYS, K as KeyValueStore, c as StorageKind, d as createStorage } from './health-DFHUstWd.js';
|
|
10
|
+
export { C as CaptureMarker, a as CaptureOutgoingMessage } from './capture-369s4YYT.js';
|
|
11
11
|
export { CAPTURE_PARAM } from '@guidelytech/shared/runtime';
|
|
12
|
+
import './http-DJd8jRhK.js';
|
|
12
13
|
|
|
13
14
|
declare const SDK_NAME = "@guidelytech/sdk-core";
|
|
14
|
-
declare const SDK_VERSION = "0.
|
|
15
|
+
declare const SDK_VERSION = "0.10.0";
|
|
15
16
|
declare const DEFAULT_API_URL = "https://api.guidely.app";
|
|
16
17
|
/**
|
|
17
18
|
* Framework-independent Guidely client. Create one per page with {@link createGuidely} or
|
|
@@ -24,6 +25,8 @@ declare class GuidelyClient {
|
|
|
24
25
|
private readonly sessionStore;
|
|
25
26
|
private readonly emitter;
|
|
26
27
|
private readonly queue;
|
|
28
|
+
/** Survey answers and guide/checklist progress, kept until the server accepts them. */
|
|
29
|
+
private readonly outbox;
|
|
27
30
|
private config;
|
|
28
31
|
private etag;
|
|
29
32
|
private anonymousId;
|
|
@@ -85,6 +88,8 @@ declare class GuidelyClient {
|
|
|
85
88
|
private wireErrorListeners;
|
|
86
89
|
/** Re-fetches the published config and re-evaluates triggers. */
|
|
87
90
|
refresh(): Promise<void>;
|
|
91
|
+
/** Re-renders after a config change (refresh, or a config that arrived after start). */
|
|
92
|
+
private applyConfigChange;
|
|
88
93
|
/** Tears everything down: guides, widgets, listeners, timers, host element. Re-init is allowed via a new client. */
|
|
89
94
|
destroy(): void;
|
|
90
95
|
/** Identifies the end user and loads server-held state (audiences, guide history, checklist progress). */
|
|
@@ -214,6 +219,11 @@ declare class GuidelyClient {
|
|
|
214
219
|
/** Opens the Scout messenger (when enabled for the project), optionally on a tab. */
|
|
215
220
|
openMessenger(tab?: MessengerTab): void;
|
|
216
221
|
closeMessenger(): void;
|
|
222
|
+
/**
|
|
223
|
+
* Opens the complaint form in the messenger, optionally with a category picked (its id from
|
|
224
|
+
* Complaints → Categories). Does nothing when the project doesn't take complaints.
|
|
225
|
+
*/
|
|
226
|
+
openComplaint(categoryId?: string): void;
|
|
217
227
|
/** Opens a new messenger conversation, optionally pre-filling the message. */
|
|
218
228
|
showNewMessage(text?: string): void;
|
|
219
229
|
/** Boolean flag value for the current user; `fallback` (default `false`) until loaded or when missing. */
|
package/dist/index.js
CHANGED
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Emitter,
|
|
3
3
|
EventQueue,
|
|
4
|
+
Outbox,
|
|
4
5
|
areMutuallyExclusive,
|
|
6
|
+
dismissedAfterMerge,
|
|
5
7
|
isExclusiveGuide,
|
|
6
8
|
isFrequencyEligible,
|
|
7
9
|
isWithinSchedule
|
|
8
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GDJB466J.js";
|
|
9
11
|
import {
|
|
12
|
+
Api,
|
|
10
13
|
KEYS,
|
|
14
|
+
__publicField,
|
|
11
15
|
createStorage,
|
|
16
|
+
postWithRetry,
|
|
12
17
|
readJson,
|
|
13
18
|
writeJson
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import {
|
|
16
|
-
Api,
|
|
17
|
-
__publicField,
|
|
18
|
-
postWithRetry
|
|
19
|
-
} from "./chunk-AKNE7D5M.js";
|
|
19
|
+
} from "./chunk-C4WWSF67.js";
|
|
20
20
|
|
|
21
21
|
// src/client.ts
|
|
22
22
|
import { evaluateRules, randomId, rolloutBucket, sanitizeProperties, supportsPlatform } from "@guidelytech/shared/runtime";
|
|
23
23
|
import {
|
|
24
|
+
closestComposed as closestComposed3,
|
|
25
|
+
composedTarget,
|
|
24
26
|
destroyHost,
|
|
25
27
|
isChecklistComplete,
|
|
26
28
|
renderChecklist,
|
|
@@ -34,7 +36,7 @@ import { createScope } from "@guidelytech/guide-runtime";
|
|
|
34
36
|
import { createFlagsClient } from "@guidelytech/flags";
|
|
35
37
|
|
|
36
38
|
// src/authoring.ts
|
|
37
|
-
import { highlightSelectors, previewStep, runGuide, startAuthoring } from "@guidelytech/guide-runtime";
|
|
39
|
+
import { closestComposed, highlightSelectors, previewStep, runGuide, startAuthoring } from "@guidelytech/guide-runtime";
|
|
38
40
|
|
|
39
41
|
// src/snapshot.ts
|
|
40
42
|
var MAX_SNAPSHOT_BYTES = 6 * 1024 * 1024;
|
|
@@ -198,7 +200,7 @@ var AuthoringBridge = class {
|
|
|
198
200
|
masked(el) {
|
|
199
201
|
return this.opts.maskSelectors.some((sel) => {
|
|
200
202
|
try {
|
|
201
|
-
return !!el
|
|
203
|
+
return !!closestComposed(el, sel);
|
|
202
204
|
} catch {
|
|
203
205
|
return false;
|
|
204
206
|
}
|
|
@@ -379,7 +381,7 @@ function takeCaptureToken(sessionStore) {
|
|
|
379
381
|
var loadCapture = async () => (await import("./capture.js")).capture;
|
|
380
382
|
|
|
381
383
|
// src/autocapture.ts
|
|
382
|
-
import { isGuidelyElement, isStableId } from "@guidelytech/guide-runtime";
|
|
384
|
+
import { closestComposed as closestComposed2, isGuidelyElement, isStableId, SHADOW_SEPARATOR, shadowHostChain } from "@guidelytech/guide-runtime";
|
|
383
385
|
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]";
|
|
384
386
|
var DEFAULT_MASK_SELECTORS = ["[data-guidely-mask]", ".guidely-mask"];
|
|
385
387
|
var PREFERRED_ATTRS = ["data-testid", "data-test-id", "data-test", "data-cy", "data-qa", "data-guidely-track", "data-tour"];
|
|
@@ -399,11 +401,11 @@ function validSelectors(list) {
|
|
|
399
401
|
return out;
|
|
400
402
|
}
|
|
401
403
|
function isIgnoredElement(el) {
|
|
402
|
-
return isGuidelyElement(el) || !!el
|
|
404
|
+
return isGuidelyElement(el) || !!closestComposed2(el, "[data-guidely-ignore]");
|
|
403
405
|
}
|
|
404
406
|
function interactiveElement(target) {
|
|
405
407
|
try {
|
|
406
|
-
return target
|
|
408
|
+
return closestComposed2(target, INTERACTIVE_SELECTOR);
|
|
407
409
|
} catch {
|
|
408
410
|
return null;
|
|
409
411
|
}
|
|
@@ -414,14 +416,19 @@ function esc(value) {
|
|
|
414
416
|
}
|
|
415
417
|
var attr = (tag, name, value) => value ? `${tag}[${name}="${value.replace(/["\\]/g, "\\$&")}"]` : `${tag}[${name}]`;
|
|
416
418
|
function elementSelector(el) {
|
|
419
|
+
const own = ownSelector(el);
|
|
420
|
+
const chain = shadowHostChain(el);
|
|
421
|
+
return (chain ? chain + SHADOW_SEPARATOR + own : own).slice(0, 300);
|
|
422
|
+
}
|
|
423
|
+
function ownSelector(el) {
|
|
417
424
|
const tag = el.tagName.toLowerCase();
|
|
418
425
|
if (el.id && isStableId(el.id)) return `#${esc(el.id)}`;
|
|
419
|
-
for (const name of PREFERRED_ATTRS) if (el.hasAttribute(name)) return attr(tag, name, el.getAttribute(name) || "")
|
|
426
|
+
for (const name of PREFERRED_ATTRS) if (el.hasAttribute(name)) return attr(tag, name, el.getAttribute(name) || "");
|
|
420
427
|
for (const a of Array.from(el.attributes)) {
|
|
421
|
-
if (a.name.startsWith("data-") && !NOISY_DATA_ATTR.test(a.name) && a.value && a.value.length <= 60) return attr(tag, a.name, a.value)
|
|
428
|
+
if (a.name.startsWith("data-") && !NOISY_DATA_ATTR.test(a.name) && a.value && a.value.length <= 60) return attr(tag, a.name, a.value);
|
|
422
429
|
}
|
|
423
430
|
const label = el.getAttribute("aria-label");
|
|
424
|
-
if (label && label.length <= 80) return attr(tag, "aria-label", label)
|
|
431
|
+
if (label && label.length <= 80) return attr(tag, "aria-label", label);
|
|
425
432
|
const parts = [];
|
|
426
433
|
let cur = el;
|
|
427
434
|
while (cur && parts.length < 4) {
|
|
@@ -437,12 +444,12 @@ function elementSelector(el) {
|
|
|
437
444
|
parts.unshift(same.length > 1 ? `${t}:nth-of-type(${same.indexOf(node) + 1})` : t);
|
|
438
445
|
cur = parent;
|
|
439
446
|
}
|
|
440
|
-
return
|
|
447
|
+
return parts.join(" > ") || tag;
|
|
441
448
|
}
|
|
442
449
|
function matches(el, selector, deep) {
|
|
443
450
|
if (!selector) return false;
|
|
444
451
|
try {
|
|
445
|
-
return !!el
|
|
452
|
+
return !!closestComposed2(el, selector) || deep && !!el.querySelector(selector);
|
|
446
453
|
} catch {
|
|
447
454
|
return false;
|
|
448
455
|
}
|
|
@@ -460,7 +467,7 @@ function elementText(el, mask, maskAll) {
|
|
|
460
467
|
return text ? text.slice(0, 100) : void 0;
|
|
461
468
|
}
|
|
462
469
|
function linkHref(el) {
|
|
463
|
-
const a = el
|
|
470
|
+
const a = closestComposed2(el, "a[href]");
|
|
464
471
|
if (!a) return void 0;
|
|
465
472
|
try {
|
|
466
473
|
const u = new URL(a.getAttribute("href") || "", location.href);
|
|
@@ -730,11 +737,15 @@ function forgetConversations(store, projectId) {
|
|
|
730
737
|
store.remove(KEYS.conversations(projectId));
|
|
731
738
|
}
|
|
732
739
|
var HttpError = class extends Error {
|
|
733
|
-
constructor(status) {
|
|
734
|
-
super(`Request failed (${status})`);
|
|
740
|
+
constructor(status, message) {
|
|
741
|
+
super(message || `Request failed (${status})`);
|
|
735
742
|
__publicField(this, "status", status);
|
|
736
743
|
}
|
|
737
744
|
};
|
|
745
|
+
function serverMessage(data) {
|
|
746
|
+
const m = data?.error?.message;
|
|
747
|
+
return typeof m === "string" && m !== "Request validation failed" ? m : void 0;
|
|
748
|
+
}
|
|
738
749
|
function createMessengerTransport(opts) {
|
|
739
750
|
const { api, store, projectId } = opts;
|
|
740
751
|
const tokenFor = (id) => storedConversations(store, projectId).find((c) => c.id === id)?.token ?? "";
|
|
@@ -816,6 +827,16 @@ function createMessengerTransport(opts) {
|
|
|
816
827
|
const res = await api.get(`/articles/${encodeURIComponent(id)}`);
|
|
817
828
|
return res.ok && res.data ? res.data.article : null;
|
|
818
829
|
},
|
|
830
|
+
async fileComplaint(input) {
|
|
831
|
+
const headers = input.conversationId ? { [TOKEN_HEADER]: tokenFor(input.conversationId) } : {};
|
|
832
|
+
const res = await api.post("/complaints", { ...input, anonymousId: opts.anonymousId(), userId: opts.userId(), pageUrl: opts.pageUrl() }, { headers });
|
|
833
|
+
if (!res.ok || !res.data || !("complaint" in res.data)) {
|
|
834
|
+
if (res.status === 429) throw new HttpError(429, "You've filed several complaints just now. Please try again later.");
|
|
835
|
+
throw new HttpError(res.status, serverMessage(res.data) ?? "Your complaint couldn't be sent. Please try again.");
|
|
836
|
+
}
|
|
837
|
+
if (res.data.token) remember(res.data.conversation.id, res.data.token);
|
|
838
|
+
return res.data;
|
|
839
|
+
},
|
|
819
840
|
async pageContent(path) {
|
|
820
841
|
const res = await api.post("/messenger/page", { anonymousId: opts.anonymousId(), userId: opts.userId(), path: path.split(/[?#]/)[0] || "/" });
|
|
821
842
|
if (!res.ok || !res.data) throw new HttpError(res.status);
|
|
@@ -1013,11 +1034,22 @@ var loadRecorder = async () => (await import("./recorder.js")).recorder;
|
|
|
1013
1034
|
|
|
1014
1035
|
// src/client.ts
|
|
1015
1036
|
var SDK_NAME = "@guidelytech/sdk-core";
|
|
1016
|
-
var SDK_VERSION = "0.
|
|
1037
|
+
var SDK_VERSION = "0.10.0";
|
|
1017
1038
|
var DEFAULT_API_URL = "https://api.guidely.app";
|
|
1018
1039
|
var PAGE_SCOPED = /* @__PURE__ */ new Set(["page_load", "url_match", "element_click", "element_visible", "delay"]);
|
|
1019
1040
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
1020
1041
|
var MAX_SESSION_EXCEPTIONS = 100;
|
|
1042
|
+
var CACHED_START_MS = 1500;
|
|
1043
|
+
function settlesWithin(promise, ms) {
|
|
1044
|
+
return new Promise((resolve) => {
|
|
1045
|
+
const timer = setTimeout(() => resolve(false), ms);
|
|
1046
|
+
const done = () => {
|
|
1047
|
+
clearTimeout(timer);
|
|
1048
|
+
resolve(true);
|
|
1049
|
+
};
|
|
1050
|
+
promise.then(done, done);
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1021
1053
|
function isBrowser() {
|
|
1022
1054
|
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
1023
1055
|
}
|
|
@@ -1038,6 +1070,8 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1038
1070
|
__publicField(this, "sessionStore");
|
|
1039
1071
|
__publicField(this, "emitter");
|
|
1040
1072
|
__publicField(this, "queue");
|
|
1073
|
+
/** Survey answers and guide/checklist progress, kept until the server accepts them. */
|
|
1074
|
+
__publicField(this, "outbox");
|
|
1041
1075
|
__publicField(this, "config", null);
|
|
1042
1076
|
__publicField(this, "etag", null);
|
|
1043
1077
|
__publicField(this, "anonymousId");
|
|
@@ -1137,6 +1171,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1137
1171
|
store: this.store,
|
|
1138
1172
|
storageKey: KEYS.queue(this.options.projectId),
|
|
1139
1173
|
enabled: () => this.analyticsEnabled(),
|
|
1174
|
+
health: this.api.health,
|
|
1140
1175
|
send: async (events) => {
|
|
1141
1176
|
const res = await this.api.post("/events", { events, sentAt: nowIso(), sdk: { name: SDK_NAME, version: SDK_VERSION } }, { keepalive: true });
|
|
1142
1177
|
return { ok: res.ok, retry: !(res.status >= 400 && res.status < 500 && res.status !== 429) };
|
|
@@ -1144,6 +1179,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1144
1179
|
beacon: (events) => this.api.beacon("/events", { events, sentAt: nowIso(), sdk: { name: SDK_NAME, version: SDK_VERSION } }),
|
|
1145
1180
|
onError: (err) => this.reportError(err)
|
|
1146
1181
|
});
|
|
1182
|
+
this.outbox = new Outbox({ api: this.api, store: this.store, storageKey: KEYS.outbox(this.options.projectId), health: this.api.health });
|
|
1147
1183
|
if (options.user) this.user = { ...options.user };
|
|
1148
1184
|
this.flags = this.createFlags(options.flags);
|
|
1149
1185
|
}
|
|
@@ -1201,12 +1237,15 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1201
1237
|
const cached = readJson(this.store, KEYS.config(this.options.projectId), null);
|
|
1202
1238
|
this.etag = this.store.get(KEYS.etag(this.options.projectId));
|
|
1203
1239
|
if (cached && cached.guides) this.config = webOnly(cached);
|
|
1204
|
-
|
|
1240
|
+
const before = this.config?.configVersion;
|
|
1241
|
+
const fresh = this.fetchConfig();
|
|
1242
|
+
if (!this.config) await fresh;
|
|
1243
|
+
else if (!await settlesWithin(fresh, CACHED_START_MS)) void fresh.then(() => this.applyConfigChange(before));
|
|
1205
1244
|
if (this.destroyed) return;
|
|
1206
1245
|
this.wireListeners();
|
|
1207
1246
|
if (!this.identitySettled) {
|
|
1208
|
-
|
|
1209
|
-
|
|
1247
|
+
const known = this.options.user ?? (this.user?.id ? this.user : null);
|
|
1248
|
+
if (known) await settlesWithin(this.identify(known.id, known.traits, known.account), CACHED_START_MS);
|
|
1210
1249
|
}
|
|
1211
1250
|
if (this.destroyed) return;
|
|
1212
1251
|
this.isReady = true;
|
|
@@ -1311,7 +1350,12 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1311
1350
|
win.removeEventListener("scroll", onScroll);
|
|
1312
1351
|
doc.removeEventListener("keydown", onKey, true);
|
|
1313
1352
|
});
|
|
1314
|
-
const refresh = setInterval(() =>
|
|
1353
|
+
const refresh = setInterval(() => {
|
|
1354
|
+
if (this.api.health.canSend()) void this.refresh();
|
|
1355
|
+
}, this.options.configRefreshMs);
|
|
1356
|
+
const onOnline = () => this.api.health.online();
|
|
1357
|
+
win.addEventListener("online", onOnline);
|
|
1358
|
+
this.cleanups.push(() => win.removeEventListener("online", onOnline));
|
|
1315
1359
|
this.cleanups.push(() => clearInterval(refresh));
|
|
1316
1360
|
const poll = setInterval(() => {
|
|
1317
1361
|
if (!this.isReady || !this.config || this.editing() || doc.visibilityState === "hidden") return;
|
|
@@ -1344,6 +1388,11 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1344
1388
|
if (this.destroyed) return;
|
|
1345
1389
|
const before = this.config?.configVersion;
|
|
1346
1390
|
await this.fetchConfig();
|
|
1391
|
+
this.applyConfigChange(before);
|
|
1392
|
+
}
|
|
1393
|
+
/** Re-renders after a config change (refresh, or a config that arrived after start). */
|
|
1394
|
+
applyConfigChange(before) {
|
|
1395
|
+
if (this.destroyed || !this.isReady) return;
|
|
1347
1396
|
if (this.config && this.config.configVersion !== before) {
|
|
1348
1397
|
this.renderPersistentWidgets();
|
|
1349
1398
|
this.evaluate("ready");
|
|
@@ -1378,6 +1427,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1378
1427
|
this.replay = null;
|
|
1379
1428
|
this.queue.flushSync();
|
|
1380
1429
|
this.queue.destroy();
|
|
1430
|
+
this.outbox.destroy();
|
|
1381
1431
|
this.flags.destroy();
|
|
1382
1432
|
this.emitter.clear();
|
|
1383
1433
|
destroyHost();
|
|
@@ -1421,7 +1471,8 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1421
1471
|
checklists[id] = {
|
|
1422
1472
|
completedTaskIds: Array.from(/* @__PURE__ */ new Set([...local?.completedTaskIds ?? [], ...p.completedTaskIds ?? []])),
|
|
1423
1473
|
completedAt: local?.completedAt ?? p.completedAt ?? null,
|
|
1424
|
-
dismissedAt: local
|
|
1474
|
+
dismissedAt: dismissedAfterMerge(local, p),
|
|
1475
|
+
dismissResetAt: p.dismissResetAt ?? local?.dismissResetAt ?? null
|
|
1425
1476
|
};
|
|
1426
1477
|
}
|
|
1427
1478
|
this.state = {
|
|
@@ -1579,7 +1630,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1579
1630
|
if (changed) void this.queue.flush();
|
|
1580
1631
|
}
|
|
1581
1632
|
handleDocumentClick(e) {
|
|
1582
|
-
const target = e
|
|
1633
|
+
const target = composedTarget(e);
|
|
1583
1634
|
if (!target || typeof target.closest !== "function") return;
|
|
1584
1635
|
this.autocaptureClick(e, target);
|
|
1585
1636
|
this.completeChecklistTasksBy("click", target);
|
|
@@ -1674,7 +1725,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1674
1725
|
}
|
|
1675
1726
|
/** Bins one pointer sample into the heatmap buffer. Not session activity. */
|
|
1676
1727
|
sampleMove(e) {
|
|
1677
|
-
const target = e
|
|
1728
|
+
const target = composedTarget(e);
|
|
1678
1729
|
if (document.visibilityState === "hidden" || !target?.closest || !this.autocaptureOn("mouseMovement") || isIgnoredElement(target)) return;
|
|
1679
1730
|
const url = location.href;
|
|
1680
1731
|
if (this.heatmap.url !== url) this.flushHeatmap();
|
|
@@ -1799,7 +1850,9 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1799
1850
|
this.persistSession();
|
|
1800
1851
|
this.replay?.stop({ discard: true });
|
|
1801
1852
|
},
|
|
1802
|
-
onError: (err) => this.reportError(err)
|
|
1853
|
+
onError: (err) => this.reportError(err),
|
|
1854
|
+
health: this.api.health,
|
|
1855
|
+
stash: this.sessionStore
|
|
1803
1856
|
});
|
|
1804
1857
|
this.syncReplay();
|
|
1805
1858
|
},
|
|
@@ -1889,7 +1942,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
1889
1942
|
case "element_click":
|
|
1890
1943
|
if (reason !== "click" || !t.value || !detail.target) return false;
|
|
1891
1944
|
try {
|
|
1892
|
-
return !!detail.target
|
|
1945
|
+
return !!closestComposed3(detail.target, t.value);
|
|
1893
1946
|
} catch {
|
|
1894
1947
|
return false;
|
|
1895
1948
|
}
|
|
@@ -2040,7 +2093,9 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2040
2093
|
if (reason === "completed") this.completeChecklistTasksBy("guide_completed", guide);
|
|
2041
2094
|
}
|
|
2042
2095
|
syncGuideState(guideId, update) {
|
|
2043
|
-
|
|
2096
|
+
const body = { anonymousId: this.anonymousId, userId: this.user?.id || null, updates: [{ guideId, ...update }] };
|
|
2097
|
+
if (update.impression) void postWithRetry(this.api, "/guide-state", body);
|
|
2098
|
+
else this.outbox.add(`guide:${guideId}:${update.completed ? "completed" : "dismissed"}:${this.anonymousId}`, "/guide-state", body);
|
|
2044
2099
|
}
|
|
2045
2100
|
show(guide, manual, stepId) {
|
|
2046
2101
|
try {
|
|
@@ -2060,6 +2115,12 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2060
2115
|
ctx: () => this.getContext(),
|
|
2061
2116
|
startAtStep: stepId,
|
|
2062
2117
|
eventBus: this.eventBus,
|
|
2118
|
+
// Started from Help, Scout or startGuide(): steps on other pages offer to take the user there.
|
|
2119
|
+
promptForUrl: manual,
|
|
2120
|
+
// Also while a step waits for its page, so a link that loads that page resumes on it.
|
|
2121
|
+
onPosition: (id) => {
|
|
2122
|
+
if (exclusive) writeJson(this.sessionStore, KEYS.active, { guideId: guide.id, stepId: id, manual });
|
|
2123
|
+
},
|
|
2063
2124
|
onError: (err) => this.reportError(err),
|
|
2064
2125
|
onEvent: (e) => {
|
|
2065
2126
|
const step = e.stepId ? guide.config.steps.find((s) => s.id === e.stepId) : void 0;
|
|
@@ -2162,7 +2223,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2162
2223
|
this.persistState();
|
|
2163
2224
|
this.recordEnd(guide, "completed");
|
|
2164
2225
|
this.enqueue("survey_submitted", { guideId: guide.id, guideVersion: guide.version, properties: { responseId } });
|
|
2165
|
-
|
|
2226
|
+
this.outbox.add(`survey:${responseId}`, `/surveys/${encodeURIComponent(guide.id)}/responses`, { anonymousId: this.anonymousId, userId: this.user?.id || null, accountId: this.user?.account?.id || null, guideVersion: guide.version, answers, responseId });
|
|
2166
2227
|
this.emitter.emit("survey:submit", { guide, answers, responseId });
|
|
2167
2228
|
this.emitter.emit("guide:complete", guide);
|
|
2168
2229
|
widget.showThankYou();
|
|
@@ -2283,7 +2344,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2283
2344
|
const ctx = this.getContext();
|
|
2284
2345
|
const url = location.href;
|
|
2285
2346
|
const seen = readJson(this.store, KEYS.newsSeen(this.options.projectId), []);
|
|
2286
|
-
|
|
2347
|
+
const visitor = buildMessengerVisitor({
|
|
2287
2348
|
guides: this.config?.guides ?? [],
|
|
2288
2349
|
state: this.state,
|
|
2289
2350
|
traits: this.user?.traits,
|
|
@@ -2296,6 +2357,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2296
2357
|
},
|
|
2297
2358
|
newsSeen: Array.isArray(seen) ? seen : []
|
|
2298
2359
|
});
|
|
2360
|
+
return { ...visitor, identified: !!this.user?.id };
|
|
2299
2361
|
}
|
|
2300
2362
|
destroyMessenger() {
|
|
2301
2363
|
this.messenger?.destroy();
|
|
@@ -2350,7 +2412,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2350
2412
|
}
|
|
2351
2413
|
this.persistState();
|
|
2352
2414
|
this.widgets.get(guide.id)?.checklist?.setProgress(this.getChecklistProgress(guide.id));
|
|
2353
|
-
|
|
2415
|
+
this.outbox.add(`checklist:${guide.id}:progress:${this.anonymousId}`, `/checklists/${encodeURIComponent(guide.id)}/progress`, { anonymousId: this.anonymousId, userId: this.user?.id || null, guideVersion: guide.version, completedTaskIds: progress.completedTaskIds, completed: !!progress.completedAt });
|
|
2354
2416
|
this.emitter.emit("checklist:task", { guide, task, progress: this.getChecklistProgress(guide.id) });
|
|
2355
2417
|
if (complete) {
|
|
2356
2418
|
this.emitter.emit("checklist:complete", guide);
|
|
@@ -2370,7 +2432,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2370
2432
|
else if (kind === "guide_completed") hit = value.id === task.completionValue || value.key === task.completionValue;
|
|
2371
2433
|
else if (kind === "click") {
|
|
2372
2434
|
try {
|
|
2373
|
-
hit = !!value
|
|
2435
|
+
hit = !!closestComposed3(value, task.completionValue);
|
|
2374
2436
|
} catch {
|
|
2375
2437
|
hit = false;
|
|
2376
2438
|
}
|
|
@@ -2390,7 +2452,7 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2390
2452
|
progress.dismissedAt = nowIso();
|
|
2391
2453
|
this.persistState();
|
|
2392
2454
|
this.enqueue("checklist_dismissed", { guideId: guide.id, guideVersion: guide.version });
|
|
2393
|
-
|
|
2455
|
+
this.outbox.add(`checklist:${guide.id}:dismissed:${this.anonymousId}`, `/checklists/${encodeURIComponent(guide.id)}/progress`, { anonymousId: this.anonymousId, userId: this.user?.id || null, guideVersion: guide.version, completedTaskIds: progress.completedTaskIds, dismissed: true });
|
|
2394
2456
|
this.widgets.get(guide.id)?.destroy();
|
|
2395
2457
|
this.widgets.delete(guide.id);
|
|
2396
2458
|
}
|
|
@@ -2481,6 +2543,14 @@ var GuidelyClient = class _GuidelyClient {
|
|
|
2481
2543
|
closeMessenger() {
|
|
2482
2544
|
this.messenger?.close();
|
|
2483
2545
|
}
|
|
2546
|
+
/**
|
|
2547
|
+
* Opens the complaint form in the messenger, optionally with a category picked (its id from
|
|
2548
|
+
* Complaints → Categories). Does nothing when the project doesn't take complaints.
|
|
2549
|
+
*/
|
|
2550
|
+
openComplaint(categoryId) {
|
|
2551
|
+
if (!this.config?.messenger?.enabled || !this.config.messenger.complaints?.categories.length) return;
|
|
2552
|
+
void this.loadMessenger().then((m) => m?.openComplaint(categoryId));
|
|
2553
|
+
}
|
|
2484
2554
|
/** Opens a new messenger conversation, optionally pre-filling the message. */
|
|
2485
2555
|
showNewMessage(text) {
|
|
2486
2556
|
if (!this.config?.messenger?.enabled) return;
|