@corbado/observe 1.8.0 → 1.9.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/analysis/client.mjs +82 -56
- package/dist/analysis/config-manager.mjs +30 -5
- package/dist/analysis/device-info-manager.mjs +16 -1
- package/dist/analysis/device.mjs +88 -10
- package/dist/analysis/handoff.mjs +7 -0
- package/dist/analysis/low-events/grouped-input-tracker.mjs +148 -33
- package/dist/analysis/low-events/input-tracker.mjs +138 -59
- package/dist/analysis/low-events/shared-window-observer.mjs +148 -77
- package/dist/analysis/low-events/viewport-batcher.mjs +3 -3
- package/dist/analysis/queue.mjs +75 -8
- package/dist/analysis/{low-events/input-paste-observer.mjs → readers/input-paste.mjs} +2 -2
- package/dist/analysis/readers/input.mjs +61 -0
- package/dist/analysis/readers/loader-global.mjs +15 -0
- package/dist/analysis/readers/network.mjs +14 -0
- package/dist/analysis/readers/page.mjs +30 -0
- package/dist/analysis/readers/storage.mjs +28 -0
- package/dist/analysis/{utils → readers}/web-lock.mjs +6 -2
- package/dist/analysis/readers/window-events.mjs +48 -0
- package/dist/analysis/storage/index.mjs +61 -9
- package/dist/analysis/store/event-store.mjs +34 -16
- package/dist/analysis/transports/fetch.mjs +45 -3
- package/dist/analysis/utils/email-link-operation-full.mjs +22 -2
- package/dist/analysis/utils/email-otp-operation-full.mjs +0 -1
- package/dist/analysis/utils/form-operation-full.mjs +0 -1
- package/dist/analysis/utils/operation-full-provide-identifier-with-cui.mjs +6 -6
- package/dist/analysis/utils/passkey-enrollment-operation-full.mjs +9 -6
- package/dist/analysis/utils/passkey-login-operation-full.mjs +10 -5
- package/dist/analysis/utils/password-login-operation-full.mjs +8 -4
- package/dist/analysis/utils/password-manager-sampler.mjs +44 -4
- package/dist/analysis/utils/sms-otp-operation-full.mjs +0 -1
- package/dist/analysis/utils/totp-operation-full.mjs +0 -1
- package/dist/analysis/version.mjs +1 -1
- package/dist/cdn/script.global.js +4 -4
- package/dist/index.d.mts +131 -19
- package/dist/index.d.ts +131 -19
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +5 -3
- package/tools/reader-meta-text.d.mts +2 -0
- package/tools/reader-meta-text.mjs +26 -0
- package/tools/strip-reader-meta.d.mts +33 -0
- package/tools/strip-reader-meta.mjs +131 -0
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
// src/low-events/input-tracker.ts
|
|
2
2
|
import { InputChangeRecorder } from "./input-change-recorder.mjs";
|
|
3
|
-
import { InputPasteObserver } from "
|
|
3
|
+
import { InputPasteObserver } from "../readers/input-paste.mjs";
|
|
4
|
+
import { observeInputField, readFocusedField, readInputContentLength } from "../readers/input.mjs";
|
|
5
|
+
import { listenWindow } from "../readers/page.mjs";
|
|
4
6
|
import { readPasswordManagerDomSignals } from "../readers/password-manager-dom.mjs";
|
|
5
7
|
var MOUNT_PROBE_DELAY_MS = 500;
|
|
6
8
|
var FOCUS_PROBE_DELAY_MS = 250;
|
|
7
9
|
function fieldContentLength(field) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
return readInputContentLength(
|
|
11
|
+
{
|
|
12
|
+
key: "dom:input.content",
|
|
13
|
+
title: "Length of the text in tracked input fields",
|
|
14
|
+
accessNote: "input value length only; the value itself does not leave the reader",
|
|
15
|
+
group: "Input interaction",
|
|
16
|
+
origin: "observe-external",
|
|
17
|
+
fields: [
|
|
18
|
+
{ path: "value.length", label: "Length of the entered text", exposure: "size" },
|
|
19
|
+
{
|
|
20
|
+
path: "checked",
|
|
21
|
+
label: "Whether a checkbox or option is selected",
|
|
22
|
+
exposure: "enum",
|
|
23
|
+
transform: { values: [true, false], note: "checkbox and radio only" }
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
field
|
|
28
|
+
);
|
|
10
29
|
}
|
|
11
30
|
var LowEventInputTracker = class {
|
|
12
31
|
constructor(logger, inputField, sink) {
|
|
@@ -20,6 +39,27 @@ var LowEventInputTracker = class {
|
|
|
20
39
|
sink.reserveCaptureSeq
|
|
21
40
|
);
|
|
22
41
|
this.pasteObserver = new InputPasteObserver(
|
|
42
|
+
{
|
|
43
|
+
key: "dom:input.paste",
|
|
44
|
+
title: "Paste into tracked input fields",
|
|
45
|
+
accessNote: "paste events on the input elements the integration passes to Observe; pasted values never leave the reader",
|
|
46
|
+
group: "Input interaction",
|
|
47
|
+
origin: "observe-external",
|
|
48
|
+
fields: [
|
|
49
|
+
{
|
|
50
|
+
path: "event.paste",
|
|
51
|
+
label: "Paste into the field",
|
|
52
|
+
exposure: "presence",
|
|
53
|
+
transform: { note: "trusted paste events only" }
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
path: "value",
|
|
57
|
+
label: "Length of the pasted text",
|
|
58
|
+
exposure: "size",
|
|
59
|
+
transform: { note: "inserted length only; values are compared in memory and discarded" }
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
},
|
|
23
63
|
[inputField],
|
|
24
64
|
logger,
|
|
25
65
|
(ts, _inserted, captureSeq) => {
|
|
@@ -36,27 +76,13 @@ var LowEventInputTracker = class {
|
|
|
36
76
|
},
|
|
37
77
|
sink.reserveCaptureSeq
|
|
38
78
|
);
|
|
39
|
-
this.boundHandlers = {
|
|
40
|
-
focus: this.wrapHandler("focus", () => this.handleFocus()),
|
|
41
|
-
blur: this.wrapHandler("blur", () => this.handleBlur()),
|
|
42
|
-
pointerdown: this.wrapHandler("pointerdown", () => this.enqueueNonInputEvent("pointerdown")),
|
|
43
|
-
pointerup: this.wrapHandler("pointerup", () => this.enqueueNonInputEvent("pointerup")),
|
|
44
|
-
click: this.wrapHandler("click", () => this.enqueueNonInputEvent("click")),
|
|
45
|
-
input: (event) => {
|
|
46
|
-
try {
|
|
47
|
-
this.handleInput(event);
|
|
48
|
-
} catch (error) {
|
|
49
|
-
this.logger.error("Failed to observe input", error);
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
keyup: this.wrapKeyboardHandler("keyup", (event) => this.handleKeyUp(event)),
|
|
53
|
-
pageHide: this.wrapHandler("pagehide", () => this.flushForTeardown())
|
|
54
|
-
};
|
|
55
79
|
this.unregisterInputs = sink.registerInputs?.([inputField], () => this.flushPending());
|
|
56
80
|
this.capturePrefill();
|
|
57
81
|
this.attach();
|
|
58
82
|
}
|
|
59
83
|
unregisterInputs;
|
|
84
|
+
removeFieldListeners;
|
|
85
|
+
removePageHide;
|
|
60
86
|
changes;
|
|
61
87
|
pasteObserver;
|
|
62
88
|
emittedSignals = /* @__PURE__ */ new Set();
|
|
@@ -64,7 +90,6 @@ var LowEventInputTracker = class {
|
|
|
64
90
|
mountProbeTimer = null;
|
|
65
91
|
focusProbeTimer = null;
|
|
66
92
|
destroyed = false;
|
|
67
|
-
boundHandlers;
|
|
68
93
|
destroy() {
|
|
69
94
|
if (this.destroyed) {
|
|
70
95
|
return;
|
|
@@ -89,18 +114,93 @@ var LowEventInputTracker = class {
|
|
|
89
114
|
}
|
|
90
115
|
attach() {
|
|
91
116
|
const input = this.inputField;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
117
|
+
this.removeFieldListeners = observeInputField(
|
|
118
|
+
{
|
|
119
|
+
key: "dom:input.events",
|
|
120
|
+
title: "Interaction with tracked input fields",
|
|
121
|
+
accessNote: "focus, blur, pointerdown, pointerup, click, input and keyup events on the input elements the integration passes to Observe, reduced to booleans and identities",
|
|
122
|
+
group: "Input interaction",
|
|
123
|
+
origin: "observe-external",
|
|
124
|
+
fields: [
|
|
125
|
+
{ path: "event.focus", label: "Field selected", exposure: "presence" },
|
|
126
|
+
{ path: "event.blur", label: "Field left", exposure: "presence" },
|
|
127
|
+
{ path: "event.pointerdown", label: "Pointer pressed on the field", exposure: "presence" },
|
|
128
|
+
{ path: "event.pointerup", label: "Pointer released on the field", exposure: "presence" },
|
|
129
|
+
{ path: "event.click", label: "Click on the field", exposure: "presence" },
|
|
130
|
+
{ path: "event.input", label: "Typing in the field", exposure: "presence" },
|
|
131
|
+
{
|
|
132
|
+
path: "event.input.isComposing",
|
|
133
|
+
label: "Whether text is being composed with an input method",
|
|
134
|
+
exposure: "enum",
|
|
135
|
+
transform: { values: [true, false] }
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
path: "event.keyup.key",
|
|
139
|
+
label: "Escape key pressed",
|
|
140
|
+
exposure: "enum",
|
|
141
|
+
transform: { values: ["Escape", "other"] }
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
input,
|
|
146
|
+
{
|
|
147
|
+
focus: () => this.handleFocus(),
|
|
148
|
+
blur: () => this.handleBlur(),
|
|
149
|
+
pointerdown: () => this.enqueueNonInputEvent("pointerdown"),
|
|
150
|
+
pointerup: () => this.enqueueNonInputEvent("pointerup"),
|
|
151
|
+
click: () => this.enqueueNonInputEvent("click"),
|
|
152
|
+
input: (composing, event) => this.handleInput(event, composing),
|
|
153
|
+
keyupEscape: () => this.enqueueNonInputEvent("keyup-escape")
|
|
154
|
+
},
|
|
155
|
+
(name, run) => {
|
|
156
|
+
try {
|
|
157
|
+
run();
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (name === "input") this.logger.error("Failed to observe input", error);
|
|
160
|
+
else this.logger.error(`Failed to handle low event "${name}"`, error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
);
|
|
99
164
|
if (typeof window !== "undefined") {
|
|
100
|
-
|
|
165
|
+
this.removePageHide = listenWindow(
|
|
166
|
+
{
|
|
167
|
+
key: "page:lifecycle.pagehide",
|
|
168
|
+
title: "Leaving the page",
|
|
169
|
+
accessNote: "pagehide event on window",
|
|
170
|
+
group: "Page lifecycle",
|
|
171
|
+
origin: "observe-external",
|
|
172
|
+
fields: [
|
|
173
|
+
{
|
|
174
|
+
path: "event.pagehide",
|
|
175
|
+
label: "Leaving the page",
|
|
176
|
+
exposure: "presence",
|
|
177
|
+
transform: { note: "flushes pending input low events on unload" }
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
"pagehide",
|
|
182
|
+
this.wrapHandler("pagehide", () => this.flushForTeardown())
|
|
183
|
+
);
|
|
101
184
|
}
|
|
102
185
|
if (typeof document !== "undefined") {
|
|
103
|
-
if (
|
|
186
|
+
if (readFocusedField(
|
|
187
|
+
{
|
|
188
|
+
key: "dom:input.focused",
|
|
189
|
+
title: "Whether a tracked input field has focus",
|
|
190
|
+
accessNote: "document.activeElement compared with the passed input elements",
|
|
191
|
+
group: "Input interaction",
|
|
192
|
+
origin: "observe-external",
|
|
193
|
+
fields: [
|
|
194
|
+
{
|
|
195
|
+
path: "focused",
|
|
196
|
+
label: "Whether the field is selected",
|
|
197
|
+
exposure: "enum",
|
|
198
|
+
transform: { values: [true, false] }
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
[input]
|
|
203
|
+
)) {
|
|
104
204
|
this.handleFocus();
|
|
105
205
|
}
|
|
106
206
|
}
|
|
@@ -109,16 +209,9 @@ var LowEventInputTracker = class {
|
|
|
109
209
|
}
|
|
110
210
|
}
|
|
111
211
|
detach() {
|
|
112
|
-
|
|
113
|
-
input.removeEventListener("focus", this.boundHandlers.focus);
|
|
114
|
-
input.removeEventListener("blur", this.boundHandlers.blur);
|
|
115
|
-
input.removeEventListener("pointerdown", this.boundHandlers.pointerdown);
|
|
116
|
-
input.removeEventListener("pointerup", this.boundHandlers.pointerup);
|
|
117
|
-
input.removeEventListener("click", this.boundHandlers.click);
|
|
118
|
-
input.removeEventListener("input", this.boundHandlers.input);
|
|
119
|
-
input.removeEventListener("keyup", this.boundHandlers.keyup);
|
|
212
|
+
this.removeFieldListeners?.();
|
|
120
213
|
if (typeof window !== "undefined") {
|
|
121
|
-
|
|
214
|
+
this.removePageHide?.();
|
|
122
215
|
}
|
|
123
216
|
}
|
|
124
217
|
pushLow = (low) => {
|
|
@@ -129,9 +222,14 @@ var LowEventInputTracker = class {
|
|
|
129
222
|
if (typeof __CBO_DATA_PMD__ === "boolean" ? __CBO_DATA_PMD__ : this.sink.passwordManagerDomScan?.() !== false) {
|
|
130
223
|
const matches = readPasswordManagerDomSignals({
|
|
131
224
|
key: "dom:password-managers.dom-signals",
|
|
225
|
+
title: "Password manager traces in the page",
|
|
226
|
+
accessNote: "DOM markers that password manager extensions insert (attributes, elements, styles)",
|
|
227
|
+
group: "Password manager detection",
|
|
228
|
+
origin: "observe-external",
|
|
132
229
|
fields: [
|
|
133
230
|
{
|
|
134
231
|
path: "signals[]",
|
|
232
|
+
label: "Password manager traces found",
|
|
135
233
|
exposure: "enum",
|
|
136
234
|
transform: { values: ["pa-1", "pa-2", "pa-3", "pa-4", "pa-5", "pa-6"] }
|
|
137
235
|
}
|
|
@@ -179,19 +277,9 @@ var LowEventInputTracker = class {
|
|
|
179
277
|
this.pasteObserver.flush();
|
|
180
278
|
this.enqueueNonInputEvent("blur");
|
|
181
279
|
}
|
|
182
|
-
handleInput(event) {
|
|
280
|
+
handleInput(event, composing) {
|
|
183
281
|
if (this.pasteObserver.handlesInput(event)) return;
|
|
184
|
-
this.changes.record(
|
|
185
|
-
fieldContentLength(this.inputField),
|
|
186
|
-
Date.now(),
|
|
187
|
-
event.isComposing || event.inputType === "insertCompositionText"
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
handleKeyUp(event) {
|
|
191
|
-
if (event.key !== "Escape") {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
this.enqueueNonInputEvent("keyup-escape");
|
|
282
|
+
this.changes.record(fieldContentLength(this.inputField), Date.now(), composing);
|
|
195
283
|
}
|
|
196
284
|
flushForTeardown() {
|
|
197
285
|
this.pasteObserver.flush();
|
|
@@ -207,15 +295,6 @@ var LowEventInputTracker = class {
|
|
|
207
295
|
}
|
|
208
296
|
};
|
|
209
297
|
}
|
|
210
|
-
wrapKeyboardHandler(name, fn) {
|
|
211
|
-
return (event) => {
|
|
212
|
-
try {
|
|
213
|
-
fn(event);
|
|
214
|
-
} catch (error) {
|
|
215
|
-
this.logger.error(`Failed to handle low event "${name}"`, error);
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
298
|
};
|
|
220
299
|
export {
|
|
221
300
|
LowEventInputTracker,
|
|
@@ -2,64 +2,124 @@
|
|
|
2
2
|
import { reserveCaptureSeq } from "./capture-sequence.mjs";
|
|
3
3
|
import { v7 as uuidv7 } from "uuid";
|
|
4
4
|
import { createViewportBatcher } from "./viewport-batcher.mjs";
|
|
5
|
+
import { observeWindowLowEvents, readActiveFocus, readFocusedChain } from "../readers/window-events.mjs";
|
|
5
6
|
var documentPageId;
|
|
6
7
|
var getCapturePageId = () => documentPageId ??= uuidv7();
|
|
7
|
-
var focusedElements = () => {
|
|
8
|
-
const chain = [];
|
|
9
|
-
for (let active = document.activeElement; active; active = active.shadowRoot?.activeElement ?? null) {
|
|
10
|
-
chain.push(active);
|
|
11
|
-
}
|
|
12
|
-
return chain;
|
|
13
|
-
};
|
|
14
8
|
var SharedWindowObserver = class {
|
|
15
9
|
constructor(logger, enqueue, flushKeepalive) {
|
|
16
10
|
this.logger = logger;
|
|
17
11
|
this.enqueue = enqueue;
|
|
18
12
|
this.flushKeepalive = flushKeepalive;
|
|
19
13
|
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
20
|
-
this.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
14
|
+
this.removers = observeWindowLowEvents(
|
|
15
|
+
{
|
|
16
|
+
key: "page:window.low-events",
|
|
17
|
+
title: "Window focus, visibility, size and back navigation",
|
|
18
|
+
accessNote: "window blur/focus, document visibilitychange/focusin/focusout/pointerdown, visualViewport resize/scroll, pagehide, Navigation API currententrychange (traverse)",
|
|
19
|
+
group: "Window interaction",
|
|
20
|
+
origin: "observe-external",
|
|
21
|
+
fields: [
|
|
22
|
+
{ path: "event.blur", label: "Window left", exposure: "presence" },
|
|
23
|
+
{ path: "event.focus", label: "Window selected", exposure: "presence" },
|
|
24
|
+
{
|
|
25
|
+
path: "event.visibilitychange",
|
|
26
|
+
label: "Page hidden or shown again",
|
|
27
|
+
exposure: "enum",
|
|
28
|
+
transform: { values: ["hidden", "visible"] }
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
path: "event.focusout",
|
|
32
|
+
label: "Whether a tracked field lost focus",
|
|
33
|
+
exposure: "computed",
|
|
34
|
+
transform: { note: "only whether a handed-over input lost focus to nothing" }
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
path: "event.focusin",
|
|
38
|
+
label: "Which tracked field gained focus",
|
|
39
|
+
exposure: "computed",
|
|
40
|
+
transform: { note: "only which handed-over input gained focus" }
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
path: "event.pointerdown",
|
|
44
|
+
label: "Whether a tracked field was pressed",
|
|
45
|
+
exposure: "computed",
|
|
46
|
+
transform: { note: "only whether the pointer went to a handed-over input" }
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
path: "event.visualviewport.resize",
|
|
50
|
+
label: "Duration of zooming or resizing",
|
|
51
|
+
exposure: "size",
|
|
52
|
+
transform: { note: "batched duration only" }
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
path: "event.visualviewport.scroll",
|
|
56
|
+
label: "Duration of scrolling",
|
|
57
|
+
exposure: "size",
|
|
58
|
+
transform: { note: "batched duration only" }
|
|
59
|
+
},
|
|
60
|
+
{ path: "event.pagehide", label: "Leaving the page", exposure: "presence" },
|
|
61
|
+
{
|
|
62
|
+
path: "event.navigation.back",
|
|
63
|
+
label: "Going back within the page",
|
|
64
|
+
exposure: "enum",
|
|
65
|
+
transform: { values: [true, false], note: "committed same-document traverse to an earlier entry" }
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
blur: () => {
|
|
71
|
+
this.flushInputs();
|
|
72
|
+
this.bracket = this.context();
|
|
73
|
+
this.emit({ lowType: "window-blur", ts: Date.now() }, this.bracket);
|
|
74
|
+
},
|
|
75
|
+
focus: () => {
|
|
76
|
+
this.flushInputs();
|
|
77
|
+
this.emit({ lowType: "window-focus", ts: Date.now() }, this.bracket ?? this.context());
|
|
78
|
+
this.bracket = void 0;
|
|
79
|
+
},
|
|
80
|
+
visibilityChange: (hidden) => {
|
|
81
|
+
this.flushInputs();
|
|
82
|
+
this.emit({
|
|
83
|
+
lowType: `document-visibilitychange-${hidden ? "hidden" : "visible"}`,
|
|
84
|
+
ts: Date.now()
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
focusOut: (target, hasRelatedTarget) => {
|
|
88
|
+
const registration = this.findInput(target);
|
|
89
|
+
this.lastBlur = registration && !hasRelatedTarget && Date.now() - this.unrelatedPointerAt > 250 ? { registration, ts: Date.now() } : void 0;
|
|
90
|
+
},
|
|
91
|
+
focusIn: (composedPath) => {
|
|
92
|
+
this.lastBlur = void 0;
|
|
93
|
+
const field = this.inputFocusListeners.size > 0 && composedPath().find((target) => this.findInput(target));
|
|
94
|
+
if (field) this.notifyInputFocus(field);
|
|
95
|
+
},
|
|
96
|
+
pointerDown: (target) => {
|
|
97
|
+
if (!this.findInput(target)) {
|
|
98
|
+
this.lastBlur = void 0;
|
|
99
|
+
this.unrelatedPointerAt = Date.now();
|
|
100
|
+
} else this.unrelatedPointerAt = -Infinity;
|
|
101
|
+
},
|
|
102
|
+
viewportResize: () => this.extendViewport("resize"),
|
|
103
|
+
viewportScroll: () => this.extendViewport("scroll"),
|
|
104
|
+
pageHide: () => {
|
|
105
|
+
this.flushInputs();
|
|
106
|
+
this.resize.flush();
|
|
107
|
+
this.scroll.flush();
|
|
108
|
+
this.flushKeepalive();
|
|
109
|
+
},
|
|
110
|
+
navigateBack: () => {
|
|
111
|
+
this.flushInputs();
|
|
112
|
+
this.emit({ lowType: "window-navigate-back", ts: Date.now() });
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
(name, run) => {
|
|
116
|
+
try {
|
|
117
|
+
run();
|
|
118
|
+
} catch (error) {
|
|
119
|
+
this.logger.error(`Failed to observe window ${name}`, error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
);
|
|
63
123
|
}
|
|
64
124
|
inputs = /* @__PURE__ */ new Set();
|
|
65
125
|
inputFocusListeners = /* @__PURE__ */ new Set();
|
|
@@ -86,7 +146,21 @@ var SharedWindowObserver = class {
|
|
|
86
146
|
const registration = { fields, context, flush };
|
|
87
147
|
this.inputs.add(registration);
|
|
88
148
|
if (this.inputFocusListeners.size > 0 && typeof document !== "undefined") {
|
|
89
|
-
const focused =
|
|
149
|
+
const focused = readFocusedChain({
|
|
150
|
+
key: "dom:window.focused-element",
|
|
151
|
+
title: "Which tracked input field has focus",
|
|
152
|
+
accessNote: "document.activeElement and the focus event path, matched against the passed input elements",
|
|
153
|
+
group: "Window interaction",
|
|
154
|
+
origin: "observe-external",
|
|
155
|
+
fields: [
|
|
156
|
+
{
|
|
157
|
+
path: "activeElement",
|
|
158
|
+
label: "Which tracked field is selected",
|
|
159
|
+
exposure: "computed",
|
|
160
|
+
transform: { note: "identity compared against handed-over inputs only" }
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
});
|
|
90
164
|
const field = fields.find((candidate) => focused.includes(candidate));
|
|
91
165
|
if (field) this.notifyInputFocus(field);
|
|
92
166
|
}
|
|
@@ -138,10 +212,31 @@ var SharedWindowObserver = class {
|
|
|
138
212
|
const active = [...this.ceremonies].filter((item) => !item.settled);
|
|
139
213
|
if (active.length === 1) return active[0].context;
|
|
140
214
|
if (active.length > 1) return void 0;
|
|
141
|
-
const
|
|
215
|
+
const focus = readActiveFocus({
|
|
216
|
+
key: "dom:window.focused-element",
|
|
217
|
+
title: "Which tracked input field has focus",
|
|
218
|
+
accessNote: "document.activeElement and the focus event path, matched against the passed input elements",
|
|
219
|
+
group: "Window interaction",
|
|
220
|
+
origin: "observe-external",
|
|
221
|
+
fields: [
|
|
222
|
+
{
|
|
223
|
+
path: "activeElement",
|
|
224
|
+
label: "Which tracked field is selected",
|
|
225
|
+
exposure: "computed",
|
|
226
|
+
transform: { note: "identity compared against handed-over inputs only" }
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
path: "activeElement.idle",
|
|
230
|
+
label: "Whether nothing is selected",
|
|
231
|
+
exposure: "enum",
|
|
232
|
+
transform: { values: [true, false], note: "nothing focused (body or none)" }
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
});
|
|
236
|
+
const input = this.findInput(focus.active);
|
|
142
237
|
if (input) return input.context();
|
|
143
238
|
const previous = this.lastBlur;
|
|
144
|
-
if (previous && Date.now() - previous.ts <= 250 &&
|
|
239
|
+
if (previous && Date.now() - previous.ts <= 250 && focus.idle) {
|
|
145
240
|
return previous.registration.context();
|
|
146
241
|
}
|
|
147
242
|
return void 0;
|
|
@@ -174,17 +269,6 @@ var SharedWindowObserver = class {
|
|
|
174
269
|
sessionId
|
|
175
270
|
);
|
|
176
271
|
}
|
|
177
|
-
listen(target, name, handler) {
|
|
178
|
-
const safe = (event) => {
|
|
179
|
-
try {
|
|
180
|
-
handler(event);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
this.logger.error(`Failed to observe window ${name}`, error);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
target.addEventListener(name, safe);
|
|
186
|
-
this.removers.push(() => target.removeEventListener(name, safe));
|
|
187
|
-
}
|
|
188
272
|
extendViewport(kind) {
|
|
189
273
|
const context = this.context();
|
|
190
274
|
const previous = this.viewportContext[kind];
|
|
@@ -193,19 +277,6 @@ var SharedWindowObserver = class {
|
|
|
193
277
|
this.viewportContext[kind] = context;
|
|
194
278
|
this[kind].extend();
|
|
195
279
|
}
|
|
196
|
-
observeNavigation() {
|
|
197
|
-
const navigation = window.navigation;
|
|
198
|
-
if (!navigation) return;
|
|
199
|
-
this.listen(navigation, "currententrychange", (event) => {
|
|
200
|
-
const change = event;
|
|
201
|
-
const from = change.from?.index;
|
|
202
|
-
const to = navigation.currentEntry?.index;
|
|
203
|
-
if (change.navigationType === "traverse" && typeof from === "number" && typeof to === "number" && from >= 0 && to >= 0 && to < from) {
|
|
204
|
-
this.flushInputs();
|
|
205
|
-
this.emit({ lowType: "window-navigate-back", ts: Date.now() });
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
280
|
destroy() {
|
|
210
281
|
if (this.destroyed) return;
|
|
211
282
|
this.flushInputs();
|
|
@@ -7,7 +7,7 @@ var createViewportBatcher = (pushLow, eventType, reserveCaptureSeq) => {
|
|
|
7
7
|
let timeout = null;
|
|
8
8
|
const flush = () => {
|
|
9
9
|
if (timeout !== null) {
|
|
10
|
-
|
|
10
|
+
clearTimeout(timeout);
|
|
11
11
|
timeout = null;
|
|
12
12
|
}
|
|
13
13
|
const ts = startTimestamp;
|
|
@@ -31,9 +31,9 @@ var createViewportBatcher = (pushLow, eventType, reserveCaptureSeq) => {
|
|
|
31
31
|
captureSeq = sequence;
|
|
32
32
|
}
|
|
33
33
|
if (timeout !== null) {
|
|
34
|
-
|
|
34
|
+
clearTimeout(timeout);
|
|
35
35
|
}
|
|
36
|
-
timeout =
|
|
36
|
+
timeout = setTimeout(flush, VIEWPORT_BATCH_TIMEOUT_MS);
|
|
37
37
|
};
|
|
38
38
|
return { extend, flush };
|
|
39
39
|
};
|