@corbado/observe 1.13.0 → 1.14.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/CHANGELOG.md +12 -0
- package/dist/analysis/index.mjs +1 -0
- package/dist/analysis/low-events/grouped-input-tracker.mjs +2 -9
- package/dist/analysis/low-events/input-tracker.mjs +2 -9
- package/dist/analysis/plugins/event-time.mjs +18 -0
- package/dist/analysis/readers/input-paste.mjs +16 -23
- package/dist/analysis/tracker-core.mjs +4 -1
- package/dist/analysis/version.mjs +1 -1
- package/dist/cdn/script.global.js +1 -1
- package/dist/index.d.mts +41 -2
- package/dist/index.d.ts +41 -2
- package/dist/index.js +4 -4
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @corbado/observe
|
|
2
2
|
|
|
3
|
+
## 1.14.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 77ac0d7: Paste observation no longer copies or compares input values: it keeps only the fields' lengths and whether the browser applied the paste. The unused inserted count and the undeclared "Length of the pasted text" reader field are removed. A host-handled paste that replaces text with text of the same length, without an input event, is no longer detected.
|
|
8
|
+
|
|
9
|
+
## 1.14.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 8f563e2: New tracker plugin `eventTime(stamp)` for `initCore`: every tracked event is dated by the integration's clock, `stamp(explicitTimestamp)`, instead of `explicitTimestamp ?? Date.now()`, also for SDK methods that take no timestamp (`flowStarted`, `flowFinished`, decisions, an operation's `subflow_started`). For integrations that replay host signals late (e.g. from a host-side event buffer). A clock that throws or returns a non-finite number falls back to the SDK's own dating; telemetry and low events keep their capture time. `init()` / `CorbadoTracker` are unchanged.
|
|
14
|
+
|
|
3
15
|
## 1.13.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/analysis/index.mjs
CHANGED
|
@@ -12,6 +12,7 @@ export * from "./plugins/cross-origin-session.mjs";
|
|
|
12
12
|
export * from "./plugins/raw-errors.mjs";
|
|
13
13
|
export * from "./plugins/probes.mjs";
|
|
14
14
|
export * from "./plugins/fixed-source-path.mjs";
|
|
15
|
+
export * from "./plugins/event-time.mjs";
|
|
15
16
|
export * from "./probe-lane.mjs";
|
|
16
17
|
export * from "./transport.mjs";
|
|
17
18
|
export * from "./queue.mjs";
|
|
@@ -18,7 +18,7 @@ var LowEventGroupedInputTracker = class {
|
|
|
18
18
|
{
|
|
19
19
|
key: "dom:input.paste",
|
|
20
20
|
title: "Paste into tracked input fields",
|
|
21
|
-
accessNote: "paste events on the input elements the integration passes to Observe;
|
|
21
|
+
accessNote: "paste events on the input elements the integration passes to Observe; only the fields' lengths are compared, values are never copied or compared",
|
|
22
22
|
group: "Input interaction",
|
|
23
23
|
origin: "observe-external",
|
|
24
24
|
fields: [
|
|
@@ -28,19 +28,12 @@ var LowEventGroupedInputTracker = class {
|
|
|
28
28
|
category: "behavior",
|
|
29
29
|
exposure: "presence",
|
|
30
30
|
transform: { note: "trusted paste events only" }
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
path: "value",
|
|
34
|
-
label: "Length of the pasted text",
|
|
35
|
-
category: "behavior",
|
|
36
|
-
exposure: "size",
|
|
37
|
-
transform: { note: "inserted length only; values are compared in memory and discarded" }
|
|
38
31
|
}
|
|
39
32
|
]
|
|
40
33
|
},
|
|
41
34
|
this.inputs,
|
|
42
35
|
logger,
|
|
43
|
-
(ts,
|
|
36
|
+
(ts, captureSeq) => {
|
|
44
37
|
this.cancelChange();
|
|
45
38
|
this.changes.flush();
|
|
46
39
|
this.changes.markInputObserved();
|
|
@@ -40,7 +40,7 @@ var LowEventInputTracker = class {
|
|
|
40
40
|
{
|
|
41
41
|
key: "dom:input.paste",
|
|
42
42
|
title: "Paste into tracked input fields",
|
|
43
|
-
accessNote: "paste events on the input elements the integration passes to Observe;
|
|
43
|
+
accessNote: "paste events on the input elements the integration passes to Observe; only the fields' lengths are compared, values are never copied or compared",
|
|
44
44
|
group: "Input interaction",
|
|
45
45
|
origin: "observe-external",
|
|
46
46
|
fields: [
|
|
@@ -50,19 +50,12 @@ var LowEventInputTracker = class {
|
|
|
50
50
|
category: "behavior",
|
|
51
51
|
exposure: "presence",
|
|
52
52
|
transform: { note: "trusted paste events only" }
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
path: "value",
|
|
56
|
-
label: "Length of the pasted text",
|
|
57
|
-
category: "behavior",
|
|
58
|
-
exposure: "size",
|
|
59
|
-
transform: { note: "inserted length only; values are compared in memory and discarded" }
|
|
60
53
|
}
|
|
61
54
|
]
|
|
62
55
|
},
|
|
63
56
|
[inputField],
|
|
64
57
|
logger,
|
|
65
|
-
(ts,
|
|
58
|
+
(ts, captureSeq) => {
|
|
66
59
|
this.changes.flush();
|
|
67
60
|
this.changes.markInputObserved();
|
|
68
61
|
const oldLength = this.changes.previousLength;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/plugins/event-time.ts
|
|
2
|
+
function eventTime(stamp) {
|
|
3
|
+
if (typeof stamp !== "function") return {};
|
|
4
|
+
return {
|
|
5
|
+
// Guarded here, not in the tracker: the core only pays for the hook call.
|
|
6
|
+
eventTime: (explicitTimestamp) => {
|
|
7
|
+
try {
|
|
8
|
+
const stamped = stamp(explicitTimestamp);
|
|
9
|
+
return typeof stamped === "number" && Number.isFinite(stamped) ? stamped : void 0;
|
|
10
|
+
} catch {
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
eventTime
|
|
18
|
+
};
|
|
@@ -10,7 +10,9 @@ var InputPasteObserver = class {
|
|
|
10
10
|
input.addEventListener("beforeinput", this.onBeforeInput, true);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
lengthsBefore = null;
|
|
14
|
+
/** An input event of this paste arrived (the browser applied it). */
|
|
15
|
+
applied = false;
|
|
14
16
|
timestamp = 0;
|
|
15
17
|
captureSeq;
|
|
16
18
|
pasteEvent;
|
|
@@ -22,21 +24,23 @@ var InputPasteObserver = class {
|
|
|
22
24
|
};
|
|
23
25
|
/** An unrelated input event invalidates pending paste provenance before recording its change. */
|
|
24
26
|
handlesInput(event) {
|
|
25
|
-
if (!this.
|
|
27
|
+
if (!this.lengthsBefore) return false;
|
|
26
28
|
const inPasteDispatch = this.pasteEvent?.type === "paste" && (this.pasteEvent.eventPhase ?? 0) > 0;
|
|
27
29
|
if (event.inputType === "insertFromPaste" || inPasteDispatch) {
|
|
30
|
+
this.applied = true;
|
|
28
31
|
return true;
|
|
29
32
|
}
|
|
30
33
|
this.clear();
|
|
31
34
|
return false;
|
|
32
35
|
}
|
|
33
36
|
get pending() {
|
|
34
|
-
return this.
|
|
37
|
+
return this.lengthsBefore !== null;
|
|
35
38
|
}
|
|
36
39
|
begin(event) {
|
|
37
|
-
if (!event.isTrusted || this.
|
|
40
|
+
if (!event.isTrusted || this.lengthsBefore) return;
|
|
38
41
|
try {
|
|
39
|
-
this.
|
|
42
|
+
this.lengthsBefore = this.inputs.map((input) => input.value.length);
|
|
43
|
+
this.applied = false;
|
|
40
44
|
this.pasteEvent = event;
|
|
41
45
|
this.timestamp = Date.now();
|
|
42
46
|
this.captureSeq = this.reserveCaptureSeq?.();
|
|
@@ -47,27 +51,15 @@ var InputPasteObserver = class {
|
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
flush() {
|
|
50
|
-
const
|
|
51
|
-
if (!
|
|
54
|
+
const lengthsBefore = this.lengthsBefore;
|
|
55
|
+
if (!lengthsBefore) return;
|
|
52
56
|
const timestamp = this.timestamp;
|
|
53
57
|
const captureSeq = this.captureSeq;
|
|
58
|
+
const applied = this.applied;
|
|
54
59
|
this.clear();
|
|
55
60
|
try {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
this.inputs.forEach((input, index) => {
|
|
59
|
-
const old = before[index];
|
|
60
|
-
const value = input.value;
|
|
61
|
-
if (old === value) return;
|
|
62
|
-
changed = true;
|
|
63
|
-
let start = 0;
|
|
64
|
-
while (start < old.length && start < value.length && old[start] === value[start]) start++;
|
|
65
|
-
let end = 0;
|
|
66
|
-
while (end < old.length - start && end < value.length - start && old[old.length - end - 1] === value[value.length - end - 1])
|
|
67
|
-
end++;
|
|
68
|
-
inserted += value.length - start - end;
|
|
69
|
-
});
|
|
70
|
-
if (changed) this.changed(timestamp, inserted, captureSeq);
|
|
61
|
+
const changed = applied || this.inputs.some((input, index) => input.value.length !== lengthsBefore[index]);
|
|
62
|
+
if (changed) this.changed(timestamp, captureSeq);
|
|
71
63
|
} catch (error) {
|
|
72
64
|
this.logger.error("Failed to finish paste observation", error);
|
|
73
65
|
}
|
|
@@ -75,7 +67,8 @@ var InputPasteObserver = class {
|
|
|
75
67
|
clear() {
|
|
76
68
|
if (this.timer !== null) clearTimeout(this.timer);
|
|
77
69
|
this.timer = null;
|
|
78
|
-
this.
|
|
70
|
+
this.lengthsBefore = null;
|
|
71
|
+
this.applied = false;
|
|
79
72
|
this.pasteEvent = void 0;
|
|
80
73
|
}
|
|
81
74
|
destroy() {
|
|
@@ -133,6 +133,7 @@ var CorbadoTrackerCore = class _CorbadoTrackerCore {
|
|
|
133
133
|
if (plugin.session) this.bridge = plugin.session(this) || this.bridge;
|
|
134
134
|
if (plugin.rawErrors) this.rawErrors = plugin.rawErrors;
|
|
135
135
|
if (typeof plugin.sourcePath === "function") this.fixedSourcePath = plugin.sourcePath;
|
|
136
|
+
if (plugin.eventTime) this.eventClock = plugin.eventTime;
|
|
136
137
|
});
|
|
137
138
|
if (options.fixedDataConfig?.crossOriginSession && !this.bridge) {
|
|
138
139
|
this.logger.warn("fixedDataConfig.crossOriginSession is ignored without the crossOriginSession() plugin");
|
|
@@ -273,6 +274,8 @@ var CorbadoTrackerCore = class _CorbadoTrackerCore {
|
|
|
273
274
|
rawErrors;
|
|
274
275
|
/** Fixed source path of a plugin; undefined = the server-driven `sourcePathQuery` policy. */
|
|
275
276
|
fixedSourcePath;
|
|
277
|
+
/** Event clock of a plugin (never throws); undefined or an undefined result = `explicitTimestamp ?? Date.now()`. */
|
|
278
|
+
eventClock;
|
|
276
279
|
/** @internal Shared passive page observation and per-operation context registration. */
|
|
277
280
|
getWindowObserver() {
|
|
278
281
|
return this.windowObserver ??= new SharedWindowObserver(
|
|
@@ -891,7 +894,7 @@ var CorbadoTrackerCore = class _CorbadoTrackerCore {
|
|
|
891
894
|
if (name === AuthEventName.FlowFinished || name === AuthEventName.FlowAutoFinished || name === AuthEventName.FlowReset) {
|
|
892
895
|
this.windowObserver?.flushInputs();
|
|
893
896
|
}
|
|
894
|
-
const timestamp = explicitTimestamp ?? Date.now();
|
|
897
|
+
const timestamp = this.eventClock?.(explicitTimestamp) ?? explicitTimestamp ?? Date.now();
|
|
895
898
|
const capturedId = capture?.id ?? uuidv7();
|
|
896
899
|
const capturedSessionId = capture?.sessionId ?? this.sessionId;
|
|
897
900
|
const nextSeq = this.nextSeq();
|