@scalebun/web 1.3.1 → 1.5.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/dist/core/pageContext.d.ts.map +1 -1
- package/dist/core/pageContext.js +2 -1
- package/dist/core/pageContext.js.map +1 -1
- package/dist/core/version.generated.d.ts +9 -0
- package/dist/core/version.generated.d.ts.map +1 -0
- package/dist/core/version.generated.js +9 -0
- package/dist/core/version.generated.js.map +1 -0
- package/dist/features/bugreport/widget.d.ts.map +1 -1
- package/dist/features/bugreport/widget.js +1 -0
- package/dist/features/bugreport/widget.js.map +1 -1
- package/dist/features/coach/overlay.d.ts.map +1 -1
- package/dist/features/coach/overlay.js +1 -0
- package/dist/features/coach/overlay.js.map +1 -1
- package/dist/features/engage/widget/InAppWidget.d.ts.map +1 -1
- package/dist/features/engage/widget/InAppWidget.js +1 -0
- package/dist/features/engage/widget/InAppWidget.js.map +1 -1
- package/dist/features/engage/widget/SurveyWidget.d.ts.map +1 -1
- package/dist/features/engage/widget/SurveyWidget.js +1 -0
- package/dist/features/engage/widget/SurveyWidget.js.map +1 -1
- package/dist/features/inbox/widget/InboxWidget.d.ts.map +1 -1
- package/dist/features/inbox/widget/InboxWidget.js +1 -0
- package/dist/features/inbox/widget/InboxWidget.js.map +1 -1
- package/dist/features/journey/InteractionTracker.d.ts +31 -1
- package/dist/features/journey/InteractionTracker.d.ts.map +1 -1
- package/dist/features/journey/InteractionTracker.js +190 -54
- package/dist/features/journey/InteractionTracker.js.map +1 -1
- package/dist/features/journey/JourneyFeature.d.ts +1 -1
- package/dist/features/journey/JourneyFeature.d.ts.map +1 -1
- package/dist/features/journey/JourneyFeature.js +6 -4
- package/dist/features/journey/JourneyFeature.js.map +1 -1
- package/dist/features/journey/friction.d.ts +3 -4
- package/dist/features/journey/friction.d.ts.map +1 -1
- package/dist/features/journey/friction.js +7 -8
- package/dist/features/journey/friction.js.map +1 -1
- package/dist/features/journey/uiState.d.ts +50 -0
- package/dist/features/journey/uiState.d.ts.map +1 -0
- package/dist/features/journey/uiState.js +97 -0
- package/dist/features/journey/uiState.js.map +1 -0
- package/dist/features/push/softAsk.d.ts.map +1 -1
- package/dist/features/push/softAsk.js +1 -0
- package/dist/features/push/softAsk.js.map +1 -1
- package/dist/features/replay/engine/interactions.d.ts.map +1 -1
- package/dist/features/replay/engine/interactions.js +26 -7
- package/dist/features/replay/engine/interactions.js.map +1 -1
- package/dist/public/ScaleBunFacade.d.ts +22 -0
- package/dist/public/ScaleBunFacade.d.ts.map +1 -1
- package/dist/public/ScaleBunFacade.js +27 -0
- package/dist/public/ScaleBunFacade.js.map +1 -1
- package/dist/scalebun.min.js +23 -23
- package/dist/scalebun.min.js.map +4 -4
- package/package.json +6 -5
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { RageDetector, DeadClickWatcher } from './friction.js';
|
|
13
13
|
import { getPositionContext } from './positionContext.js';
|
|
14
|
+
import { uiStateSignature } from './uiState.js';
|
|
15
|
+
import { getReplayCoord, uuid } from '../../core/index.js';
|
|
14
16
|
export class InteractionTracker {
|
|
15
17
|
constructor(emit, opts = {},
|
|
16
18
|
/** Raise a capture signal ('rage'|'dead') onto the bus — arms friction-triggered features
|
|
@@ -18,6 +20,7 @@ export class InteractionTracker {
|
|
|
18
20
|
signal) {
|
|
19
21
|
this.emit = emit;
|
|
20
22
|
this.signal = signal;
|
|
23
|
+
this.pending = new Map();
|
|
21
24
|
this.rage = new RageDetector(opts.rageWindowMs ?? 800, opts.rageRadius ?? 30, opts.rageMin ?? 3);
|
|
22
25
|
this.dead = new DeadClickWatcher(opts.deadMs ?? 700);
|
|
23
26
|
this.maskText = opts.maskText ?? false;
|
|
@@ -26,42 +29,159 @@ export class InteractionTracker {
|
|
|
26
29
|
if (typeof document === 'undefined')
|
|
27
30
|
return;
|
|
28
31
|
this.dead.observe(document);
|
|
32
|
+
this.onPointerDownFn = (e) => {
|
|
33
|
+
const pe = e;
|
|
34
|
+
if (pe.isPrimary === false || (typeof pe.button === 'number' && pe.button !== 0))
|
|
35
|
+
return;
|
|
36
|
+
const target = eventTarget(e);
|
|
37
|
+
if (!target)
|
|
38
|
+
return;
|
|
39
|
+
this.pointerDown = capture(target, pe, Date.now(), this.maskText);
|
|
40
|
+
};
|
|
29
41
|
this.onClickFn = (e) => {
|
|
30
42
|
const me = e;
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
if (!target || !target.tagName)
|
|
43
|
+
const target = eventTarget(e);
|
|
44
|
+
if (!target)
|
|
34
45
|
return;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const el = anchor(target);
|
|
44
|
-
const rage = this.rage.push(me.clientX, me.clientY, t);
|
|
45
|
-
const data = { action: 'click', ...p, selector, ...el };
|
|
46
|
+
const down = this.pointerDown;
|
|
47
|
+
this.pointerDown = undefined;
|
|
48
|
+
// A real pointer click reuses the evidence captured when the finger/button landed. Keyboard
|
|
49
|
+
// and programmatic clicks have no pointerdown and are measured honestly at click time.
|
|
50
|
+
const captured = me.detail !== 0 && down && down.target === target && Date.now() - down.timestamp < 5000
|
|
51
|
+
? down
|
|
52
|
+
: capture(target, me, Date.now(), this.maskText);
|
|
53
|
+
const rage = this.rage.push(captured.x, captured.y, captured.timestamp);
|
|
46
54
|
if (rage)
|
|
47
|
-
data.rage = true;
|
|
48
|
-
this.emit('USER_ACTION', data, label);
|
|
55
|
+
captured.data.rage = true;
|
|
49
56
|
if (rage)
|
|
50
57
|
this.signal?.('rage'); // RageDetector coalesces → one signal per cluster
|
|
51
|
-
this.
|
|
52
|
-
|
|
53
|
-
this.
|
|
58
|
+
this.pending.set(captured.interactionId, captured);
|
|
59
|
+
this.dead.onClick((dead) => {
|
|
60
|
+
this.finish(captured.interactionId, dead);
|
|
61
|
+
if (dead)
|
|
62
|
+
this.signal?.('dead');
|
|
54
63
|
});
|
|
55
64
|
};
|
|
65
|
+
this.onPageHideFn = () => this.flushPending();
|
|
66
|
+
document.addEventListener('pointerdown', this.onPointerDownFn, true);
|
|
56
67
|
document.addEventListener('click', this.onClickFn, true);
|
|
68
|
+
if (typeof window !== 'undefined')
|
|
69
|
+
window.addEventListener('pagehide', this.onPageHideFn, true);
|
|
57
70
|
}
|
|
58
71
|
stop() {
|
|
72
|
+
this.flushPending();
|
|
59
73
|
this.dead.disconnect();
|
|
60
|
-
if (
|
|
61
|
-
|
|
74
|
+
if (typeof document !== 'undefined') {
|
|
75
|
+
if (this.onPointerDownFn)
|
|
76
|
+
document.removeEventListener('pointerdown', this.onPointerDownFn, true);
|
|
77
|
+
if (this.onClickFn)
|
|
78
|
+
document.removeEventListener('click', this.onClickFn, true);
|
|
62
79
|
}
|
|
80
|
+
if (this.onPageHideFn && typeof window !== 'undefined') {
|
|
81
|
+
window.removeEventListener('pagehide', this.onPageHideFn, true);
|
|
82
|
+
}
|
|
83
|
+
this.pointerDown = undefined;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Emit the one row for this interaction.
|
|
87
|
+
*
|
|
88
|
+
* `dead` is THREE-VALUED here, and that is the point. `true` and `false` are verdicts the watcher
|
|
89
|
+
* actually reached; `undefined` means the page went away before the observation window closed, so
|
|
90
|
+
* nobody looked. Writing `dead: false` in that case asserts a measurement never taken — "this click
|
|
91
|
+
* DID cause a change" — and a dead-click rate computed from it is wrong in exactly the situation
|
|
92
|
+
* where dead clicks matter most: the visitor left immediately after clicking.
|
|
93
|
+
*
|
|
94
|
+
* So the key is omitted rather than defaulted, the same rule the rest of this pipeline follows:
|
|
95
|
+
* absent means unknown, never zero. `deadUnknown` records WHY it is absent, so a consumer can tell
|
|
96
|
+
* "not dead" from "we never found out".
|
|
97
|
+
*/
|
|
98
|
+
finish(interactionId, dead) {
|
|
99
|
+
const captured = this.pending.get(interactionId);
|
|
100
|
+
if (!captured)
|
|
101
|
+
return;
|
|
102
|
+
this.pending.delete(interactionId);
|
|
103
|
+
if (dead === true)
|
|
104
|
+
captured.data.dead = true;
|
|
105
|
+
else if (dead === undefined)
|
|
106
|
+
captured.data.deadUnknown = true;
|
|
107
|
+
this.emit('USER_ACTION', captured.data, captured.label, {
|
|
108
|
+
interactionId,
|
|
109
|
+
timestamp: captured.timestamp,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Flush every interaction still awaiting its dead-click verdict.
|
|
114
|
+
*
|
|
115
|
+
* Runs on `pagehide` and on `stop()`. Without it a click followed immediately by a navigation is
|
|
116
|
+
* never emitted at all — the whole row is lost, which is strictly worse than an unknown flag. The
|
|
117
|
+
* verdict is passed as `undefined` because none was reached; see finish().
|
|
118
|
+
*/
|
|
119
|
+
flushPending() {
|
|
120
|
+
for (const interactionId of [...this.pending.keys()])
|
|
121
|
+
this.finish(interactionId, undefined);
|
|
63
122
|
}
|
|
64
123
|
}
|
|
124
|
+
function eventTarget(e) {
|
|
125
|
+
// SHADOW DOM — see the note below. `e.target` is retargeted to the host, so the composed
|
|
126
|
+
// path is what identifies the control. Our own surfaces stay reported as their host, which
|
|
127
|
+
// is what keeps the query-side exclusion matching them.
|
|
128
|
+
let target = e.target;
|
|
129
|
+
if (!target || !target.tagName)
|
|
130
|
+
return null;
|
|
131
|
+
if (!target.hasAttribute('data-scalebun')) {
|
|
132
|
+
const deep = e.composedPath?.()[0];
|
|
133
|
+
if (deep && deep.tagName)
|
|
134
|
+
target = deep;
|
|
135
|
+
}
|
|
136
|
+
return target;
|
|
137
|
+
}
|
|
138
|
+
function capture(target, pointerEvent, timestamp, maskText) {
|
|
139
|
+
const ui = uiStateSignature();
|
|
140
|
+
const data = {
|
|
141
|
+
action: 'click',
|
|
142
|
+
...pointer(pointerEvent),
|
|
143
|
+
selector: selectorOf(target),
|
|
144
|
+
...anchor(target),
|
|
145
|
+
state_status: ui ? 'captured_nonempty' : 'captured_empty',
|
|
146
|
+
};
|
|
147
|
+
if (ui)
|
|
148
|
+
data.ui = ui;
|
|
149
|
+
const replay = getReplayCoord();
|
|
150
|
+
if (replay)
|
|
151
|
+
data.replayCoord = replay;
|
|
152
|
+
return {
|
|
153
|
+
target,
|
|
154
|
+
x: pointerEvent.clientX,
|
|
155
|
+
y: pointerEvent.clientY,
|
|
156
|
+
data,
|
|
157
|
+
label: describe(target, maskText),
|
|
158
|
+
interactionId: `ixw-${uuid()}`,
|
|
159
|
+
timestamp,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The element the visitor actually clicked, not the wrapper the browser reports.
|
|
164
|
+
*
|
|
165
|
+
* `event.target` is RETARGETED at a shadow boundary: a click on a button inside a web component is
|
|
166
|
+
* reported as a click on the component's host, so every control inside it collapses onto one
|
|
167
|
+
* element and neither the selector nor the path describes what was hit. `composedPath()[0]` is the
|
|
168
|
+
* real node, and both of the SDK's other capture lanes already use it — the replay engine says so
|
|
169
|
+
* outright ("composedPath()[0] beats e.target, which is the host"). This lane was the one that
|
|
170
|
+
* never got the fix, and it is the lane heatmaps read.
|
|
171
|
+
*
|
|
172
|
+
* OUR OWN WIDGETS ARE DELIBERATELY NOT UNWRAPPED. Every ScaleBun surface — in-app, inbox, survey,
|
|
173
|
+
* coachmark, soft-ask, bug report — is mounted in a shadow root too, and the query layer keeps
|
|
174
|
+
* those clicks out of customer heatmaps by matching the HOST's selector. Resolving through them
|
|
175
|
+
* would produce an inner control the filter does not recognise, so clicks on ScaleBun's own UI
|
|
176
|
+
* would start being reported as clicks on the customer's page. Reporting the host for our surfaces
|
|
177
|
+
* is therefore not a limitation, it is what keeps the existing exclusion working — and it keeps
|
|
178
|
+
* that decision in one place rather than splitting it across two repos.
|
|
179
|
+
*
|
|
180
|
+
* Falls back to `event.target` whenever the composed path is unavailable or its first entry is not
|
|
181
|
+
* an element (text nodes, the document, a closed root's boundary), so nothing is lost when the
|
|
182
|
+
* answer cannot be improved.
|
|
183
|
+
*/
|
|
184
|
+
/* resolution happens inline in the click handler — see the note above. */
|
|
65
185
|
/** Click position + viewport + scroll + document height, so the dashboard can place clicks in
|
|
66
186
|
* viewport-% or DOCUMENT space across different screen sizes. `x`/`y` are viewport-relative (clientX/Y).
|
|
67
187
|
*
|
|
@@ -86,6 +206,44 @@ function pointer(me) {
|
|
|
86
206
|
dpr: w?.devicePixelRatio ?? 1,
|
|
87
207
|
};
|
|
88
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* A CONTRADICTION SIGNAL, NOT A LOCATOR — one independent fact about what was clicked.
|
|
211
|
+
*
|
|
212
|
+
* `path` resolves uniquely on real markup (measured 30/30 on the reported site) but is FRAGILE: one
|
|
213
|
+
* inserted sibling shifts an `nth-of-type` segment onto a different control, and the reader has no
|
|
214
|
+
* way to notice. This field exists so it can notice. When the element a stored path resolves to
|
|
215
|
+
* contradicts this, the reader refuses to anchor.
|
|
216
|
+
*
|
|
217
|
+
* IT MAY ONLY EVER REFUSE. Agreement is not evidence of identity: a duplicate sibling carrying the
|
|
218
|
+
* same href satisfies it exactly, which is why promoting on agreement was measured and rejected —
|
|
219
|
+
* it raised silent mis-anchors from 12 to 14 across 156 adversarial trials while looking like an
|
|
220
|
+
* improvement. Refusal-only took them from 12 to 2 and cannot, by construction, add an anchor.
|
|
221
|
+
*
|
|
222
|
+
* PRIVACY. Only the three signals below, in this order, and nothing derived from user text:
|
|
223
|
+
* · href PATHNAME — query string and fragment stripped, so tokens, emails and ids never travel.
|
|
224
|
+
* · aria-label — author-written UI wording, capped at 32 chars and rejected outright if it carries
|
|
225
|
+
* an address or a long digit run, which is what a personalised label looks like.
|
|
226
|
+
* · type — a build-time constant on form controls.
|
|
227
|
+
* Deliberately excluded: visible text, input values, full URLs, class lists.
|
|
228
|
+
*
|
|
229
|
+
* One optional field, absent when nothing safe is available, so the common case adds no bytes and an
|
|
230
|
+
* old reader that ignores it behaves exactly as before.
|
|
231
|
+
*/
|
|
232
|
+
function constraintOf(el) {
|
|
233
|
+
// Same normalisation as `stripQuery` in core/breadcrumbs and performance/resources — one regex,
|
|
234
|
+
// and it drops the fragment as well, so `#anchor` links reduce to their path.
|
|
235
|
+
const h = (el.getAttribute('href') || '').replace(/[?#].*$/, '');
|
|
236
|
+
// No `javascript:` guard: this is a comparison token, not a URL to follow, so an odd scheme is
|
|
237
|
+
// harmless and the check cost more bytes than the case is worth.
|
|
238
|
+
if (h)
|
|
239
|
+
return 'h' + h;
|
|
240
|
+
const a = (el.getAttribute('aria-label') || '').trim();
|
|
241
|
+
// A long digit run or an address is what a personalised label looks like — refuse those.
|
|
242
|
+
if (a && a.length < 33 && !/@|\d{4,}/.test(a))
|
|
243
|
+
return 'a' + a;
|
|
244
|
+
const t = el.getAttribute('type');
|
|
245
|
+
return t ? 'y' + t : undefined;
|
|
246
|
+
}
|
|
89
247
|
/** H7 — element bounding rect (for responsive re-anchoring) + opt-in component name (for component heatmaps). */
|
|
90
248
|
function anchor(el) {
|
|
91
249
|
const r = el.getBoundingClientRect();
|
|
@@ -110,43 +268,14 @@ function anchor(el) {
|
|
|
110
268
|
if (pc)
|
|
111
269
|
out.fx = pc;
|
|
112
270
|
out.path = structuralPath(el);
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
115
|
-
out.
|
|
271
|
+
const cx = constraintOf(el);
|
|
272
|
+
if (cx)
|
|
273
|
+
out.cx = cx;
|
|
116
274
|
const comp = el.closest('[data-scalebun-component]')?.getAttribute('data-scalebun-component');
|
|
117
275
|
if (comp)
|
|
118
276
|
out.component = comp;
|
|
119
277
|
return out;
|
|
120
278
|
}
|
|
121
|
-
/**
|
|
122
|
-
* S1 — WHICH OVERLAYS WERE OPEN when this click happened.
|
|
123
|
-
*
|
|
124
|
-
* A click on a menu item only means something if the menu was open. Nothing stored today says so, and
|
|
125
|
-
* it is not derivable afterwards from anything else on the event — the same lesson as `fx` and
|
|
126
|
-
* `path`: capture has to lead use, and history cannot be backfilled. Without this the dashboard can
|
|
127
|
-
* SHOW a state (the viewer can open and close the reconstruction) but cannot COUNT one, because every
|
|
128
|
-
* click on the route is aggregated into a single picture regardless of what was on screen.
|
|
129
|
-
*
|
|
130
|
-
* PLATFORM-DEFINED STATE ONLY: `aria-expanded="true"` and `<dialog open>`. Deliberately no guessing at
|
|
131
|
-
* `.menu-open` or `.is-active` — a class heuristic is right on some sites and silently wrong on
|
|
132
|
-
* others, and a wrong state key partitions clicks into buckets that do not correspond to anything.
|
|
133
|
-
*
|
|
134
|
-
* SORTED, so the same set of open overlays produces the same key whatever order the DOM happens to
|
|
135
|
-
* return them in — otherwise one state would fragment into several and the counts would be
|
|
136
|
-
* meaningless.
|
|
137
|
-
*
|
|
138
|
-
* Emitted ONLY when something is open, exactly as `fx` is. The overwhelming majority of clicks happen
|
|
139
|
-
* with nothing open and pay nothing at all; the length cap bounds the rest.
|
|
140
|
-
*/
|
|
141
|
-
function openOverlays() {
|
|
142
|
-
const out = [];
|
|
143
|
-
document.querySelectorAll('[aria-expanded="true"],dialog[open]').forEach((e) => {
|
|
144
|
-
const k = e.getAttribute('aria-controls') || e.getAttribute('aria-label') || e.id;
|
|
145
|
-
if (k)
|
|
146
|
-
out.push(k);
|
|
147
|
-
});
|
|
148
|
-
return out.length ? out.sort().join('|').slice(0, 64) : undefined;
|
|
149
|
-
}
|
|
150
279
|
/**
|
|
151
280
|
* True when this click landed on a VIEWPORT-anchored element — one that does not move as the page
|
|
152
281
|
* scrolls, because it or an ancestor computes to `position: fixed`.
|
|
@@ -253,7 +382,14 @@ export function describe(el, maskText = false) {
|
|
|
253
382
|
return tag;
|
|
254
383
|
return `${tag}: ${maskText ? mask(text) : text}`;
|
|
255
384
|
}
|
|
256
|
-
/**
|
|
385
|
+
/**
|
|
386
|
+
* A compact CSS-ish selector (tag#id.class) — structure only, no text/PII.
|
|
387
|
+
*
|
|
388
|
+
* Byte-identical to autocapture's copy, and DELIBERATELY left duplicated. Sharing it through a
|
|
389
|
+
* `core/` module was tried and measured: the eager bundle grew 136 B rather than shrinking, because
|
|
390
|
+
* gzip already collapses the second copy to almost nothing while a new module boundary costs real
|
|
391
|
+
* bytes. Deduplicate this only if the two lanes ever need to agree on the value, not to save space.
|
|
392
|
+
*/
|
|
257
393
|
function selectorOf(el) {
|
|
258
394
|
let s = el.tagName.toLowerCase();
|
|
259
395
|
if (el.id)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InteractionTracker.js","sourceRoot":"","sources":["../../../src/features/journey/InteractionTracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"InteractionTracker.js","sourceRoot":"","sources":["../../../src/features/journey/InteractionTracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* F-JRN — journey + navigation. Emits SCREEN_VIEW (route changes) and USER_ACTION (clicks
|
|
2
|
+
* F-JRN — journey + navigation. Emits SCREEN_VIEW (route changes) and USER_ACTION (clicks enriched with rage/dead
|
|
3
3
|
* friction) as `kind:'event'` items → the existing funnels / flows / friction analytics. Always-on,
|
|
4
4
|
* decoupled from replay.
|
|
5
5
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JourneyFeature.d.ts","sourceRoot":"","sources":["../../../src/features/journey/JourneyFeature.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAMxE,qBAAa,cAAe,YAAW,QAAQ;IAC7C,QAAQ,CAAC,IAAI,aAAa;IAC1B,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,GAAG,CAAC,CAAoB;IAChC,OAAO,CAAC,YAAY,CAAC,CAAqB;IAE1C,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"JourneyFeature.d.ts","sourceRoot":"","sources":["../../../src/features/journey/JourneyFeature.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAMxE,qBAAa,cAAe,YAAW,QAAQ;IAC7C,QAAQ,CAAC,IAAI,aAAa;IAC1B,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,GAAG,CAAC,CAAoB;IAChC,OAAO,CAAC,YAAY,CAAC,CAAqB;IAE1C,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI;IAoBhC,OAAO,CAAC,IAAI;IA0BZ,QAAQ,IAAI,IAAI;CAKjB"}
|
|
@@ -15,7 +15,7 @@ export class JourneyFeature {
|
|
|
15
15
|
if (typeof window === 'undefined' || typeof document === 'undefined')
|
|
16
16
|
return;
|
|
17
17
|
this.nav = new NavigationTracker((from, to) => this.emit('SCREEN_VIEW', { from, to }, undefined, to));
|
|
18
|
-
this.interactions = new InteractionTracker((name, data, label) => this.emit(name, data, label, currentPath()),
|
|
18
|
+
this.interactions = new InteractionTracker((name, data, label, interaction) => this.emit(name, data, label, currentPath(), interaction),
|
|
19
19
|
// Label masking is opt-IN (`privacy.maskEventText`). Off by default so USER_ACTION rows carry the
|
|
20
20
|
// real element text and group per element instead of collapsing by text shape.
|
|
21
21
|
{ maskText: ctx.maskEventText === true }, (reason) => this.ctx?.signal(reason));
|
|
@@ -23,12 +23,12 @@ export class JourneyFeature {
|
|
|
23
23
|
this.interactions.start();
|
|
24
24
|
this.active = true;
|
|
25
25
|
}
|
|
26
|
-
emit(name, props, label, screenName) {
|
|
26
|
+
emit(name, props, label, screenName, interaction) {
|
|
27
27
|
// Heatmaps 2.0 (H5) — stamp WHERE the replay was when this happened, so a heatmap hotspot (or friction
|
|
28
28
|
// click) deep-links to the EXACT replay frame, not just the session. Cheap (a getter + spread); only
|
|
29
29
|
// present when replay is producing frames (undefined otherwise). Closes the journey↔replay gap that
|
|
30
30
|
// left the richest heatmap signal (clicks + rage/dead) without a frame coordinate.
|
|
31
|
-
const replay = getReplayCoord();
|
|
31
|
+
const replay = props.replayCoord ? undefined : getReplayCoord();
|
|
32
32
|
const item = {
|
|
33
33
|
kind: 'event',
|
|
34
34
|
name,
|
|
@@ -36,7 +36,9 @@ export class JourneyFeature {
|
|
|
36
36
|
label,
|
|
37
37
|
screenName: screenName ?? currentPath(),
|
|
38
38
|
eventId: `evt_${uuid()}`,
|
|
39
|
-
timestamp: now(),
|
|
39
|
+
timestamp: interaction?.timestamp ?? now(),
|
|
40
|
+
interactionId: interaction?.interactionId,
|
|
41
|
+
interactionProtocol: interaction ? 1 : undefined,
|
|
40
42
|
};
|
|
41
43
|
this.ctx?.emit(item);
|
|
42
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JourneyFeature.js","sourceRoot":"","sources":["../../../src/features/journey/JourneyFeature.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,OAAO,cAAc;IAA3B;QACW,SAAI,GAAG,SAAS,CAAC;QAClB,QAAG,GAA0B,IAAI,CAAC;QAClC,WAAM,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"JourneyFeature.js","sourceRoot":"","sources":["../../../src/features/journey/JourneyFeature.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,14 +16,13 @@ export declare class RageDetector {
|
|
|
16
16
|
/** Fires a callback when a click had NO DOM change and NO navigation within `deadMs`. */
|
|
17
17
|
export declare class DeadClickWatcher {
|
|
18
18
|
private readonly deadMs;
|
|
19
|
-
private
|
|
19
|
+
private changeVersion;
|
|
20
20
|
private observer;
|
|
21
21
|
private timers;
|
|
22
|
-
private startHref;
|
|
23
22
|
constructor(deadMs?: number);
|
|
24
23
|
observe(target: Node): void;
|
|
25
|
-
/**
|
|
26
|
-
onClick(
|
|
24
|
+
/** Resolve exactly once after the observation window. `true` means no DOM change + no navigation. */
|
|
25
|
+
onClick(onResult: (dead: boolean) => void): void;
|
|
27
26
|
disconnect(): void;
|
|
28
27
|
}
|
|
29
28
|
//# sourceMappingURL=friction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"friction.d.ts","sourceRoot":"","sources":["../../../src/features/journey/friction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yGAAyG;AACzG,qBAAa,YAAY;IAGrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAJtB,OAAO,CAAC,OAAO,CAAkD;gBAE9C,QAAQ,SAAM,EACd,MAAM,SAAK,EACX,GAAG,SAAI;IAG1B,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;CAM/C;AAED,yFAAyF;AACzF,qBAAa,gBAAgB;
|
|
1
|
+
{"version":3,"file":"friction.d.ts","sourceRoot":"","sources":["../../../src/features/journey/friction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yGAAyG;AACzG,qBAAa,YAAY;IAGrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAJtB,OAAO,CAAC,OAAO,CAAkD;gBAE9C,QAAQ,SAAM,EACd,MAAM,SAAK,EACX,GAAG,SAAI;IAG1B,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;CAM/C;AAED,yFAAyF;AACzF,qBAAa,gBAAgB;IAKf,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,MAAM,CAA4C;gBAE7B,MAAM,SAAM;IAEzC,OAAO,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI;IAQ3B,qGAAqG;IACrG,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAUhD,UAAU,IAAI,IAAI;CAMnB"}
|
|
@@ -23,26 +23,25 @@ export class RageDetector {
|
|
|
23
23
|
export class DeadClickWatcher {
|
|
24
24
|
constructor(deadMs = 700) {
|
|
25
25
|
this.deadMs = deadMs;
|
|
26
|
-
this.
|
|
26
|
+
this.changeVersion = 0;
|
|
27
27
|
this.observer = null;
|
|
28
28
|
this.timers = new Set();
|
|
29
|
-
this.startHref = '';
|
|
30
29
|
}
|
|
31
30
|
observe(target) {
|
|
32
31
|
if (typeof MutationObserver === 'undefined')
|
|
33
32
|
return;
|
|
34
|
-
this.startHref = href();
|
|
35
33
|
this.observer = new MutationObserver(() => {
|
|
36
|
-
this.
|
|
34
|
+
this.changeVersion++;
|
|
37
35
|
});
|
|
38
36
|
this.observer.observe(target, { subtree: true, childList: true, attributes: true, characterData: true });
|
|
39
37
|
}
|
|
40
|
-
/**
|
|
41
|
-
onClick(
|
|
38
|
+
/** Resolve exactly once after the observation window. `true` means no DOM change + no navigation. */
|
|
39
|
+
onClick(onResult) {
|
|
40
|
+
const version = this.changeVersion;
|
|
41
|
+
const startHref = href();
|
|
42
42
|
const timer = setTimeout(() => {
|
|
43
43
|
this.timers.delete(timer);
|
|
44
|
-
|
|
45
|
-
onDead();
|
|
44
|
+
onResult(this.changeVersion === version && href() === startHref);
|
|
46
45
|
}, this.deadMs);
|
|
47
46
|
this.timers.add(timer);
|
|
48
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"friction.js","sourceRoot":"","sources":["../../../src/features/journey/friction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yGAAyG;AACzG,MAAM,OAAO,YAAY;IAEvB,YACmB,WAAW,GAAG,EACd,SAAS,EAAE,EACX,MAAM,CAAC;QAFP,aAAQ,GAAR,QAAQ,CAAM;QACd,WAAM,GAAN,MAAM,CAAK;QACX,QAAG,GAAH,GAAG,CAAI;QAJlB,YAAO,GAA+C,EAAE,CAAC;IAK9D,CAAC;IAEJ,IAAI,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS;QAClC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;QACrF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC;IACrC,CAAC;CACF;AAED,yFAAyF;AACzF,MAAM,OAAO,gBAAgB;
|
|
1
|
+
{"version":3,"file":"friction.js","sourceRoot":"","sources":["../../../src/features/journey/friction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI STATE AT INTERACTION TIME — which visual surface the visitor was actually looking at.
|
|
3
|
+
*
|
|
4
|
+
* A page is not just `/products`. It is `/products` with the cart drawer shut, or with it open, or
|
|
5
|
+
* with the reviews tab selected. Those are different interaction surfaces, and a click on one must
|
|
6
|
+
* never be counted on another: heat from the open drawer painted over the closed layout is a picture
|
|
7
|
+
* of something that never existed.
|
|
8
|
+
*
|
|
9
|
+
* WHAT WAS WRONG WITH THE PREVIOUS SHAPE. State was captured as an opaque set of the things that
|
|
10
|
+
* happened to be OPEN — `menu|dialog`, sorted and joined. Two consequences:
|
|
11
|
+
*
|
|
12
|
+
* 1. `sidebar = closed` was not representable. Absence meant "nothing open", which conflates a shut
|
|
13
|
+
* sidebar with a page that has no sidebar. But the click that OPENS a drawer belongs to the
|
|
14
|
+
* closed state — that is what the visitor saw when they reached for it — so the closed value has
|
|
15
|
+
* to exist to receive it.
|
|
16
|
+
* 2. It could not be queried by dimension. Asking for `sidebar=open` meant matching the whole
|
|
17
|
+
* signature, so every unrelated dimension had to match too and the buckets fragmented
|
|
18
|
+
* combinatorially.
|
|
19
|
+
*
|
|
20
|
+
* THE SHAPE NOW: `name:value` pairs, sorted, joined with `;`. Still ONE short string on the wire — no
|
|
21
|
+
* new field — but the backend can split it and match one dimension without constraining the others.
|
|
22
|
+
*
|
|
23
|
+
* DELIBERATELY NARROW, because every byte here is eager. Only `aria-expanded` (read in BOTH states,
|
|
24
|
+
* which is what makes "closed" a value rather than an absence) and `dialog` (likewise). No
|
|
25
|
+
* `aria-selected`, no `aria-current`, no `details` — those were measured at ~90 B of bundle for
|
|
26
|
+
* dimensions the host can declare explicitly for free. No class-name guessing at any price: `.is-open`
|
|
27
|
+
* is right on some sites and silently wrong on others, and a wrong state key partitions clicks into
|
|
28
|
+
* buckets that correspond to nothing.
|
|
29
|
+
*
|
|
30
|
+
* Anything the platform does not express is `setUiState`'s job, and an explicit dimension WINS for the
|
|
31
|
+
* same name — a heuristic that silently overrode a declaration would make the API pointless.
|
|
32
|
+
*/
|
|
33
|
+
/** Declare a UI state dimension the DOM does not express. */
|
|
34
|
+
export declare function setUiState(name: string, value: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Stop reporting a dimension — or, with no name, every declared dimension.
|
|
37
|
+
*
|
|
38
|
+
* Later interactions carry no value for it, which is not the same as carrying a value. Clearing all is
|
|
39
|
+
* what a single-page app wants on route change: declarations from the previous screen would otherwise
|
|
40
|
+
* follow the visitor onto a surface where they mean nothing.
|
|
41
|
+
*/
|
|
42
|
+
export declare function clearUiState(name?: string): void;
|
|
43
|
+
/**
|
|
44
|
+
* The state signature for the interaction happening RIGHT NOW.
|
|
45
|
+
*
|
|
46
|
+
* Read synchronously inside the event handler, never reconstructed afterwards: by the time a menu has
|
|
47
|
+
* closed, the DOM no longer knows it was open when the click landed.
|
|
48
|
+
*/
|
|
49
|
+
export declare function uiStateSignature(): string | undefined;
|
|
50
|
+
//# sourceMappingURL=uiState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uiState.d.ts","sourceRoot":"","sources":["../../../src/features/journey/uiState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAaH,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAI5D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAGhD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,SAAS,CA2BrD"}
|