@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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { v7 as uuidv7 } from "uuid";
|
|
3
3
|
import { OperationFull } from "./operation-full.mjs";
|
|
4
4
|
import { findEmailLinkStart, markEmailLinkContinued, rememberEmailLinkStart } from "./email-link-continuity.mjs";
|
|
5
|
+
import { listenWindow } from "../readers/page.mjs";
|
|
5
6
|
var EmailLinkOperationFull = class extends OperationFull {
|
|
6
7
|
send;
|
|
7
8
|
postResponse;
|
|
@@ -42,6 +43,7 @@ var EmailLinkContinuationOperation = class extends OperationFull {
|
|
|
42
43
|
started = false;
|
|
43
44
|
deferred = [];
|
|
44
45
|
onPageHide = () => this.flush();
|
|
46
|
+
removePageHide;
|
|
45
47
|
constructor(tracker, config = {}) {
|
|
46
48
|
super(tracker, "email-link");
|
|
47
49
|
this.continuationTracker = tracker;
|
|
@@ -65,7 +67,25 @@ var EmailLinkContinuationOperation = class extends OperationFull {
|
|
|
65
67
|
if (this.transaction) {
|
|
66
68
|
this.startDetached(this.createdAt);
|
|
67
69
|
} else if (typeof window !== "undefined") {
|
|
68
|
-
|
|
70
|
+
this.removePageHide = listenWindow(
|
|
71
|
+
{
|
|
72
|
+
key: "page:lifecycle.pagehide",
|
|
73
|
+
title: "Leaving the page",
|
|
74
|
+
accessNote: "pagehide event on window",
|
|
75
|
+
group: "Page lifecycle",
|
|
76
|
+
origin: "observe-external",
|
|
77
|
+
fields: [
|
|
78
|
+
{
|
|
79
|
+
path: "event.pagehide",
|
|
80
|
+
label: "Leaving the page",
|
|
81
|
+
exposure: "presence",
|
|
82
|
+
transform: { note: "flushes a detached email-link continuation on unload" }
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
"pagehide",
|
|
87
|
+
this.onPageHide
|
|
88
|
+
);
|
|
69
89
|
}
|
|
70
90
|
}
|
|
71
91
|
/** Transaction ID of the continued email link, when known. */
|
|
@@ -113,7 +133,7 @@ var EmailLinkContinuationOperation = class extends OperationFull {
|
|
|
113
133
|
}
|
|
114
134
|
}
|
|
115
135
|
removePageHideListener() {
|
|
116
|
-
if (typeof window !== "undefined")
|
|
136
|
+
if (typeof window !== "undefined") this.removePageHide?.();
|
|
117
137
|
}
|
|
118
138
|
/** Defer step calls until the detached start is emitted; mark the continued link on completion. */
|
|
119
139
|
deferrable(step, completesLink = false) {
|
|
@@ -26,7 +26,6 @@ var EmailOtpOperationFull = class extends OperationFull {
|
|
|
26
26
|
}
|
|
27
27
|
this.registerLowFlusher(() => this.lowEventTracker?.flushPending());
|
|
28
28
|
}
|
|
29
|
-
/** Rebind the ordered OTP group without starting a new subflow. An empty list detaches it. */
|
|
30
29
|
setInputFields(fields) {
|
|
31
30
|
this.lowEventTracker?.setFields(fields);
|
|
32
31
|
}
|
|
@@ -22,7 +22,6 @@ var FormOperationFull = class extends OperationFull {
|
|
|
22
22
|
this.registerLowFlusher(() => this.inputBinding?.flushPending());
|
|
23
23
|
if (config?.inputHtmlFields?.length) this.setInputFields(config.inputHtmlFields);
|
|
24
24
|
}
|
|
25
|
-
/** Bind the fields the form shows now, without starting a new subflow. An empty list detaches all. */
|
|
26
25
|
setInputFields(fields) {
|
|
27
26
|
this.inputBinding?.setFields(fields);
|
|
28
27
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/utils/operation-full-provide-identifier-with-cui.ts
|
|
2
2
|
import { LowEventInputTracker } from "../low-events/input-tracker.mjs";
|
|
3
3
|
import { OperationFull } from "./operation-full.mjs";
|
|
4
|
+
import { acceptHandoffs } from "../handoff.mjs";
|
|
4
5
|
var OperationFullProvideIdentifierWithCUI = class extends OperationFull {
|
|
5
6
|
lowEventTracker;
|
|
6
7
|
cui;
|
|
@@ -12,14 +13,13 @@ var OperationFullProvideIdentifierWithCUI = class extends OperationFull {
|
|
|
12
13
|
this.trigger(data, options);
|
|
13
14
|
},
|
|
14
15
|
// getOptions only fetches assertion options; it is not a user interaction on its own.
|
|
15
|
-
getOptions:
|
|
16
|
-
"cui-get-options",
|
|
17
|
-
true
|
|
16
|
+
getOptions: acceptHandoffs(
|
|
17
|
+
this.defineStep("cui-get-options", true)
|
|
18
18
|
),
|
|
19
|
-
ceremony:
|
|
20
|
-
"cui-ceremony"
|
|
19
|
+
ceremony: acceptHandoffs(
|
|
20
|
+
this.defineStep("cui-ceremony")
|
|
21
21
|
),
|
|
22
|
-
postResponse: this.defineStep("cui-post-response")
|
|
22
|
+
postResponse: acceptHandoffs(this.defineStep("cui-post-response"))
|
|
23
23
|
};
|
|
24
24
|
this.provideIdentifier = {
|
|
25
25
|
trigger: (data, options) => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/utils/passkey-enrollment-operation-full.ts
|
|
2
2
|
import { LowEventWindowTracker } from "../low-events/window-tracker.mjs";
|
|
3
3
|
import { OperationFull } from "./operation-full.mjs";
|
|
4
|
+
import { acceptHandoffs } from "../handoff.mjs";
|
|
4
5
|
var PasskeyEnrollmentOperationFull = class extends OperationFull {
|
|
5
6
|
getOptions;
|
|
6
7
|
ceremony;
|
|
@@ -10,14 +11,16 @@ var PasskeyEnrollmentOperationFull = class extends OperationFull {
|
|
|
10
11
|
super(tracker, "passkey-enrollment");
|
|
11
12
|
this.lowEventTracker = new LowEventWindowTracker(tracker.getLogger(), this.lowEventSink("ceremony"));
|
|
12
13
|
this.registerLowFlusher(() => this.lowEventTracker.disarm());
|
|
13
|
-
this.getOptions =
|
|
14
|
-
"get-options"
|
|
14
|
+
this.getOptions = acceptHandoffs(
|
|
15
|
+
this.defineStep("get-options")
|
|
15
16
|
);
|
|
16
|
-
this.ceremony =
|
|
17
|
-
this.
|
|
18
|
-
|
|
17
|
+
this.ceremony = acceptHandoffs(
|
|
18
|
+
this.withWindowLowEvents(
|
|
19
|
+
this.defineStep("ceremony"),
|
|
20
|
+
this.lowEventTracker
|
|
21
|
+
)
|
|
19
22
|
);
|
|
20
|
-
this.postResponse = this.defineStep("post-response");
|
|
23
|
+
this.postResponse = acceptHandoffs(this.defineStep("post-response"));
|
|
21
24
|
void this.subflowStart({
|
|
22
25
|
explicitSpecType: config?.explicitSpecType,
|
|
23
26
|
...config?.ignoreAsInteraction ? { ignoreAsInteraction: true } : {}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/utils/passkey-login-operation-full.ts
|
|
2
2
|
import { LowEventWindowTracker } from "../low-events/window-tracker.mjs";
|
|
3
3
|
import { OperationFull } from "./operation-full.mjs";
|
|
4
|
+
import { acceptHandoffs } from "../handoff.mjs";
|
|
4
5
|
var PasskeyLoginOperationFull = class extends OperationFull {
|
|
5
6
|
getOptions;
|
|
6
7
|
ceremony;
|
|
@@ -10,12 +11,16 @@ var PasskeyLoginOperationFull = class extends OperationFull {
|
|
|
10
11
|
super(tracker, "passkey-login");
|
|
11
12
|
this.lowEventTracker = new LowEventWindowTracker(tracker.getLogger(), this.lowEventSink("ceremony"));
|
|
12
13
|
this.registerLowFlusher(() => this.lowEventTracker.disarm());
|
|
13
|
-
this.getOptions =
|
|
14
|
-
|
|
15
|
-
this.defineStep("ceremony"),
|
|
16
|
-
this.lowEventTracker
|
|
14
|
+
this.getOptions = acceptHandoffs(
|
|
15
|
+
this.defineStep("get-options")
|
|
17
16
|
);
|
|
18
|
-
this.
|
|
17
|
+
this.ceremony = acceptHandoffs(
|
|
18
|
+
this.withWindowLowEvents(
|
|
19
|
+
this.defineStep("ceremony"),
|
|
20
|
+
this.lowEventTracker
|
|
21
|
+
)
|
|
22
|
+
);
|
|
23
|
+
this.postResponse = acceptHandoffs(this.defineStep("post-response"));
|
|
19
24
|
void this.subflowStart({ explicitSpecType: config?.explicitSpecType });
|
|
20
25
|
}
|
|
21
26
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/utils/password-login-operation-full.ts
|
|
2
2
|
import { LowEventInputTracker } from "../low-events/input-tracker.mjs";
|
|
3
3
|
import { OperationFull } from "./operation-full.mjs";
|
|
4
|
+
import { acceptHandoffs } from "../handoff.mjs";
|
|
4
5
|
var PasswordLoginOperationFull = class extends OperationFull {
|
|
5
6
|
clientValidation;
|
|
6
7
|
postResponse;
|
|
@@ -18,11 +19,14 @@ var PasswordLoginOperationFull = class extends OperationFull {
|
|
|
18
19
|
this.postResponse = this.defineStep("post-response");
|
|
19
20
|
this.cui = {
|
|
20
21
|
// getOptions only fetches assertion options; it is not a user interaction on its own.
|
|
21
|
-
getOptions:
|
|
22
|
-
"cui-get-options",
|
|
23
|
-
|
|
22
|
+
getOptions: acceptHandoffs(
|
|
23
|
+
this.defineStep("cui-get-options", true)
|
|
24
|
+
),
|
|
25
|
+
ceremony: acceptHandoffs(
|
|
26
|
+
this.defineStep(
|
|
27
|
+
"cui-ceremony"
|
|
28
|
+
)
|
|
24
29
|
),
|
|
25
|
-
ceremony: this.defineStep("cui-ceremony"),
|
|
26
30
|
postResponse: this.defineStep("cui-post-response")
|
|
27
31
|
};
|
|
28
32
|
void this.subflowStart({ explicitSpecType: config?.explicitSpecType });
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/utils/password-manager-sampler.ts
|
|
2
|
+
import { listenWindow, readDocumentComplete } from "../readers/page.mjs";
|
|
2
3
|
import {
|
|
3
4
|
readPasswordManagerExtensions
|
|
4
5
|
} from "../readers/password-manager-extensions.mjs";
|
|
@@ -12,15 +13,49 @@ var PasswordManagerSampler = class {
|
|
|
12
13
|
this.isEnabled = isEnabled;
|
|
13
14
|
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
14
15
|
this.sample();
|
|
15
|
-
if (
|
|
16
|
+
if (readDocumentComplete({
|
|
17
|
+
key: "page:lifecycle.ready-state",
|
|
18
|
+
title: "Page load state",
|
|
19
|
+
accessNote: "document.readyState",
|
|
20
|
+
group: "Page lifecycle",
|
|
21
|
+
origin: "observe-external",
|
|
22
|
+
fields: [
|
|
23
|
+
{
|
|
24
|
+
path: "readyState",
|
|
25
|
+
label: "Whether the page has finished loading",
|
|
26
|
+
exposure: "enum",
|
|
27
|
+
transform: { values: ["complete", "other"] }
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
})) {
|
|
16
31
|
this.sampleAfter(LOAD_RESAMPLE_DELAY_MS);
|
|
17
32
|
} else {
|
|
18
33
|
const onLoad = () => {
|
|
19
34
|
this.sampleWhenIdle();
|
|
20
35
|
this.sampleAfter(LOAD_RESAMPLE_DELAY_MS);
|
|
21
36
|
};
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
this.removers.push(
|
|
38
|
+
listenWindow(
|
|
39
|
+
{
|
|
40
|
+
key: "page:lifecycle.load",
|
|
41
|
+
title: "Page load",
|
|
42
|
+
accessNote: "load event on window",
|
|
43
|
+
group: "Page lifecycle",
|
|
44
|
+
origin: "observe-external",
|
|
45
|
+
fields: [
|
|
46
|
+
{
|
|
47
|
+
path: "event.load",
|
|
48
|
+
label: "Page finished loading",
|
|
49
|
+
exposure: "presence",
|
|
50
|
+
transform: { note: "schedules a password manager sample" }
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"load",
|
|
55
|
+
onLoad,
|
|
56
|
+
{ once: true }
|
|
57
|
+
)
|
|
58
|
+
);
|
|
24
59
|
}
|
|
25
60
|
const removeFocus = windowObserver.onInputFocus((field) => {
|
|
26
61
|
if (!this.isEnabled() || this.sampledFields.has(field)) return;
|
|
@@ -89,9 +124,14 @@ var PasswordManagerSampler = class {
|
|
|
89
124
|
try {
|
|
90
125
|
const found = readPasswordManagerExtensions({
|
|
91
126
|
key: "browser-api:password-managers.extensions",
|
|
127
|
+
title: "Installed password manager extensions",
|
|
128
|
+
accessNote: "globals and DOM elements that known password manager extensions inject",
|
|
129
|
+
group: "Password manager detection",
|
|
130
|
+
origin: "observe-external",
|
|
92
131
|
fields: [
|
|
93
132
|
{
|
|
94
133
|
path: "result[].name",
|
|
134
|
+
label: "Names of known password managers found",
|
|
95
135
|
exposure: "enum",
|
|
96
136
|
transform: {
|
|
97
137
|
values: [
|
|
@@ -107,7 +147,7 @@ var PasswordManagerSampler = class {
|
|
|
107
147
|
}
|
|
108
148
|
},
|
|
109
149
|
// Read to merge samples, never reported (takeReport keeps only the name).
|
|
110
|
-
{ path: "result[].signedIn", exposure: "none" }
|
|
150
|
+
{ path: "result[].signedIn", label: "Whether the password manager is signed in", exposure: "none" }
|
|
111
151
|
],
|
|
112
152
|
dataConfig: "pmd"
|
|
113
153
|
});
|
|
@@ -26,7 +26,6 @@ var SmsOtpOperationFull = class extends OperationFull {
|
|
|
26
26
|
}
|
|
27
27
|
this.registerLowFlusher(() => this.lowEventTracker?.flushPending());
|
|
28
28
|
}
|
|
29
|
-
/** Rebind the ordered OTP group without starting a new subflow. An empty list detaches it. */
|
|
30
29
|
setInputFields(fields) {
|
|
31
30
|
this.lowEventTracker?.setFields(fields);
|
|
32
31
|
}
|
|
@@ -22,7 +22,6 @@ var TotpOperationFull = class extends OperationFull {
|
|
|
22
22
|
}
|
|
23
23
|
this.registerLowFlusher(() => this.lowEventTracker?.flushPending());
|
|
24
24
|
}
|
|
25
|
-
/** Rebind the ordered code input group without starting a new subflow. An empty list detaches it. */
|
|
26
25
|
setInputFields(fields) {
|
|
27
26
|
this.lowEventTracker?.setFields(fields);
|
|
28
27
|
}
|