@openreplay/tracker 17.2.9 → 17.2.11
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/cjs/entry.js +678 -117
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +615 -100
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/app/canvas.d.ts +6 -0
- package/dist/cjs/main/app/index.d.ts +67 -1
- package/dist/cjs/main/app/observer/observer.d.ts +10 -0
- package/dist/cjs/main/app/sanitizer.d.ts +5 -3
- package/dist/cjs/main/index.d.ts +17 -2
- package/dist/cjs/main/singleton.d.ts +25 -1
- package/dist/lib/entry.js +678 -117
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +615 -100
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/app/canvas.d.ts +6 -0
- package/dist/lib/main/app/index.d.ts +67 -1
- package/dist/lib/main/app/observer/observer.d.ts +10 -0
- package/dist/lib/main/app/sanitizer.d.ts +5 -3
- package/dist/lib/main/index.d.ts +17 -2
- package/dist/lib/main/singleton.d.ts +25 -1
- package/dist/types/main/app/canvas.d.ts +6 -0
- package/dist/types/main/app/index.d.ts +67 -1
- package/dist/types/main/app/observer/observer.d.ts +10 -0
- package/dist/types/main/app/sanitizer.d.ts +5 -3
- package/dist/types/main/index.d.ts +17 -2
- package/dist/types/main/singleton.d.ts +25 -1
- package/package.json +1 -1
|
@@ -24,6 +24,12 @@ declare class CanvasRecorder {
|
|
|
24
24
|
private isProcessingQueue;
|
|
25
25
|
constructor(app: App, options: Options);
|
|
26
26
|
startTracking(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Reacts to a runtime sanitization change on a canvas: stop capturing if it
|
|
29
|
+
* just became masked, start if it just became visible. (Already-sent frames
|
|
30
|
+
* can't be retracted — escalation only stops future capture.)
|
|
31
|
+
*/
|
|
32
|
+
resanitizeCanvas: (node: Node, id: number) => void;
|
|
27
33
|
restartTracking: () => void;
|
|
28
34
|
captureCanvas: (node: Node) => void;
|
|
29
35
|
recordCanvas: (node: Node, id: number) => void;
|
|
@@ -8,7 +8,7 @@ import Nodes from './nodes/index.js';
|
|
|
8
8
|
import type { Options as ObserverOptions } from './observer/top_observer.js';
|
|
9
9
|
import Observer from './observer/top_observer.js';
|
|
10
10
|
import type { Options as SanitizerOptions } from './sanitizer.js';
|
|
11
|
-
import Sanitizer from './sanitizer.js';
|
|
11
|
+
import Sanitizer, { SanitizeLevel } from './sanitizer.js';
|
|
12
12
|
import type { Options as SessOptions } from './session.js';
|
|
13
13
|
import Session from './session.js';
|
|
14
14
|
import Ticker from './ticker.js';
|
|
@@ -129,6 +129,7 @@ export default class App {
|
|
|
129
129
|
readonly ticker: Ticker;
|
|
130
130
|
readonly projectKey: string;
|
|
131
131
|
readonly sanitizer: Sanitizer;
|
|
132
|
+
private readonly resanitizeCallbacks;
|
|
132
133
|
readonly debug: Logger;
|
|
133
134
|
readonly notify: Logger;
|
|
134
135
|
readonly session: Session;
|
|
@@ -170,11 +171,72 @@ export default class App {
|
|
|
170
171
|
/** used by child iframes for crossdomain only */
|
|
171
172
|
parentActive: boolean;
|
|
172
173
|
checkStatus: () => boolean;
|
|
174
|
+
/** child-side crossdomain debug state (only meaningful when insideIframe) */
|
|
175
|
+
private lastTokenReceived;
|
|
176
|
+
private lastParentMsgAt;
|
|
177
|
+
private lastSentToParentAt;
|
|
178
|
+
private iframeDebugInterval;
|
|
179
|
+
/** stamp every outbound post to the parent window, for the child debug snapshot */
|
|
180
|
+
private markSentToParent;
|
|
181
|
+
/**
|
|
182
|
+
* Child-side counterpart of emitCrossdomainDebug: once per minute an iframe posts an
|
|
183
|
+
* encoded snapshot of its own tracking state up to the parent, which records it as a
|
|
184
|
+
* console log. Posted directly (not via this.send) so it is reported even when the
|
|
185
|
+
* child is NOT active — an inactive/orphaned child is exactly what we want to catch.
|
|
186
|
+
*/
|
|
187
|
+
private emitIframeDebug;
|
|
173
188
|
parentCrossDomainFrameListener: (event: MessageEvent) => void;
|
|
174
189
|
trackedFrames: string[];
|
|
190
|
+
/** every context that has been enrolled at least once, to tell an orphan (re-adopt) apart
|
|
191
|
+
* from a brand-new child still mid-enrollment (leave alone). */
|
|
192
|
+
private everTrackedFrames;
|
|
175
193
|
private frameLastSeen;
|
|
194
|
+
/** crossdomain debug diagnostics, reported once per minute as an encoded console log */
|
|
195
|
+
private frameOrigin;
|
|
196
|
+
private frameAnyLastSeen;
|
|
197
|
+
private frameBatchLastSeen;
|
|
198
|
+
private frameLastSent;
|
|
199
|
+
private xdomainDebugInterval;
|
|
200
|
+
/** last time we re-adopted a given orphaned context, to avoid restart spam */
|
|
201
|
+
private reAdoptCooldown;
|
|
202
|
+
private readonly RE_ADOPT_COOLDOWN_MS;
|
|
203
|
+
/**
|
|
204
|
+
* Stable, collision-free frame-order allocation. Node ids are partitioned by
|
|
205
|
+
* (frameLevel, frameOrder) via pack() — every (level, order) owns its own id block, so
|
|
206
|
+
* two simultaneously-live frames sharing an order at the same level corrupt each other's
|
|
207
|
+
* node trees and one stops rendering. The previous `trackedFrames.findIndex+1` derived
|
|
208
|
+
* order from a mutable array index, and pruneStaleFrames()'s .filter() shifts those
|
|
209
|
+
* indices, so a newly enrolled frame could be handed an order still in use by a live
|
|
210
|
+
* (but pruned) frame. We instead assign each context a persistent order, unique among all
|
|
211
|
+
* non-recycled contexts at its level, freed only when the context is GC'd (truly gone).
|
|
212
|
+
*/
|
|
213
|
+
private frameAlloc;
|
|
214
|
+
private usedOrdersByLevel;
|
|
215
|
+
private allocateFrameOrder;
|
|
216
|
+
private freeFrameOrder;
|
|
176
217
|
private readonly FRAME_STALE_MS;
|
|
177
218
|
private pruneStaleFrames;
|
|
219
|
+
/** records the last command/signal we posted to a given child iframe context (debug) */
|
|
220
|
+
private recordSentToFrame;
|
|
221
|
+
/**
|
|
222
|
+
* Self-heal for the "kill-then-prune orphan" race: a live child can fall out of
|
|
223
|
+
* `trackedFrames` (its 250ms poll was delayed past FRAME_STALE_MS during the parent's
|
|
224
|
+
* stop/start NotActive gap, so pruneStaleFrames evicted it). It keeps polling but the
|
|
225
|
+
* only re-enrollment path is an `iframeSignal`, which a stopped/active-but-orphaned
|
|
226
|
+
* child never re-emits — so it would record nothing forever. When we (the parent) are
|
|
227
|
+
* active and see a poll from an un-tracked context, push a `startIframe` so the child
|
|
228
|
+
* restarts, re-runs the full handshake and re-observes with a fresh rootId. Cooldowned
|
|
229
|
+
* so we don't spam restarts during the child's start window.
|
|
230
|
+
*/
|
|
231
|
+
private reAdoptOrphanFrame;
|
|
232
|
+
/**
|
|
233
|
+
* Once per minute: emit an encoded console log from the parent tracker describing every
|
|
234
|
+
* tracked child iframe and the freshness of our two-way communication with it. Lets us
|
|
235
|
+
* see in replay which crossdomain iframe went silent and on which leg of the handshake.
|
|
236
|
+
*/
|
|
237
|
+
/** drop debug entries for contexts we have neither heard from nor messaged in this long */
|
|
238
|
+
private readonly XDOMAIN_DEBUG_RETENTION_MS;
|
|
239
|
+
private emitCrossdomainDebug;
|
|
178
240
|
crossDomainIframeListener: (event: MessageEvent) => void;
|
|
179
241
|
/**
|
|
180
242
|
* { command : [remaining iframes] }
|
|
@@ -292,6 +354,10 @@ export default class App {
|
|
|
292
354
|
private userStartCallback?;
|
|
293
355
|
private _start;
|
|
294
356
|
restartCanvasTracking: () => void;
|
|
357
|
+
attachResanitizeCallback: (cb: (node: Node, id: number) => void) => void;
|
|
358
|
+
callResanitizeCallbacks: (node: Node, id: number) => void;
|
|
359
|
+
resanitize: (el?: Element) => void;
|
|
360
|
+
checkSanitization: (el: Node) => SanitizeLevel | undefined;
|
|
295
361
|
flushBuffer: (buffer: Message[]) => Promise<unknown>;
|
|
296
362
|
waitStart(): Promise<unknown>;
|
|
297
363
|
waitStarted(): Promise<unknown>;
|
|
@@ -53,6 +53,16 @@ export default abstract class Observer {
|
|
|
53
53
|
private commitNode;
|
|
54
54
|
private commitNodes;
|
|
55
55
|
protected observeRoot(node: Node, beforeCommit: (id?: number) => unknown, nodeToBind?: Node): void;
|
|
56
|
+
/**
|
|
57
|
+
* Re-evaluates sanitization for every tracked node in `root`'s subtree against
|
|
58
|
+
* the current DOM and re-emits whatever changed. Pass the highest node you
|
|
59
|
+
* changed (or the document root) so inherited levels propagate correctly.
|
|
60
|
+
*/
|
|
61
|
+
resanitizeSubtree(root: Node): void;
|
|
62
|
+
private resanitizeNode;
|
|
63
|
+
private recreateSubtree;
|
|
64
|
+
private clearSubtreeRegistration;
|
|
65
|
+
private reemitNode;
|
|
56
66
|
disconnect(): void;
|
|
57
67
|
}
|
|
58
68
|
export {};
|
|
@@ -35,8 +35,7 @@ export interface Options {
|
|
|
35
35
|
}
|
|
36
36
|
export declare const stringWiper: (input: string) => string;
|
|
37
37
|
export default class Sanitizer {
|
|
38
|
-
private readonly
|
|
39
|
-
private readonly hidden;
|
|
38
|
+
private readonly levels;
|
|
40
39
|
private readonly options;
|
|
41
40
|
readonly privateMode: boolean;
|
|
42
41
|
private readonly app;
|
|
@@ -44,7 +43,10 @@ export default class Sanitizer {
|
|
|
44
43
|
app: App;
|
|
45
44
|
options?: Partial<Options>;
|
|
46
45
|
});
|
|
47
|
-
|
|
46
|
+
computeLevel(node: Node, parentLevel: SanitizeLevel): SanitizeLevel;
|
|
47
|
+
getLevel(id: number): SanitizeLevel;
|
|
48
|
+
setLevel(id: number, level: SanitizeLevel): SanitizeLevel;
|
|
49
|
+
handleNode(id: number, parentID: number, node: Node): void;
|
|
48
50
|
sanitize(id: number, data: string): string;
|
|
49
51
|
isObscured(id: number): boolean;
|
|
50
52
|
isHidden(id: number): boolean;
|
package/dist/cjs/main/index.d.ts
CHANGED
|
@@ -47,6 +47,21 @@ export default class API {
|
|
|
47
47
|
checkDoNotTrack: () => boolean | undefined;
|
|
48
48
|
signalStartIssue: (reason: string, missingApi: string[]) => void;
|
|
49
49
|
restartCanvasTracking: () => void;
|
|
50
|
+
/**
|
|
51
|
+
* Re-evaluates sanitization against the current DOM and re-emits whatever
|
|
52
|
+
* changed, updating already-recorded nodes mid-session. Call after toggling
|
|
53
|
+
* `data-openreplay-*` attributes or after changing whatever your `domSanitizer`
|
|
54
|
+
* keys on (class/id/etc).
|
|
55
|
+
*
|
|
56
|
+
* @param el - the highest node you changed; omit to re-scan the whole document;
|
|
57
|
+
* scanning the entire doc is O(dom size)
|
|
58
|
+
* */
|
|
59
|
+
resanitize: (el?: Element) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Returns the sanitization level the tracker currently has for a node
|
|
62
|
+
* (0 = Plain, 1 = Obscured, 2 = Hidden), or undefined if it isn't tracked.
|
|
63
|
+
* */
|
|
64
|
+
checkSanitization: (el: Node) => import("./app/sanitizer.js").SanitizeLevel | undefined;
|
|
50
65
|
use<T>(fn: (app: App | null, options?: Partial<Options>) => T): T;
|
|
51
66
|
isActive(): boolean;
|
|
52
67
|
/**
|
|
@@ -103,9 +118,9 @@ export default class API {
|
|
|
103
118
|
}) => string | undefined;
|
|
104
119
|
setUserID: (id: string) => void;
|
|
105
120
|
identify: (id: string) => void;
|
|
106
|
-
track: (
|
|
121
|
+
track: (eventName: string, properties?: Record<string, any>, options?: {
|
|
107
122
|
send_immediately: boolean;
|
|
108
|
-
}) => void
|
|
123
|
+
}) => void | undefined;
|
|
109
124
|
userID: (id: string) => void;
|
|
110
125
|
setUserAnonymousID(id: string): void;
|
|
111
126
|
userAnonymousID(id: string): void;
|
|
@@ -16,7 +16,7 @@ declare class TrackerSingleton {
|
|
|
16
16
|
* (which can be used to stitch sessions together)
|
|
17
17
|
* */
|
|
18
18
|
stop(): string | undefined;
|
|
19
|
-
setUserID(id: string)
|
|
19
|
+
setUserID: (id: string) => void;
|
|
20
20
|
get analytics(): import("./entry.js").Analytics | null;
|
|
21
21
|
identify: (id: string) => void;
|
|
22
22
|
track: (eventName: string, properties?: Record<string, any>, options?: {
|
|
@@ -101,6 +101,30 @@ declare class TrackerSingleton {
|
|
|
101
101
|
forceFlushBatch(): void;
|
|
102
102
|
getSessionInfo(): import("./app/session.js").SessionInfo | null;
|
|
103
103
|
getTabId(): string | null;
|
|
104
|
+
/**
|
|
105
|
+
* Re-evaluates sanitization against the current DOM and re-emits whatever
|
|
106
|
+
* changed, updating already-recorded nodes mid-session. Call after toggling
|
|
107
|
+
* `data-openreplay-*` attributes or after changing whatever your `domSanitizer`
|
|
108
|
+
* keys on (class/id/etc).
|
|
109
|
+
*
|
|
110
|
+
* @param el - the highest node you changed; omit to re-scan the whole document.
|
|
111
|
+
* */
|
|
112
|
+
resanitize(el?: Element): void;
|
|
113
|
+
/**
|
|
114
|
+
* Returns the sanitization level the tracker currently has for a node
|
|
115
|
+
* (0 = Plain, 1 = Obscured, 2 = Hidden), or undefined if it isn't tracked.
|
|
116
|
+
* */
|
|
117
|
+
checkSanitization(el: Node): import("./index.js").SanitizeLevel | undefined;
|
|
118
|
+
incident(options: {
|
|
119
|
+
label?: string;
|
|
120
|
+
startTime: number;
|
|
121
|
+
endTime?: number;
|
|
122
|
+
}): void;
|
|
123
|
+
/**
|
|
124
|
+
* Use custom token for analytics events without session recording
|
|
125
|
+
* */
|
|
126
|
+
setAnalyticsToken(token: string): void;
|
|
127
|
+
getAnalyticsToken(): string | undefined;
|
|
104
128
|
}
|
|
105
129
|
declare const tracker: TrackerSingleton;
|
|
106
130
|
export default tracker;
|