@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.
Files changed (42) hide show
  1. package/dist/analysis/client.mjs +82 -56
  2. package/dist/analysis/config-manager.mjs +30 -5
  3. package/dist/analysis/device-info-manager.mjs +16 -1
  4. package/dist/analysis/device.mjs +88 -10
  5. package/dist/analysis/handoff.mjs +7 -0
  6. package/dist/analysis/low-events/grouped-input-tracker.mjs +148 -33
  7. package/dist/analysis/low-events/input-tracker.mjs +138 -59
  8. package/dist/analysis/low-events/shared-window-observer.mjs +148 -77
  9. package/dist/analysis/low-events/viewport-batcher.mjs +3 -3
  10. package/dist/analysis/queue.mjs +75 -8
  11. package/dist/analysis/{low-events/input-paste-observer.mjs → readers/input-paste.mjs} +2 -2
  12. package/dist/analysis/readers/input.mjs +61 -0
  13. package/dist/analysis/readers/loader-global.mjs +15 -0
  14. package/dist/analysis/readers/network.mjs +14 -0
  15. package/dist/analysis/readers/page.mjs +30 -0
  16. package/dist/analysis/readers/storage.mjs +28 -0
  17. package/dist/analysis/{utils → readers}/web-lock.mjs +6 -2
  18. package/dist/analysis/readers/window-events.mjs +48 -0
  19. package/dist/analysis/storage/index.mjs +61 -9
  20. package/dist/analysis/store/event-store.mjs +34 -16
  21. package/dist/analysis/transports/fetch.mjs +45 -3
  22. package/dist/analysis/utils/email-link-operation-full.mjs +22 -2
  23. package/dist/analysis/utils/email-otp-operation-full.mjs +0 -1
  24. package/dist/analysis/utils/form-operation-full.mjs +0 -1
  25. package/dist/analysis/utils/operation-full-provide-identifier-with-cui.mjs +6 -6
  26. package/dist/analysis/utils/passkey-enrollment-operation-full.mjs +9 -6
  27. package/dist/analysis/utils/passkey-login-operation-full.mjs +10 -5
  28. package/dist/analysis/utils/password-login-operation-full.mjs +8 -4
  29. package/dist/analysis/utils/password-manager-sampler.mjs +44 -4
  30. package/dist/analysis/utils/sms-otp-operation-full.mjs +0 -1
  31. package/dist/analysis/utils/totp-operation-full.mjs +0 -1
  32. package/dist/analysis/version.mjs +1 -1
  33. package/dist/cdn/script.global.js +4 -4
  34. package/dist/index.d.mts +131 -19
  35. package/dist/index.d.ts +131 -19
  36. package/dist/index.js +2 -2
  37. package/dist/index.mjs +2 -2
  38. package/package.json +5 -3
  39. package/tools/reader-meta-text.d.mts +2 -0
  40. package/tools/reader-meta-text.mjs +26 -0
  41. package/tools/strip-reader-meta.d.mts +33 -0
  42. 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 "./input-paste-observer.mjs";
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
- if (field.type === "checkbox" || field.type === "radio") return field.checked ? 1 : 0;
9
- return field.value.length;
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
- input.addEventListener("focus", this.boundHandlers.focus);
93
- input.addEventListener("blur", this.boundHandlers.blur);
94
- input.addEventListener("pointerdown", this.boundHandlers.pointerdown);
95
- input.addEventListener("pointerup", this.boundHandlers.pointerup);
96
- input.addEventListener("click", this.boundHandlers.click);
97
- input.addEventListener("input", this.boundHandlers.input);
98
- input.addEventListener("keyup", this.boundHandlers.keyup);
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
- window.addEventListener("pagehide", this.boundHandlers.pageHide);
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 (document.activeElement === input) {
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
- const input = this.inputField;
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
- window.removeEventListener("pagehide", this.boundHandlers.pageHide);
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.listen(window, "blur", () => {
21
- this.flushInputs();
22
- this.bracket = this.context();
23
- this.emit({ lowType: "window-blur", ts: Date.now() }, this.bracket);
24
- });
25
- this.listen(window, "focus", () => {
26
- this.flushInputs();
27
- this.emit({ lowType: "window-focus", ts: Date.now() }, this.bracket ?? this.context());
28
- this.bracket = void 0;
29
- });
30
- this.listen(document, "visibilitychange", () => {
31
- this.flushInputs();
32
- this.emit({
33
- lowType: `document-visibilitychange-${document.visibilityState === "hidden" ? "hidden" : "visible"}`,
34
- ts: Date.now()
35
- });
36
- });
37
- this.listen(document, "focusout", (event) => {
38
- const registration = this.findInput(event.target);
39
- this.lastBlur = registration && !event.relatedTarget && Date.now() - this.unrelatedPointerAt > 250 ? { registration, ts: Date.now() } : void 0;
40
- });
41
- this.listen(document, "focusin", (event) => {
42
- this.lastBlur = void 0;
43
- const field = this.inputFocusListeners.size > 0 && event.composedPath().find((target) => this.findInput(target));
44
- if (field) this.notifyInputFocus(field);
45
- });
46
- this.listen(document, "pointerdown", (event) => {
47
- if (!this.findInput(event.target)) {
48
- this.lastBlur = void 0;
49
- this.unrelatedPointerAt = Date.now();
50
- } else this.unrelatedPointerAt = -Infinity;
51
- });
52
- if (window.visualViewport) {
53
- this.listen(window.visualViewport, "resize", () => this.extendViewport("resize"));
54
- this.listen(window.visualViewport, "scroll", () => this.extendViewport("scroll"));
55
- }
56
- this.listen(window, "pagehide", () => {
57
- this.flushInputs();
58
- this.resize.flush();
59
- this.scroll.flush();
60
- this.flushKeepalive();
61
- });
62
- this.observeNavigation();
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 = focusedElements();
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 input = this.findInput(document.activeElement);
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 && (document.activeElement === document.body || !document.activeElement)) {
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
- window.clearTimeout(timeout);
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
- window.clearTimeout(timeout);
34
+ clearTimeout(timeout);
35
35
  }
36
- timeout = window.setTimeout(flush, VIEWPORT_BATCH_TIMEOUT_MS);
36
+ timeout = setTimeout(flush, VIEWPORT_BATCH_TIMEOUT_MS);
37
37
  };
38
38
  return { extend, flush };
39
39
  };