@thumbmarkjs/thumbmarkjs 1.3.3 → 1.3.4
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/README.md +1 -1
- package/dist/thumbmark.cjs.js +1 -1
- package/dist/thumbmark.cjs.js.map +1 -1
- package/dist/thumbmark.esm.d.ts +6 -2
- package/dist/thumbmark.esm.js +1 -1
- package/dist/thumbmark.esm.js.map +1 -1
- package/dist/thumbmark.umd.js +1 -1
- package/dist/thumbmark.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/mathml/index.ts +69 -42
- package/src/components/permissions/index.ts +11 -6
- package/src/components/speech/index.ts +103 -0
- package/src/components/webrtc/index.ts +18 -9
- package/src/factory.ts +3 -1
- package/src/functions/api.ts +3 -2
- package/src/functions/index.ts +5 -2
- package/src/options.ts +3 -10
- package/src/utils/log.ts +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { componentInterface } from '../../factory';
|
|
2
2
|
import { hash } from '../../utils/hash';
|
|
3
|
+
import { ephemeralIFrame } from '../../utils/ephemeralIFrame';
|
|
3
4
|
|
|
4
5
|
const BLACKBOARD_BOLD = ['\uD835\uDD04', '\uD835\uDD05', '\u212D', '\uD835\uDD07', '\uD835\uDD08', '\uD835\uDD09', '\uD835\uDD38', '\uD835\uDD39', '\u2102', '\uD835\uDD3B', '\uD835\uDD3C', '\uD835\uDD3D'];
|
|
5
6
|
const GREEK_SYMBOLS = ['\u03B2', '\u03C8', '\u03BB', '\u03B5', '\u03B6', '\u03B1', '\u03BE', '\u03BC', '\u03C1', '\u03C6', '\u03BA', '\u03C4', '\u03B7', '\u03C3', '\u03B9', '\u03C9', '\u03B3', '\u03BD', '\u03C7', '\u03B4', '\u03B8', '\u03C0', '\u03C5', '\u03BF'];
|
|
@@ -7,33 +8,58 @@ const GREEK_SYMBOLS = ['\u03B2', '\u03C8', '\u03BB', '\u03B5', '\u03B6', '\u03B1
|
|
|
7
8
|
export default async function getMathML(): Promise<componentInterface | null> {
|
|
8
9
|
return new Promise((resolve) => {
|
|
9
10
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
ephemeralIFrame(async ({ iframe }) => {
|
|
12
|
+
try {
|
|
13
|
+
if (!isMathMLSupported(iframe)) {
|
|
14
|
+
resolve({
|
|
15
|
+
supported: false,
|
|
16
|
+
error: 'MathML not supported'
|
|
17
|
+
});
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const measurements: any = {};
|
|
27
|
-
structures.forEach((struct, i) => {
|
|
28
|
-
measurements[`struct_${i}`] = measureMathMLStructure(struct);
|
|
29
|
-
});
|
|
21
|
+
const structures = [
|
|
22
|
+
createMathML('integral', '<msubsup><mo>\u222B</mo><mi>a</mi><mi>b</mi></msubsup><mfrac><mrow><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo></mrow><mrow><mi>g</mi><mo>(</mo><mi>x</mi><mo>)</mo></mrow></mfrac><mi>dx</mi>'),
|
|
23
|
+
createMathML('fraction', '<mfrac><mrow><mi>\u03C0</mi><mo>\u00D7</mo><msup><mi>r</mi><mn>2</mn></msup></mrow><mrow><mn>2</mn><mi>\u03C3</mi></mrow></mfrac>'),
|
|
24
|
+
createMathML('matrix', '<mo>[</mo><mtable><mtr><mtd><mi>\u03B1</mi></mtd><mtd><mi>\u03B2</mi></mtd></mtr><mtr><mtd><mi>\u03B3</mi></mtd><mtd><mi>\u03B4</mi></mtd></mtr></mtable><mo>]</mo>'),
|
|
25
|
+
createComplexNestedStructure(),
|
|
26
|
+
...createSymbolStructures()
|
|
27
|
+
];
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
//measurements,
|
|
34
|
-
hash: hash(JSON.stringify(measurements))
|
|
35
|
-
});
|
|
29
|
+
const dimensionsArray: any[] = [];
|
|
30
|
+
let fontStyleHash: string = '';
|
|
36
31
|
|
|
32
|
+
structures.forEach((struct, i) => {
|
|
33
|
+
const measurement = measureMathMLStructure(struct, iframe);
|
|
34
|
+
// Extract dimensions for this structure
|
|
35
|
+
dimensionsArray.push({
|
|
36
|
+
width: measurement.dimensions.width,
|
|
37
|
+
height: measurement.dimensions.height
|
|
38
|
+
});
|
|
39
|
+
// Capture font style hash from the first structure (it's the same for all)
|
|
40
|
+
if (i === 0 && measurement.fontInfo) {
|
|
41
|
+
fontStyleHash = hash(JSON.stringify(measurement.fontInfo));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const details = {
|
|
46
|
+
fontStyleHash,
|
|
47
|
+
dimensions: dimensionsArray
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
resolve({
|
|
51
|
+
//supported: true,
|
|
52
|
+
details,
|
|
53
|
+
hash: hash(JSON.stringify(details))
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
} catch (error) {
|
|
57
|
+
resolve({
|
|
58
|
+
supported: false,
|
|
59
|
+
error: `MathML error: ${(error as Error).message}`
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
});
|
|
37
63
|
} catch (error) {
|
|
38
64
|
resolve({
|
|
39
65
|
supported: false,
|
|
@@ -43,17 +69,17 @@ export default async function getMathML(): Promise<componentInterface | null> {
|
|
|
43
69
|
});
|
|
44
70
|
}
|
|
45
71
|
|
|
46
|
-
function isMathMLSupported(): boolean {
|
|
72
|
+
function isMathMLSupported(iframe: Document): boolean {
|
|
47
73
|
try {
|
|
48
|
-
const testElement =
|
|
74
|
+
const testElement = iframe.createElement('math');
|
|
49
75
|
testElement.innerHTML = '<mrow><mi>x</mi></mrow>';
|
|
50
76
|
testElement.style.position = 'absolute';
|
|
51
77
|
testElement.style.visibility = 'hidden';
|
|
52
|
-
|
|
53
|
-
|
|
78
|
+
|
|
79
|
+
iframe.body.appendChild(testElement);
|
|
54
80
|
const rect = testElement.getBoundingClientRect();
|
|
55
|
-
|
|
56
|
-
|
|
81
|
+
iframe.body.removeChild(testElement);
|
|
82
|
+
|
|
57
83
|
return rect.width > 0 && rect.height > 0;
|
|
58
84
|
} catch {
|
|
59
85
|
return false;
|
|
@@ -101,25 +127,26 @@ function createSymbolStructures(): string[] {
|
|
|
101
127
|
return structures;
|
|
102
128
|
}
|
|
103
129
|
|
|
104
|
-
function measureMathMLStructure(mathml: string): any {
|
|
130
|
+
function measureMathMLStructure(mathml: string, iframe: Document): any {
|
|
105
131
|
try {
|
|
106
|
-
const mathElement =
|
|
132
|
+
const mathElement = iframe.createElement('math');
|
|
107
133
|
mathElement.innerHTML = mathml.replace(/<\/?math>/g, '');
|
|
108
134
|
mathElement.style.whiteSpace = 'nowrap';
|
|
109
135
|
mathElement.style.position = 'absolute';
|
|
110
136
|
mathElement.style.visibility = 'hidden';
|
|
111
137
|
mathElement.style.top = '-9999px';
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
138
|
+
|
|
139
|
+
iframe.body.appendChild(mathElement);
|
|
140
|
+
|
|
115
141
|
const rect = mathElement.getBoundingClientRect();
|
|
116
|
-
const
|
|
117
|
-
|
|
142
|
+
const iframeWindow = iframe.defaultView || window;
|
|
143
|
+
const computedStyle = iframeWindow.getComputedStyle(mathElement);
|
|
144
|
+
|
|
118
145
|
const measurements = {
|
|
119
146
|
dimensions: {
|
|
120
147
|
width: rect.width,
|
|
121
148
|
height: rect.height,
|
|
122
|
-
|
|
149
|
+
|
|
123
150
|
},
|
|
124
151
|
fontInfo: {
|
|
125
152
|
fontFamily: computedStyle.fontFamily,
|
|
@@ -129,7 +156,7 @@ function measureMathMLStructure(mathml: string): any {
|
|
|
129
156
|
lineHeight: computedStyle.lineHeight,
|
|
130
157
|
// Enhanced font properties for better system detection
|
|
131
158
|
fontVariant: computedStyle.fontVariant || 'normal',
|
|
132
|
-
fontStretch: computedStyle.fontStretch || 'normal',
|
|
159
|
+
fontStretch: computedStyle.fontStretch || 'normal',
|
|
133
160
|
fontSizeAdjust: computedStyle.fontSizeAdjust || 'none',
|
|
134
161
|
textRendering: computedStyle.textRendering || 'auto',
|
|
135
162
|
fontFeatureSettings: computedStyle.fontFeatureSettings || 'normal',
|
|
@@ -137,10 +164,10 @@ function measureMathMLStructure(mathml: string): any {
|
|
|
137
164
|
fontKerning: computedStyle.fontKerning || 'auto'
|
|
138
165
|
}
|
|
139
166
|
};
|
|
140
|
-
|
|
141
|
-
|
|
167
|
+
|
|
168
|
+
iframe.body.removeChild(mathElement);
|
|
142
169
|
return measurements;
|
|
143
|
-
|
|
170
|
+
|
|
144
171
|
} catch (error) {
|
|
145
172
|
return {
|
|
146
173
|
error: (error as Error).message
|
|
@@ -25,13 +25,18 @@ const defaultPermissionKeys: PermissionName[] = [
|
|
|
25
25
|
] as PermissionName[];
|
|
26
26
|
|
|
27
27
|
export default async function getPermissions(options?: optionsInterface): Promise<componentInterface> {
|
|
28
|
-
|
|
28
|
+
const permission_keys = options?.permissions_to_check || defaultPermissionKeys;
|
|
29
29
|
const retries = 3;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
|
|
31
|
+
// Run permission checks multiple times
|
|
32
|
+
const results = await Promise.all(
|
|
33
|
+
Array.from({ length: retries }, () => getBrowserPermissionsOnce(permission_keys))
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Get most frequent values across all retries
|
|
37
|
+
const permissionStatus = mostFrequentValuesInArrayOfDictionaries(results, permission_keys);
|
|
38
|
+
|
|
39
|
+
return permissionStatus;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
async function getBrowserPermissionsOnce(permission_keys: PermissionName[]): Promise<componentInterface> {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { componentInterface } from '../../factory';
|
|
2
|
+
import { hash } from '../../utils/hash';
|
|
3
|
+
|
|
4
|
+
const VOICE_LOAD_TIMEOUT = 800; // milliseconds to wait for voices to load
|
|
5
|
+
|
|
6
|
+
export default async function getSpeech(): Promise<componentInterface | null> {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
try {
|
|
9
|
+
// Check if Speech Synthesis API is available
|
|
10
|
+
if (typeof window === 'undefined' || !window.speechSynthesis || typeof window.speechSynthesis.getVoices !== 'function') {
|
|
11
|
+
resolve({
|
|
12
|
+
supported: false,
|
|
13
|
+
error: 'Speech Synthesis API not supported'
|
|
14
|
+
});
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let voicesResolved = false;
|
|
19
|
+
let timeoutHandle: ReturnType<typeof setTimeout> | null = null;
|
|
20
|
+
|
|
21
|
+
const processVoices = (voices: SpeechSynthesisVoice[]) => {
|
|
22
|
+
if (voicesResolved) return;
|
|
23
|
+
voicesResolved = true;
|
|
24
|
+
|
|
25
|
+
// Clear timeout if it exists
|
|
26
|
+
if (timeoutHandle) {
|
|
27
|
+
clearTimeout(timeoutHandle);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Collect voice signatures
|
|
32
|
+
const voiceSignatures = voices.map((voice) => {
|
|
33
|
+
// Escape commas and backslashes in voice properties
|
|
34
|
+
const escapeValue = (value: string): string => {
|
|
35
|
+
return value.replace(/\\/g, '\\\\').replace(/,/g, '\\,');
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Format: voiceURI,name,lang,localService,default
|
|
39
|
+
const signature = [
|
|
40
|
+
escapeValue(voice.voiceURI || ''),
|
|
41
|
+
escapeValue(voice.name || ''),
|
|
42
|
+
escapeValue(voice.lang || ''),
|
|
43
|
+
voice.localService ? '1' : '0',
|
|
44
|
+
voice.default ? '1' : '0'
|
|
45
|
+
].join(',');
|
|
46
|
+
|
|
47
|
+
return signature;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Sort alphabetically for consistent ordering
|
|
51
|
+
voiceSignatures.sort();
|
|
52
|
+
|
|
53
|
+
// Create details object with count and hash
|
|
54
|
+
const details = {
|
|
55
|
+
voiceCount: voices.length,
|
|
56
|
+
voicesHash: hash(JSON.stringify(voiceSignatures))
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
resolve({
|
|
60
|
+
details,
|
|
61
|
+
hash: hash(JSON.stringify(details))
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
} catch (error) {
|
|
65
|
+
resolve({
|
|
66
|
+
supported: true,
|
|
67
|
+
error: `Voice processing failed: ${(error as Error).message}`
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Try to get voices immediately
|
|
73
|
+
const voices = window.speechSynthesis.getVoices();
|
|
74
|
+
|
|
75
|
+
// If voices are available immediately, process them
|
|
76
|
+
if (voices.length > 0) {
|
|
77
|
+
processVoices(voices);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Set up timeout in case voices never load
|
|
82
|
+
timeoutHandle = setTimeout(() => {
|
|
83
|
+
const voices = window.speechSynthesis.getVoices();
|
|
84
|
+
processVoices(voices);
|
|
85
|
+
}, VOICE_LOAD_TIMEOUT);
|
|
86
|
+
|
|
87
|
+
// Listen for voiceschanged event (for browsers that load voices asynchronously)
|
|
88
|
+
const onVoicesChanged = () => {
|
|
89
|
+
window.speechSynthesis.removeEventListener('voiceschanged', onVoicesChanged);
|
|
90
|
+
const voices = window.speechSynthesis.getVoices();
|
|
91
|
+
processVoices(voices);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
window.speechSynthesis.addEventListener('voiceschanged', onVoicesChanged);
|
|
95
|
+
|
|
96
|
+
} catch (error) {
|
|
97
|
+
resolve({
|
|
98
|
+
supported: false,
|
|
99
|
+
error: `Speech Synthesis error: ${(error as Error).message}`
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
@@ -64,9 +64,19 @@ export default async function getWebRTC(): Promise<componentInterface | null> {
|
|
|
64
64
|
}).filter(Boolean);
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
const audioCodecs = constructDescriptions('audio', getDescriptors('audio'));
|
|
68
|
+
const videoCodecs = constructDescriptions('video', getDescriptors('video'));
|
|
69
|
+
|
|
70
|
+
const compressedData = {
|
|
71
|
+
audio: {
|
|
72
|
+
count: audioCodecs.length,
|
|
73
|
+
hash: hash(JSON.stringify(audioCodecs))
|
|
74
|
+
},
|
|
75
|
+
video: {
|
|
76
|
+
count: videoCodecs.length,
|
|
77
|
+
hash: hash(JSON.stringify(videoCodecs))
|
|
78
|
+
},
|
|
79
|
+
extensionsHash: hash(JSON.stringify(extensions))
|
|
70
80
|
};
|
|
71
81
|
|
|
72
82
|
// Set up for ICE candidate collection with timeout
|
|
@@ -74,10 +84,9 @@ export default async function getWebRTC(): Promise<componentInterface | null> {
|
|
|
74
84
|
const timeout = setTimeout(() => {
|
|
75
85
|
connection.removeEventListener('icecandidate', onIceCandidate);
|
|
76
86
|
connection.close();
|
|
77
|
-
resolveResult({
|
|
87
|
+
resolveResult({
|
|
78
88
|
supported: true,
|
|
79
|
-
|
|
80
|
-
extensions: extensions as string[],
|
|
89
|
+
...compressedData,
|
|
81
90
|
timeout: true
|
|
82
91
|
});
|
|
83
92
|
}, 3000);
|
|
@@ -90,10 +99,9 @@ export default async function getWebRTC(): Promise<componentInterface | null> {
|
|
|
90
99
|
connection.removeEventListener('icecandidate', onIceCandidate);
|
|
91
100
|
connection.close();
|
|
92
101
|
|
|
93
|
-
resolveResult({
|
|
102
|
+
resolveResult({
|
|
94
103
|
supported: true,
|
|
95
|
-
|
|
96
|
-
extensions: extensions as string[],
|
|
104
|
+
...compressedData,
|
|
97
105
|
candidateType: candidateObj.type || ''
|
|
98
106
|
});
|
|
99
107
|
};
|
|
@@ -102,6 +110,7 @@ export default async function getWebRTC(): Promise<componentInterface | null> {
|
|
|
102
110
|
});
|
|
103
111
|
|
|
104
112
|
resolve({
|
|
113
|
+
details: result,
|
|
105
114
|
hash: hash(JSON.stringify(result)),
|
|
106
115
|
});
|
|
107
116
|
|
package/src/factory.ts
CHANGED
|
@@ -23,6 +23,7 @@ import getWebGL from "./components/webgl";
|
|
|
23
23
|
// Import experimental component functions
|
|
24
24
|
import getWebRTC from "./components/webrtc";
|
|
25
25
|
import getMathML from "./components/mathml";
|
|
26
|
+
import getSpeech from "./components/speech";
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* @description key->function map of built-in components. Do not call the function here.
|
|
@@ -46,7 +47,8 @@ export const tm_component_promises = {
|
|
|
46
47
|
*/
|
|
47
48
|
export const tm_experimental_component_promises = {
|
|
48
49
|
'webrtc': getWebRTC,
|
|
49
|
-
'mathml': getMathML
|
|
50
|
+
'mathml': getMathML,
|
|
51
|
+
'speech': getSpeech
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
// the component interface is the form of the JSON object the function's promise must return
|
package/src/functions/api.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { optionsInterface,
|
|
1
|
+
import { optionsInterface, DEFAULT_API_ENDPOINT } from '../options';
|
|
2
2
|
import { componentInterface } from '../factory';
|
|
3
3
|
import { getVisitorId, setVisitorId } from '../utils/visitorId';
|
|
4
4
|
import { getVersion } from "../utils/version";
|
|
@@ -63,7 +63,8 @@ export const getApiPromise = (
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// 3. Otherwise, initiate a new API call with timeout.
|
|
66
|
-
const
|
|
66
|
+
const apiEndpoint = options.api_endpoint || DEFAULT_API_ENDPOINT;
|
|
67
|
+
const endpoint = `${apiEndpoint}/thumbmark`;
|
|
67
68
|
const visitorId = getVisitorId();
|
|
68
69
|
const requestBody: any = {
|
|
69
70
|
components,
|
package/src/functions/index.ts
CHANGED
|
@@ -55,9 +55,11 @@ export async function getThumbmark(options?: optionsInterface): Promise<thumbmar
|
|
|
55
55
|
|
|
56
56
|
// Resolve experimental components only when logging
|
|
57
57
|
let experimentalComponents = {};
|
|
58
|
+
let experimentalElapsed = {};
|
|
58
59
|
if (shouldLog || _options.experimental) {
|
|
59
|
-
const { resolvedComponents } = await resolveClientComponents(tm_experimental_component_promises, _options);
|
|
60
|
+
const { elapsed: expElapsed, resolvedComponents } = await resolveClientComponents(tm_experimental_component_promises, _options);
|
|
60
61
|
experimentalComponents = resolvedComponents;
|
|
62
|
+
experimentalElapsed = expElapsed;
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
const apiPromise = _options.api_key ? getApiPromise(_options, clientComponentsResult) : null;
|
|
@@ -82,7 +84,8 @@ export async function getThumbmark(options?: optionsInterface): Promise<thumbmar
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
// Only add 'elapsed' if performance is true
|
|
85
|
-
const
|
|
87
|
+
const allElapsed = { ...elapsed, ...experimentalElapsed };
|
|
88
|
+
const maybeElapsed = _options.performance ? { elapsed: allElapsed } : {};
|
|
86
89
|
const apiComponents = filterThumbmarkData(apiResult?.components || {}, _options);
|
|
87
90
|
const components = {...clientComponentsResult, ...apiComponents};
|
|
88
91
|
const info: infoInterface = apiResult?.info || { uniqueness: { score: 'api only' } };
|
package/src/options.ts
CHANGED
|
@@ -5,13 +5,14 @@ export interface optionsInterface {
|
|
|
5
5
|
timeout?: number,
|
|
6
6
|
logging?: boolean,
|
|
7
7
|
api_key?: string,
|
|
8
|
+
api_endpoint?: string,
|
|
8
9
|
cache_api_call?: boolean,
|
|
9
10
|
performance?: boolean,
|
|
10
11
|
stabilize?: string[],
|
|
11
12
|
experimental?: boolean,
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export const
|
|
15
|
+
export const DEFAULT_API_ENDPOINT = 'https://api.thumbmarkjs.com';
|
|
15
16
|
|
|
16
17
|
export const defaultOptions: optionsInterface = {
|
|
17
18
|
exclude: [],
|
|
@@ -46,22 +47,14 @@ export const stabilizationExclusionRules = {
|
|
|
46
47
|
'iframe': [
|
|
47
48
|
{
|
|
48
49
|
exclude: [
|
|
49
|
-
'permissions.camera',
|
|
50
|
-
'permission.geolocation',
|
|
51
|
-
'permissions.microphone',
|
|
52
50
|
'system.applePayVersion',
|
|
53
51
|
'system.cookieEnabled',
|
|
54
52
|
],
|
|
55
53
|
browsers: ['safari']
|
|
56
54
|
},
|
|
57
55
|
{
|
|
58
|
-
exclude: [
|
|
59
|
-
'permissions.background-fetch',
|
|
60
|
-
'permissions.storage-access',
|
|
61
|
-
],
|
|
62
|
-
browsers: ['chrome', 'brave', 'edge', 'opera']
|
|
56
|
+
exclude: ['permissions']
|
|
63
57
|
}
|
|
64
|
-
|
|
65
58
|
],
|
|
66
59
|
'vpn': [
|
|
67
60
|
{ exclude: ['ip'] },
|
package/src/utils/log.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { componentInterface } from '../factory';
|
|
2
|
-
import { optionsInterface } from '../options';
|
|
2
|
+
import { optionsInterface, DEFAULT_API_ENDPOINT } from '../options';
|
|
3
3
|
import { getVersion } from './version';
|
|
4
|
-
import { API_ENDPOINT } from '../options';
|
|
5
4
|
|
|
6
5
|
// ===================== Logging (Internal) =====================
|
|
7
6
|
|
|
@@ -11,7 +10,8 @@ import { API_ENDPOINT } from '../options';
|
|
|
11
10
|
* @internal
|
|
12
11
|
*/
|
|
13
12
|
export async function logThumbmarkData(thisHash: string, thumbmarkData: componentInterface, options: optionsInterface, experimentalData: componentInterface = {}): Promise<void> {
|
|
14
|
-
const
|
|
13
|
+
const apiEndpoint = DEFAULT_API_ENDPOINT;
|
|
14
|
+
const url = `${apiEndpoint}/log`;
|
|
15
15
|
const payload = {
|
|
16
16
|
thumbmark: thisHash,
|
|
17
17
|
components: thumbmarkData,
|