@openreplay/tracker 16.2.1 → 16.3.0-beta.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/cjs/entry.js +130 -39
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +130 -39
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/index.d.ts +2 -0
- package/dist/cjs/main/modules/cssrules.d.ts +15 -1
- package/dist/lib/entry.js +130 -39
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +130 -39
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/index.d.ts +2 -0
- package/dist/lib/main/modules/cssrules.d.ts +15 -1
- package/dist/types/main/index.d.ts +2 -0
- package/dist/types/main/modules/cssrules.d.ts +15 -1
- package/package.json +1 -1
package/dist/cjs/main/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type { Options as TimingOptions } from './modules/timing.js';
|
|
|
13
13
|
import type { Options as NetworkOptions } from './modules/network.js';
|
|
14
14
|
import type { MouseHandlerOptions } from './modules/mouse.js';
|
|
15
15
|
import type { SessionInfo } from './app/session.js';
|
|
16
|
+
import type { CssRulesOptions } from './modules/cssrules.js';
|
|
16
17
|
import type { StartOptions } from './app/index.js';
|
|
17
18
|
import type { StartPromiseReturn } from './app/index.js';
|
|
18
19
|
export type Options = Partial<AppOptions & ConsoleOptions & ExceptionOptions & InputOptions & PerformanceOptions & TimingOptions> & {
|
|
@@ -28,6 +29,7 @@ export type Options = Partial<AppOptions & ConsoleOptions & ExceptionOptions & I
|
|
|
28
29
|
onFlagsLoad?: (flags: IFeatureFlag[]) => void;
|
|
29
30
|
};
|
|
30
31
|
__DISABLE_SECURE_MODE?: boolean;
|
|
32
|
+
css: CssRulesOptions;
|
|
31
33
|
};
|
|
32
34
|
export default class API {
|
|
33
35
|
readonly options: Partial<Options>;
|
|
@@ -1,2 +1,16 @@
|
|
|
1
1
|
import type App from '../app/index.js';
|
|
2
|
-
export
|
|
2
|
+
export interface CssRulesOptions {
|
|
3
|
+
checkCssInterval?: number;
|
|
4
|
+
scanInMemoryCSS?: boolean;
|
|
5
|
+
/**
|
|
6
|
+
Useful for cases where you expect limited amount of mutations
|
|
7
|
+
|
|
8
|
+
i.e when sheets are hydrated on client after initial render.
|
|
9
|
+
|
|
10
|
+
if you want to observe for x seconds, do (x*1000)/checkCssInterval = checkLimit
|
|
11
|
+
|
|
12
|
+
applied to each stylesheet individually.
|
|
13
|
+
*/
|
|
14
|
+
checkLimit?: number;
|
|
15
|
+
}
|
|
16
|
+
export default function (app: App, opts: CssRulesOptions): void;
|
package/dist/lib/entry.js
CHANGED
|
@@ -3560,7 +3560,7 @@ function isNodeStillActive(node) {
|
|
|
3560
3560
|
return [false, e];
|
|
3561
3561
|
}
|
|
3562
3562
|
}
|
|
3563
|
-
const defaults = {
|
|
3563
|
+
const defaults$1 = {
|
|
3564
3564
|
interval: SECOND * 30,
|
|
3565
3565
|
batchSize: 2500,
|
|
3566
3566
|
enabled: true,
|
|
@@ -3588,7 +3588,7 @@ class Maintainer {
|
|
|
3588
3588
|
clearInterval(this.interval);
|
|
3589
3589
|
}
|
|
3590
3590
|
};
|
|
3591
|
-
this.options = { ...defaults, ...options };
|
|
3591
|
+
this.options = { ...defaults$1, ...options };
|
|
3592
3592
|
}
|
|
3593
3593
|
}
|
|
3594
3594
|
|
|
@@ -5202,7 +5202,7 @@ class App {
|
|
|
5202
5202
|
this.stopCallbacks = [];
|
|
5203
5203
|
this.commitCallbacks = [];
|
|
5204
5204
|
this.activityState = ActivityState.NotActive;
|
|
5205
|
-
this.version = '16.
|
|
5205
|
+
this.version = '16.3.0-beta.0'; // TODO: version compatability check inside each plugin.
|
|
5206
5206
|
this.socketMode = false;
|
|
5207
5207
|
this.compressionThreshold = 24 * 1000;
|
|
5208
5208
|
this.bc = null;
|
|
@@ -8037,60 +8037,137 @@ function Viewport (app) {
|
|
|
8037
8037
|
app.ticker.attach(sendSetViewportSize, 5, false);
|
|
8038
8038
|
}
|
|
8039
8039
|
|
|
8040
|
-
|
|
8041
|
-
|
|
8040
|
+
const defaults = {
|
|
8041
|
+
checkCssInterval: 200,
|
|
8042
|
+
scanInMemoryCSS: false,
|
|
8043
|
+
checkLimit: undefined,
|
|
8044
|
+
};
|
|
8045
|
+
function CSSRules (app, opts) {
|
|
8046
|
+
if (app === null)
|
|
8042
8047
|
return;
|
|
8043
|
-
}
|
|
8044
8048
|
if (!window.CSSStyleSheet) {
|
|
8045
8049
|
app.send(TechnicalInfo('no_stylesheet_prototype_in_window', ''));
|
|
8046
8050
|
return;
|
|
8047
8051
|
}
|
|
8052
|
+
const options = { ...defaults, ...opts };
|
|
8053
|
+
// sheetID:index -> ruleText
|
|
8054
|
+
const ruleSnapshots = new Map();
|
|
8055
|
+
let checkInterval = null;
|
|
8056
|
+
const trackedSheets = new Set();
|
|
8057
|
+
const checkIntervalMs = options.checkCssInterval || 200;
|
|
8058
|
+
let checkIterations = {};
|
|
8059
|
+
function checkRuleChanges() {
|
|
8060
|
+
if (!options.scanInMemoryCSS)
|
|
8061
|
+
return;
|
|
8062
|
+
const allSheets = trackedSheets.values();
|
|
8063
|
+
for (const sheet of allSheets) {
|
|
8064
|
+
try {
|
|
8065
|
+
const sheetID = styleSheetIDMap.get(sheet);
|
|
8066
|
+
if (!sheetID)
|
|
8067
|
+
continue;
|
|
8068
|
+
if (options.checkLimit) {
|
|
8069
|
+
if (!checkIterations[sheetID]) {
|
|
8070
|
+
checkIterations[sheetID] = 0;
|
|
8071
|
+
}
|
|
8072
|
+
else {
|
|
8073
|
+
checkIterations[sheetID]++;
|
|
8074
|
+
}
|
|
8075
|
+
if (checkIterations[sheetID] > options.checkLimit) {
|
|
8076
|
+
trackedSheets.delete(sheet);
|
|
8077
|
+
return;
|
|
8078
|
+
}
|
|
8079
|
+
}
|
|
8080
|
+
for (let j = 0; j < sheet.cssRules.length; j++) {
|
|
8081
|
+
try {
|
|
8082
|
+
const rule = sheet.cssRules[j];
|
|
8083
|
+
const key = `${sheetID}:${j}`;
|
|
8084
|
+
const oldText = ruleSnapshots.get(key);
|
|
8085
|
+
const newText = rule.cssText;
|
|
8086
|
+
if (oldText !== newText) {
|
|
8087
|
+
if (oldText !== undefined) {
|
|
8088
|
+
// Rule is changed
|
|
8089
|
+
app.send(AdoptedSSDeleteRule(sheetID, j));
|
|
8090
|
+
app.send(AdoptedSSInsertRuleURLBased(sheetID, newText, j, app.getBaseHref()));
|
|
8091
|
+
}
|
|
8092
|
+
else {
|
|
8093
|
+
// Rule added
|
|
8094
|
+
app.send(AdoptedSSInsertRuleURLBased(sheetID, newText, j, app.getBaseHref()));
|
|
8095
|
+
}
|
|
8096
|
+
ruleSnapshots.set(key, newText);
|
|
8097
|
+
}
|
|
8098
|
+
}
|
|
8099
|
+
catch (e) {
|
|
8100
|
+
/* Skip inaccessible rules */
|
|
8101
|
+
}
|
|
8102
|
+
}
|
|
8103
|
+
const keysToCheck = Array.from(ruleSnapshots.keys()).filter((key) => key.startsWith(`${sheetID}:`));
|
|
8104
|
+
for (const key of keysToCheck) {
|
|
8105
|
+
const index = parseInt(key.split(':')[1], 10);
|
|
8106
|
+
if (index >= sheet.cssRules.length) {
|
|
8107
|
+
ruleSnapshots.delete(key);
|
|
8108
|
+
}
|
|
8109
|
+
}
|
|
8110
|
+
}
|
|
8111
|
+
catch (e) {
|
|
8112
|
+
/* Skip inaccessible sheets */
|
|
8113
|
+
trackedSheets.delete(sheet);
|
|
8114
|
+
}
|
|
8115
|
+
}
|
|
8116
|
+
}
|
|
8117
|
+
const emptyRuleReg = /{\s*}/;
|
|
8118
|
+
function isRuleEmpty(rule) {
|
|
8119
|
+
return emptyRuleReg.test(rule);
|
|
8120
|
+
}
|
|
8048
8121
|
const sendInsertDeleteRule = app.safe((sheet, index, rule) => {
|
|
8049
8122
|
const sheetID = styleSheetIDMap.get(sheet);
|
|
8050
|
-
if (!sheetID)
|
|
8051
|
-
// OK-case. Sheet haven't been registered yet. Rules will be sent on registration.
|
|
8123
|
+
if (!sheetID)
|
|
8052
8124
|
return;
|
|
8053
|
-
}
|
|
8054
8125
|
if (typeof rule === 'string') {
|
|
8055
8126
|
app.send(AdoptedSSInsertRuleURLBased(sheetID, rule, index, app.getBaseHref()));
|
|
8127
|
+
if (isRuleEmpty(rule)) {
|
|
8128
|
+
ruleSnapshots.set(`${sheetID}:${index}`, rule);
|
|
8129
|
+
trackedSheets.add(sheet);
|
|
8130
|
+
}
|
|
8056
8131
|
}
|
|
8057
8132
|
else {
|
|
8058
8133
|
app.send(AdoptedSSDeleteRule(sheetID, index));
|
|
8134
|
+
if (ruleSnapshots.has(`${sheetID}:${index}`)) {
|
|
8135
|
+
ruleSnapshots.delete(`${sheetID}:${index}`);
|
|
8136
|
+
}
|
|
8059
8137
|
}
|
|
8060
8138
|
});
|
|
8061
|
-
// TODO: proper rule insertion/removal (how?)
|
|
8062
8139
|
const sendReplaceGroupingRule = app.safe((rule) => {
|
|
8063
8140
|
let topmostRule = rule;
|
|
8064
|
-
while (topmostRule.parentRule)
|
|
8141
|
+
while (topmostRule.parentRule)
|
|
8065
8142
|
topmostRule = topmostRule.parentRule;
|
|
8066
|
-
}
|
|
8067
8143
|
const sheet = topmostRule.parentStyleSheet;
|
|
8068
|
-
if (!sheet)
|
|
8069
|
-
app.debug.warn('No parent StyleSheet found for', topmostRule, rule);
|
|
8144
|
+
if (!sheet)
|
|
8070
8145
|
return;
|
|
8071
|
-
}
|
|
8072
8146
|
const sheetID = styleSheetIDMap.get(sheet);
|
|
8073
|
-
if (!sheetID)
|
|
8074
|
-
app.debug.warn('No sheedID found for', sheet, styleSheetIDMap);
|
|
8147
|
+
if (!sheetID)
|
|
8075
8148
|
return;
|
|
8076
|
-
}
|
|
8077
8149
|
const cssText = topmostRule.cssText;
|
|
8078
|
-
const
|
|
8079
|
-
const idx = Array.from(ruleList).indexOf(topmostRule);
|
|
8150
|
+
const idx = Array.from(sheet.cssRules).indexOf(topmostRule);
|
|
8080
8151
|
if (idx >= 0) {
|
|
8081
8152
|
app.send(AdoptedSSInsertRuleURLBased(sheetID, cssText, idx, app.getBaseHref()));
|
|
8082
|
-
app.send(AdoptedSSDeleteRule(sheetID, idx + 1));
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8153
|
+
app.send(AdoptedSSDeleteRule(sheetID, idx + 1));
|
|
8154
|
+
if (isRuleEmpty(cssText)) {
|
|
8155
|
+
ruleSnapshots.set(`${sheetID}:${idx}`, cssText);
|
|
8156
|
+
trackedSheets.add(sheet);
|
|
8157
|
+
}
|
|
8086
8158
|
}
|
|
8087
8159
|
});
|
|
8160
|
+
// Patch prototype methods
|
|
8088
8161
|
const patchContext = app.safe((context) => {
|
|
8162
|
+
if (context.__css_tracking_patched__)
|
|
8163
|
+
return;
|
|
8164
|
+
context.__css_tracking_patched__ = true;
|
|
8089
8165
|
const { insertRule, deleteRule } = context.CSSStyleSheet.prototype;
|
|
8090
8166
|
const { insertRule: groupInsertRule, deleteRule: groupDeleteRule } = context.CSSGroupingRule.prototype;
|
|
8091
8167
|
context.CSSStyleSheet.prototype.insertRule = function (rule, index = 0) {
|
|
8092
|
-
|
|
8093
|
-
|
|
8168
|
+
const result = insertRule.call(this, rule, index);
|
|
8169
|
+
sendInsertDeleteRule(this, result, rule);
|
|
8170
|
+
return result;
|
|
8094
8171
|
};
|
|
8095
8172
|
context.CSSStyleSheet.prototype.deleteRule = function (index) {
|
|
8096
8173
|
sendInsertDeleteRule(this, index);
|
|
@@ -8101,7 +8178,7 @@ function CSSRules (app) {
|
|
|
8101
8178
|
sendReplaceGroupingRule(this);
|
|
8102
8179
|
return result;
|
|
8103
8180
|
};
|
|
8104
|
-
context.CSSGroupingRule.prototype.deleteRule = function (index
|
|
8181
|
+
context.CSSGroupingRule.prototype.deleteRule = function (index) {
|
|
8105
8182
|
const result = groupDeleteRule.call(this, index);
|
|
8106
8183
|
sendReplaceGroupingRule(this);
|
|
8107
8184
|
return result;
|
|
@@ -8110,24 +8187,38 @@ function CSSRules (app) {
|
|
|
8110
8187
|
patchContext(window);
|
|
8111
8188
|
app.observer.attachContextCallback(patchContext);
|
|
8112
8189
|
app.nodes.attachNodeCallback((node) => {
|
|
8113
|
-
if (!hasTag(node, 'style') || !node.sheet)
|
|
8190
|
+
if (!hasTag(node, 'style') || !node.sheet)
|
|
8191
|
+
return;
|
|
8192
|
+
if (node.textContent !== null && node.textContent.trim().length > 0)
|
|
8114
8193
|
return;
|
|
8115
|
-
}
|
|
8116
|
-
if (node.textContent !== null && node.textContent.trim().length > 0) {
|
|
8117
|
-
return; // Non-virtual styles captured by the observer as a text
|
|
8118
|
-
}
|
|
8119
8194
|
const nodeID = app.nodes.getID(node);
|
|
8120
|
-
if (!nodeID)
|
|
8195
|
+
if (!nodeID)
|
|
8121
8196
|
return;
|
|
8122
|
-
}
|
|
8123
8197
|
const sheet = node.sheet;
|
|
8124
8198
|
const sheetID = nextID();
|
|
8125
8199
|
styleSheetIDMap.set(sheet, sheetID);
|
|
8126
8200
|
app.send(AdoptedSSAddOwner(sheetID, nodeID));
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8201
|
+
for (let i = 0; i < sheet.cssRules.length; i++) {
|
|
8202
|
+
try {
|
|
8203
|
+
sendInsertDeleteRule(sheet, i, sheet.cssRules[i].cssText);
|
|
8204
|
+
}
|
|
8205
|
+
catch (e) {
|
|
8206
|
+
// Skip inaccessible rules
|
|
8207
|
+
}
|
|
8208
|
+
}
|
|
8209
|
+
});
|
|
8210
|
+
function startChecking() {
|
|
8211
|
+
if (checkInterval || !options.scanInMemoryCSS)
|
|
8212
|
+
return;
|
|
8213
|
+
checkInterval = window.setInterval(checkRuleChanges, checkIntervalMs);
|
|
8214
|
+
}
|
|
8215
|
+
setTimeout(startChecking, 50);
|
|
8216
|
+
app.attachStopCallback(() => {
|
|
8217
|
+
if (checkInterval) {
|
|
8218
|
+
clearInterval(checkInterval);
|
|
8219
|
+
checkInterval = null;
|
|
8130
8220
|
}
|
|
8221
|
+
ruleSnapshots.clear();
|
|
8131
8222
|
});
|
|
8132
8223
|
}
|
|
8133
8224
|
|
|
@@ -9692,7 +9783,7 @@ class API {
|
|
|
9692
9783
|
this.signalStartIssue = (reason, missingApi) => {
|
|
9693
9784
|
const doNotTrack = this.checkDoNotTrack();
|
|
9694
9785
|
console.log("Tracker couldn't start due to:", JSON.stringify({
|
|
9695
|
-
trackerVersion: '16.
|
|
9786
|
+
trackerVersion: '16.3.0-beta.0',
|
|
9696
9787
|
projectKey: this.options.projectKey,
|
|
9697
9788
|
doNotTrack,
|
|
9698
9789
|
reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
|
|
@@ -9791,7 +9882,7 @@ class API {
|
|
|
9791
9882
|
Mouse(app, options.mouse);
|
|
9792
9883
|
// inside iframe, we ignore viewport scroll
|
|
9793
9884
|
Scroll(app, this.crossdomainMode);
|
|
9794
|
-
CSSRules(app);
|
|
9885
|
+
CSSRules(app, options.css);
|
|
9795
9886
|
ConstructedStyleSheets(app);
|
|
9796
9887
|
Console(app, options);
|
|
9797
9888
|
Exception(app, options);
|