@corbado/observe 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/client.mjs +82 -56
- package/dist/analysis/config-manager.mjs +172 -18
- package/dist/analysis/config.mjs +12 -4
- 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 +151 -26
- package/dist/index.d.ts +151 -26
- 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
|
@@ -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
|
};
|
package/dist/analysis/queue.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { v7 as uuidv7 } from "uuid";
|
|
|
3
3
|
import { AuthEventName } from "./types.mjs";
|
|
4
4
|
import { CONFIG_REQUEST_HEADER, DEFAULT_RELIABILITY_CONFIG } from "./config.mjs";
|
|
5
5
|
import { EventStore } from "./store/event-store.mjs";
|
|
6
|
+
import { listenWindow, readDocumentHidden, readPageUrl } from "./readers/page.mjs";
|
|
6
7
|
var PROBE_QUEUE_MAX_ITEMS = 500;
|
|
7
8
|
var PROBE_QUEUE_MAX_BYTES = 512 * 1024;
|
|
8
9
|
var PROBE_BATCH_MAX_ITEMS = 200;
|
|
@@ -96,6 +97,8 @@ var RequestQueue = class {
|
|
|
96
97
|
dirtySinceSyncFlush = false;
|
|
97
98
|
onVisibilityChange = null;
|
|
98
99
|
onPageHide = null;
|
|
100
|
+
removeVisibilityChange = null;
|
|
101
|
+
removePageHide = null;
|
|
99
102
|
batchSize;
|
|
100
103
|
flushIntervalOverride;
|
|
101
104
|
debug;
|
|
@@ -743,7 +746,21 @@ var RequestQueue = class {
|
|
|
743
746
|
this.onVisibilityChange = () => {
|
|
744
747
|
try {
|
|
745
748
|
this.writeLifecycleDebugOutput("cbo_visibility_change_debug");
|
|
746
|
-
if (
|
|
749
|
+
if (readDocumentHidden({
|
|
750
|
+
key: "page:lifecycle.visibility",
|
|
751
|
+
title: "Page visibility",
|
|
752
|
+
accessNote: "document.visibilityState",
|
|
753
|
+
group: "Page lifecycle",
|
|
754
|
+
origin: "observe-external",
|
|
755
|
+
fields: [
|
|
756
|
+
{
|
|
757
|
+
path: "visibilityState",
|
|
758
|
+
label: "Whether the page is hidden",
|
|
759
|
+
exposure: "enum",
|
|
760
|
+
transform: { values: ["hidden", "other"] }
|
|
761
|
+
}
|
|
762
|
+
]
|
|
763
|
+
}) && this.config.tvc) {
|
|
747
764
|
this.flushSync("visibility-change");
|
|
748
765
|
}
|
|
749
766
|
} catch (error) {
|
|
@@ -760,8 +777,44 @@ var RequestQueue = class {
|
|
|
760
777
|
this.logger.error("Unexpected error in queue flushSync", error);
|
|
761
778
|
}
|
|
762
779
|
};
|
|
763
|
-
|
|
764
|
-
|
|
780
|
+
this.removeVisibilityChange = listenWindow(
|
|
781
|
+
{
|
|
782
|
+
key: "page:lifecycle.visibilitychange",
|
|
783
|
+
title: "Page visibility changes",
|
|
784
|
+
accessNote: "visibilitychange event on document",
|
|
785
|
+
group: "Page lifecycle",
|
|
786
|
+
origin: "observe-external",
|
|
787
|
+
fields: [
|
|
788
|
+
{
|
|
789
|
+
path: "event.visibilitychange",
|
|
790
|
+
label: "Page hidden or shown again",
|
|
791
|
+
exposure: "presence",
|
|
792
|
+
transform: { note: "flushes pending events when the page is hidden" }
|
|
793
|
+
}
|
|
794
|
+
]
|
|
795
|
+
},
|
|
796
|
+
"visibilitychange",
|
|
797
|
+
this.onVisibilityChange
|
|
798
|
+
);
|
|
799
|
+
this.removePageHide = listenWindow(
|
|
800
|
+
{
|
|
801
|
+
key: "page:lifecycle.pagehide",
|
|
802
|
+
title: "Leaving the page",
|
|
803
|
+
accessNote: "pagehide event on window",
|
|
804
|
+
group: "Page lifecycle",
|
|
805
|
+
origin: "observe-external",
|
|
806
|
+
fields: [
|
|
807
|
+
{
|
|
808
|
+
path: "event.pagehide",
|
|
809
|
+
label: "Leaving the page",
|
|
810
|
+
exposure: "presence",
|
|
811
|
+
transform: { note: "flushes pending events on unload" }
|
|
812
|
+
}
|
|
813
|
+
]
|
|
814
|
+
},
|
|
815
|
+
"pagehide",
|
|
816
|
+
this.onPageHide
|
|
817
|
+
);
|
|
765
818
|
}
|
|
766
819
|
writeLifecycleDebugOutput(key) {
|
|
767
820
|
if (!this.debug || !this.storage) {
|
|
@@ -769,7 +822,21 @@ var RequestQueue = class {
|
|
|
769
822
|
}
|
|
770
823
|
const output = {
|
|
771
824
|
last_executed: Date.now(),
|
|
772
|
-
url:
|
|
825
|
+
url: readPageUrl({
|
|
826
|
+
key: "page:location.url",
|
|
827
|
+
title: "Page address (debug output only)",
|
|
828
|
+
accessNote: "location.href, only written to the debug log",
|
|
829
|
+
group: "Page",
|
|
830
|
+
origin: "observe-external",
|
|
831
|
+
fields: [
|
|
832
|
+
{
|
|
833
|
+
path: "url",
|
|
834
|
+
label: "Full page address",
|
|
835
|
+
exposure: "raw",
|
|
836
|
+
transform: { note: "debug mode only; written to own localStorage, never sent" }
|
|
837
|
+
}
|
|
838
|
+
]
|
|
839
|
+
})
|
|
773
840
|
};
|
|
774
841
|
this.storage.setItem(key, output);
|
|
775
842
|
}
|
|
@@ -862,11 +929,11 @@ var RequestQueue = class {
|
|
|
862
929
|
this.clearTimer();
|
|
863
930
|
this.dropProbes(this.probeQueue.slice());
|
|
864
931
|
if (typeof window !== "undefined") {
|
|
865
|
-
if (this.
|
|
866
|
-
|
|
932
|
+
if (this.removeVisibilityChange) {
|
|
933
|
+
this.removeVisibilityChange();
|
|
867
934
|
}
|
|
868
|
-
if (this.
|
|
869
|
-
|
|
935
|
+
if (this.removePageHide) {
|
|
936
|
+
this.removePageHide();
|
|
870
937
|
}
|
|
871
938
|
}
|
|
872
939
|
} catch (error) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/readers/input-paste.ts
|
|
2
2
|
var InputPasteObserver = class {
|
|
3
|
-
constructor(inputs, logger, changed, reserveCaptureSeq) {
|
|
3
|
+
constructor(_meta, inputs, logger, changed, reserveCaptureSeq) {
|
|
4
4
|
this.inputs = inputs;
|
|
5
5
|
this.logger = logger;
|
|
6
6
|
this.changed = changed;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// src/readers/input.ts
|
|
2
|
+
var FIELD_EVENTS = ["focus", "blur", "pointerdown", "pointerup", "click", "input", "keyup"];
|
|
3
|
+
function observeInputField(_meta, field, handlers, guard, order = FIELD_EVENTS) {
|
|
4
|
+
const listeners = [];
|
|
5
|
+
for (const type of order) {
|
|
6
|
+
const listener = reduceFieldEvent(type, handlers, guard);
|
|
7
|
+
if (listener) listeners.push([type, listener]);
|
|
8
|
+
}
|
|
9
|
+
for (const [type, listener] of listeners) field.addEventListener(type, listener);
|
|
10
|
+
return () => {
|
|
11
|
+
for (const [type, listener] of listeners) field.removeEventListener(type, listener);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function reduceFieldEvent(type, handlers, guard) {
|
|
15
|
+
switch (type) {
|
|
16
|
+
case "focus":
|
|
17
|
+
case "blur": {
|
|
18
|
+
const handler = handlers[type];
|
|
19
|
+
return handler && ((event) => guard(type, () => handler(event.relatedTarget)));
|
|
20
|
+
}
|
|
21
|
+
case "pointerdown":
|
|
22
|
+
case "pointerup":
|
|
23
|
+
case "click": {
|
|
24
|
+
const handler = handlers[type];
|
|
25
|
+
return handler && (() => guard(type, () => handler()));
|
|
26
|
+
}
|
|
27
|
+
case "input": {
|
|
28
|
+
const handler = handlers.input;
|
|
29
|
+
return handler && ((event) => guard(
|
|
30
|
+
"input",
|
|
31
|
+
() => handler(
|
|
32
|
+
event.isComposing || event.inputType === "insertCompositionText",
|
|
33
|
+
event
|
|
34
|
+
)
|
|
35
|
+
));
|
|
36
|
+
}
|
|
37
|
+
case "keyup": {
|
|
38
|
+
const handler = handlers.keyupEscape;
|
|
39
|
+
return handler && ((event) => guard("keyup", () => {
|
|
40
|
+
if (event.key === "Escape") handler();
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function readInputContentLength(_meta, field) {
|
|
46
|
+
if (field.type === "checkbox" || field.type === "radio") return field.checked ? 1 : 0;
|
|
47
|
+
return field.value.length;
|
|
48
|
+
}
|
|
49
|
+
function readInputGroupValueLength(_meta, fields) {
|
|
50
|
+
return fields.reduce((total, field) => total + field.value.length, 0);
|
|
51
|
+
}
|
|
52
|
+
function readFocusedField(_meta, fields) {
|
|
53
|
+
const active = document.activeElement;
|
|
54
|
+
return fields.find((field) => field === active);
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
observeInputField,
|
|
58
|
+
readFocusedField,
|
|
59
|
+
readInputContentLength,
|
|
60
|
+
readInputGroupValueLength
|
|
61
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/readers/loader-global.ts
|
|
2
|
+
function drainLoaderQueue(_meta, name, replay) {
|
|
3
|
+
const global = window.Corbado;
|
|
4
|
+
if (global?.[name]) {
|
|
5
|
+
replay(global[name]);
|
|
6
|
+
delete window.Corbado[name];
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function installLoaderGlobal(_meta, api) {
|
|
10
|
+
window.Corbado = api;
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
drainLoaderQueue,
|
|
14
|
+
installLoaderGlobal
|
|
15
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/readers/network.ts
|
|
2
|
+
function sendFetch(_meta, url, init) {
|
|
3
|
+
return fetch(url, init);
|
|
4
|
+
}
|
|
5
|
+
function beaconSender(_meta) {
|
|
6
|
+
if (typeof navigator?.sendBeacon !== "function") {
|
|
7
|
+
return void 0;
|
|
8
|
+
}
|
|
9
|
+
return (url, data) => navigator.sendBeacon(url, data);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
beaconSender,
|
|
13
|
+
sendFetch
|
|
14
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/readers/page.ts
|
|
2
|
+
import { applySourcePathQuery } from "../utils/source-path.mjs";
|
|
3
|
+
function listenWindow(_meta, type, handler, options) {
|
|
4
|
+
if (options === void 0) {
|
|
5
|
+
window.addEventListener(type, handler);
|
|
6
|
+
} else {
|
|
7
|
+
window.addEventListener(type, handler, options);
|
|
8
|
+
}
|
|
9
|
+
return () => window.removeEventListener(type, handler);
|
|
10
|
+
}
|
|
11
|
+
function readDocumentHidden(_meta) {
|
|
12
|
+
return document.visibilityState === "hidden";
|
|
13
|
+
}
|
|
14
|
+
function readDocumentComplete(_meta) {
|
|
15
|
+
return document.readyState === "complete";
|
|
16
|
+
}
|
|
17
|
+
function readPageUrl(_meta) {
|
|
18
|
+
return window.location.href;
|
|
19
|
+
}
|
|
20
|
+
function readSourcePath(_meta, mode, allow) {
|
|
21
|
+
const search = applySourcePathQuery(window.location.search, mode, allow);
|
|
22
|
+
return `${window.location.pathname}${search}`;
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
listenWindow,
|
|
26
|
+
readDocumentComplete,
|
|
27
|
+
readDocumentHidden,
|
|
28
|
+
readPageUrl,
|
|
29
|
+
readSourcePath
|
|
30
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/readers/storage.ts
|
|
2
|
+
function localStorageArea(_meta) {
|
|
3
|
+
return {
|
|
4
|
+
getItem: (key) => localStorage.getItem(key),
|
|
5
|
+
setItem: (key, value) => localStorage.setItem(key, value),
|
|
6
|
+
removeItem: (key) => localStorage.removeItem(key)
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function sessionStorageArea(_meta) {
|
|
10
|
+
return {
|
|
11
|
+
getItem: (key) => sessionStorage.getItem(key),
|
|
12
|
+
setItem: (key, value) => sessionStorage.setItem(key, value),
|
|
13
|
+
removeItem: (key) => sessionStorage.removeItem(key)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function cookieJar(_meta) {
|
|
17
|
+
return {
|
|
18
|
+
read: () => document.cookie,
|
|
19
|
+
write: (cookie) => {
|
|
20
|
+
document.cookie = cookie;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
cookieJar,
|
|
26
|
+
localStorageArea,
|
|
27
|
+
sessionStorageArea
|
|
28
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
function withWebLock(name, fn, options = {}) {
|
|
1
|
+
// src/readers/web-lock.ts
|
|
2
|
+
function withWebLock(_meta, name, fn, options = {}) {
|
|
3
3
|
const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
|
|
4
4
|
if (locks?.request) {
|
|
5
5
|
const timeoutMs = options.timeoutMs;
|
|
@@ -47,6 +47,10 @@ function withWebLock(name, fn, options = {}) {
|
|
|
47
47
|
return Promise.reject(error);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
function hasWebLocks(_meta) {
|
|
51
|
+
return typeof navigator !== "undefined" && !!navigator.locks?.request;
|
|
52
|
+
}
|
|
50
53
|
export {
|
|
54
|
+
hasWebLocks,
|
|
51
55
|
withWebLock
|
|
52
56
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/readers/window-events.ts
|
|
2
|
+
function observeWindowLowEvents(_meta, handlers, guard) {
|
|
3
|
+
const removers = [];
|
|
4
|
+
const listen = (target, name, handler) => {
|
|
5
|
+
const safe = (event) => guard(name, () => handler(event));
|
|
6
|
+
target.addEventListener(name, safe);
|
|
7
|
+
removers.push(() => target.removeEventListener(name, safe));
|
|
8
|
+
};
|
|
9
|
+
listen(window, "blur", () => handlers.blur());
|
|
10
|
+
listen(window, "focus", () => handlers.focus());
|
|
11
|
+
listen(document, "visibilitychange", () => handlers.visibilityChange(document.visibilityState === "hidden"));
|
|
12
|
+
listen(document, "focusout", (event) => handlers.focusOut(event.target, !!event.relatedTarget));
|
|
13
|
+
listen(document, "focusin", (event) => handlers.focusIn(() => event.composedPath()));
|
|
14
|
+
listen(document, "pointerdown", (event) => handlers.pointerDown(event.target));
|
|
15
|
+
if (window.visualViewport) {
|
|
16
|
+
listen(window.visualViewport, "resize", () => handlers.viewportResize());
|
|
17
|
+
listen(window.visualViewport, "scroll", () => handlers.viewportScroll());
|
|
18
|
+
}
|
|
19
|
+
listen(window, "pagehide", () => handlers.pageHide());
|
|
20
|
+
const navigation = window.navigation;
|
|
21
|
+
if (navigation) {
|
|
22
|
+
listen(navigation, "currententrychange", (event) => {
|
|
23
|
+
const change = event;
|
|
24
|
+
const from = change.from?.index;
|
|
25
|
+
const to = navigation.currentEntry?.index;
|
|
26
|
+
if (change.navigationType === "traverse" && typeof from === "number" && typeof to === "number" && from >= 0 && to >= 0 && to < from) {
|
|
27
|
+
handlers.navigateBack();
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return removers;
|
|
32
|
+
}
|
|
33
|
+
function readFocusedChain(_meta) {
|
|
34
|
+
const chain = [];
|
|
35
|
+
for (let active = document.activeElement; active; active = active.shadowRoot?.activeElement ?? null) {
|
|
36
|
+
chain.push(active);
|
|
37
|
+
}
|
|
38
|
+
return chain;
|
|
39
|
+
}
|
|
40
|
+
function readActiveFocus(_meta) {
|
|
41
|
+
const active = document.activeElement;
|
|
42
|
+
return { active, idle: active === document.body || !active };
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
observeWindowLowEvents,
|
|
46
|
+
readActiveFocus,
|
|
47
|
+
readFocusedChain
|
|
48
|
+
};
|