@thumbmarkjs/thumbmarkjs 0.20.6 → 1.0.0-rc.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.
Files changed (32) hide show
  1. package/dist/thumbmark.cjs.js +1 -1
  2. package/dist/thumbmark.cjs.js.map +1 -1
  3. package/dist/thumbmark.esm.d.ts +110 -16
  4. package/dist/thumbmark.esm.js +1 -1
  5. package/dist/thumbmark.esm.js.map +1 -1
  6. package/dist/thumbmark.umd.js +1 -1
  7. package/dist/thumbmark.umd.js.map +1 -1
  8. package/package.json +7 -2
  9. package/src/components/audio/{audio.ts → index.ts} +11 -8
  10. package/src/components/canvas/{canvas.ts → index.ts} +12 -8
  11. package/src/components/fonts/{fonts.ts → index.ts} +10 -5
  12. package/src/components/hardware/{hardware.ts → index.ts} +3 -5
  13. package/src/components/locales/{locales.ts → index.ts} +2 -4
  14. package/src/components/math/index.ts +28 -0
  15. package/src/components/permissions/{permissions.ts → index.ts} +2 -4
  16. package/src/components/plugins/{plugins.ts → index.ts} +2 -4
  17. package/src/components/screen/{screen.ts → index.ts} +2 -4
  18. package/src/components/system/{system.ts → index.ts} +2 -5
  19. package/src/components/webgl/{webgl.ts → index.ts} +3 -5
  20. package/src/factory.ts +40 -29
  21. package/src/fingerprint/functions.test.ts +12 -7
  22. package/src/fingerprint/functions.ts +248 -118
  23. package/src/fingerprint/legacy_functions.ts +54 -0
  24. package/src/fingerprint/options.ts +19 -8
  25. package/src/index.ts +12 -3
  26. package/src/thumbmark.ts +41 -0
  27. package/src/{declarations.d.ts → types/global.d.ts} +1 -1
  28. package/src/utils/filterComponents.ts +42 -0
  29. package/src/utils/raceAll.ts +0 -2
  30. package/src/components/index.ts +0 -17
  31. package/src/components/math/math.ts +0 -39
  32. /package/src/components/canvas/{canvas.test.ts → index.test.ts} +0 -0
@@ -2,6 +2,7 @@ import { componentInterface, includeComponent } from '../../factory';
2
2
  import { getCommonPixels } from '../../utils/commonPixels';
3
3
  import { hash } from '../../utils/hash';
4
4
  import { getBrowser } from '../system/browser';
5
+ import { optionsInterface } from '../../fingerprint/options';
5
6
 
6
7
  const browser = getBrowser();
7
8
  const name = browser.name.toLowerCase();
@@ -19,7 +20,15 @@ const _RUNS = 3;
19
20
  const _WIDTH = 280;
20
21
  const _HEIGHT = 20;
21
22
 
22
- export default function generateCanvasFingerprint(): Promise<componentInterface> {
23
+ export default async function getCanvas(options?: optionsInterface): Promise<componentInterface | null> {
24
+ const browser = getBrowser()
25
+ if (name !== 'firefox' && !(name === 'safari' && majorVer >= 17))
26
+ return generateCanvasFingerprint()
27
+ return null;
28
+ }
29
+
30
+
31
+ export function generateCanvasFingerprint(): Promise<componentInterface> {
23
32
  const canvas = document.createElement('canvas');
24
33
  const ctx = canvas.getContext('2d');
25
34
 
@@ -33,7 +42,7 @@ export default function generateCanvasFingerprint(): Promise<componentInterface>
33
42
  const commonImageData = getCommonPixels(imageDatas, _WIDTH, _HEIGHT);
34
43
 
35
44
  resolve({
36
- commonImageDataHash: hash(commonImageData.data.toString()).toString(),
45
+ commonPixelsHash: hash(commonImageData.data.toString()).toString(),
37
46
  });
38
47
  });
39
48
  }
@@ -85,9 +94,4 @@ function generateCanvasImageData(): ImageData {
85
94
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
86
95
  // Return data URL of the canvas
87
96
  return imageData;
88
- }
89
-
90
- // In Safari from version 17 in private and normal modes canvas differs
91
- if (name !== 'firefox' && !(name === 'safari' && majorVer === 17)) {
92
- includeComponent('canvas', generateCanvasFingerprint);
93
- }
97
+ }
@@ -1,6 +1,7 @@
1
1
  import { componentInterface, includeComponent } from '../../factory'
2
2
  import { ephemeralIFrame } from '../../utils/ephemeralIFrame'
3
3
  import { getBrowser } from '../system/browser'
4
+ import { optionsInterface } from '../../fingerprint/options'
4
5
 
5
6
  interface FontMetrics {[k: string]: number}
6
7
 
@@ -98,7 +99,14 @@ const availableFonts = [
98
99
 
99
100
  const baseFonts = ['monospace', 'sans-serif', 'serif'];
100
101
 
101
- export default function getFontMetrics(): Promise<componentInterface> {
102
+ export default async function getFonts(options?: optionsInterface): Promise<componentInterface | null> {
103
+ const browser = getBrowser()
104
+ if (!['Firefox'].includes(browser.name))
105
+ return getFontMetrics()
106
+ return null;
107
+ }
108
+
109
+ export function getFontMetrics(): Promise<componentInterface> {
102
110
 
103
111
  return new Promise((resolve, reject) => {
104
112
  try {
@@ -139,7 +147,4 @@ function measureSingleFont(ctx: CanvasRenderingContext2D | null, font: string):
139
147
  const defaultFont = ctx.font; // Store default font
140
148
  ctx.font = `72px ${font}`; // Set a default font size
141
149
  return ctx.measureText(text).width;
142
- }
143
-
144
- if (getBrowser().name != 'Firefox')
145
- includeComponent('fonts', getFontMetrics);
150
+ }
@@ -1,8 +1,8 @@
1
1
  import { componentInterface, includeComponent } from '../../factory'
2
2
 
3
- function getHardwareInfo(): Promise<componentInterface> {
3
+ export default function getHardware(): Promise<componentInterface> {
4
4
  return new Promise((resolve, reject) => {
5
- const deviceMemory = (navigator.deviceMemory !== undefined) ? navigator.deviceMemory : 0
5
+ const deviceMemory = ((navigator as any).deviceMemory !== undefined) ? (navigator as any).deviceMemory : 0
6
6
  const memoryInfo = (window.performance && (window.performance as any).memory ) ? (window.performance as any).memory : 0
7
7
  resolve(
8
8
  {
@@ -64,6 +64,4 @@ function getArchitecture(): number {
64
64
  f[0] = f[0] - f[0];
65
65
 
66
66
  return u8[3];
67
- }
68
-
69
- includeComponent('hardware', getHardwareInfo);
67
+ }
@@ -1,6 +1,6 @@
1
1
  import { componentInterface, includeComponent } from '../../factory'
2
2
 
3
- function getLocales(): Promise<componentInterface> {
3
+ export default function getLocales(): Promise<componentInterface> {
4
4
  return new Promise((resolve) => {
5
5
  resolve(
6
6
  {
@@ -18,6 +18,4 @@ function getUserLanguage(): string {
18
18
 
19
19
  function getUserTimezone(): string {
20
20
  return Intl.DateTimeFormat().resolvedOptions().timeZone;
21
- }
22
-
23
- includeComponent('locales', getLocales);
21
+ }
@@ -0,0 +1,28 @@
1
+ import { componentInterface, includeComponent } from '../../factory'
2
+
3
+ const integrate = (f: (x: number) => number, a: number, b: number, n: number): number => {
4
+ const h = (b - a) / n;
5
+ let sum = 0;
6
+ for (let i = 0; i < n; i++) {
7
+ const x = a + (i + 0.5) * h;
8
+ sum += f(x);
9
+ }
10
+ return sum * h;
11
+ };
12
+
13
+ export default function getMath(): Promise<componentInterface> {
14
+ return new Promise((resolve) => {
15
+ resolve(
16
+ {
17
+ 'acos': Math.acos(0.5),
18
+ 'asin': integrate(Math.asin, -1, 1, 97),
19
+ 'cos': integrate(Math.cos, 0, Math.PI, 97),
20
+ 'largeCos': Math.cos(1e20),
21
+ 'largeSin': Math.sin(1e20),
22
+ 'largeTan': Math.tan(1e20),
23
+ 'sin': integrate(Math.sin, -Math.PI, Math.PI, 97),
24
+ 'tan': integrate(Math.tan, 0, 2 * Math.PI, 97),
25
+ }
26
+ );
27
+ });
28
+ }
@@ -28,7 +28,7 @@ function initializePermissionKeys() {
28
28
  ] as PermissionName[];
29
29
  }
30
30
 
31
- export default async function getBrowserPermissions(): Promise<componentInterface> {
31
+ export default async function getPermissions(): Promise<componentInterface> {
32
32
  initializePermissionKeys();
33
33
  const browser = getBrowser();
34
34
  if (browser.name.toLowerCase() === 'safari') { // removing from Safari due to iFrame handling
@@ -58,6 +58,4 @@ async function getBrowserPermissionsOnce(): Promise<componentInterface> {
58
58
  }
59
59
 
60
60
  return permissionStatus;
61
- }
62
-
63
- includeComponent("permissions", getBrowserPermissions);
61
+ }
@@ -1,6 +1,6 @@
1
1
  import { componentInterface, includeComponent } from '../../factory'
2
2
 
3
- export default function getInstalledPlugins(): Promise<componentInterface> {
3
+ export default function getPlugins(): Promise<componentInterface> {
4
4
  const plugins: string[] = [];
5
5
 
6
6
  if (navigator.plugins) {
@@ -17,6 +17,4 @@ export default function getInstalledPlugins(): Promise<componentInterface> {
17
17
  }
18
18
  );
19
19
  });
20
- }
21
-
22
- includeComponent('plugins', getInstalledPlugins);
20
+ }
@@ -1,6 +1,6 @@
1
1
  import { componentInterface, includeComponent } from '../../factory';
2
2
 
3
- function screenDetails(): Promise<componentInterface> {
3
+ export default function getScreen(): Promise<componentInterface> {
4
4
  return new Promise((resolve) => {
5
5
  resolve(
6
6
  {
@@ -41,6 +41,4 @@ function matchMedias(): string[] {
41
41
  })
42
42
  });
43
43
  return results;
44
- }
45
-
46
- includeComponent('screen', screenDetails);
44
+ }
@@ -1,7 +1,7 @@
1
1
  import { componentInterface, includeComponent } from '../../factory';
2
2
  import { getBrowser } from './browser'
3
3
 
4
- function getSystemDetails(): Promise<componentInterface> {
4
+ export default function getSystem(): Promise<componentInterface> {
5
5
  return new Promise((resolve) => {
6
6
  const browser = getBrowser()
7
7
 
@@ -39,7 +39,4 @@ function getApplePayVersion(): number {
39
39
  }
40
40
  }
41
41
  return 0
42
- }
43
-
44
- includeComponent('system', getSystemDetails);
45
-
42
+ }
@@ -16,7 +16,7 @@ function initializeCanvasAndWebGL() {
16
16
  }
17
17
  }
18
18
 
19
- async function createWebGLFingerprint(): Promise<componentInterface> {
19
+ export default async function getWebGL(): Promise<componentInterface> {
20
20
  initializeCanvasAndWebGL();
21
21
 
22
22
  try {
@@ -32,7 +32,7 @@ async function createWebGLFingerprint(): Promise<componentInterface> {
32
32
  //const imageData = createWebGLImageData()
33
33
 
34
34
  return {
35
- 'commonImageHash': hash(commonImageData.data.toString()).toString(),
35
+ 'commonPixelsHash': hash(commonImageData.data.toString()).toString(),
36
36
  }
37
37
  } catch (error) {
38
38
  return {
@@ -143,6 +143,4 @@ function createWebGLImageData(): ImageData {
143
143
  gl.clearColor(0.0, 0.0, 0.0, 0.0);
144
144
  }
145
145
  }
146
- }
147
-
148
- includeComponent('webgl', createWebGLFingerprint);
146
+ }
package/src/factory.ts CHANGED
@@ -4,21 +4,53 @@
4
4
  *
5
5
  */
6
6
 
7
- import { options, optionsInterface } from './fingerprint/options';
7
+ import { optionsInterface } from './fingerprint/options';
8
+ //import { getComponentPromises } from './fingerprint/tm_functions';
9
+
10
+ // Import all built-in component functions
11
+ import getAudio from "./components/audio";
12
+ import getCanvas from "./components/canvas";
13
+ import getFonts from "./components/fonts";
14
+ import getHardware from "./components/hardware";
15
+ import getLocales from "./components/locales";
16
+ import getMath from "./components/math";
17
+ import getPermissions from "./components/permissions";
18
+ import getPlugins from "./components/plugins";
19
+ import getScreen from "./components/screen";
20
+ import getSystem from "./components/system";
21
+ import getWebGL from "./components/webgl";
22
+
23
+ /**
24
+ * @description key->function map of built-in components. Do not call the function here.
25
+ */
26
+ export const tm_component_promises = {
27
+ 'audio': getAudio,
28
+ 'canvas': getCanvas,
29
+ 'fonts': getFonts,
30
+ 'hardware': getHardware,
31
+ 'locales': getLocales,
32
+ 'math': getMath,
33
+ 'permissions': getPermissions,
34
+ 'plugins': getPlugins,
35
+ 'screen': getScreen,
36
+ 'system': getSystem,
37
+ 'webgl': getWebGL
38
+ };
8
39
 
9
40
  // the component interface is the form of the JSON object the function's promise must return
10
41
  export interface componentInterface {
11
42
  [key: string]: string | string[] | number | boolean | componentInterface;
12
- }
43
+ };
13
44
 
14
45
 
15
46
  // The component function's interface is simply the promise of the above
16
47
  export interface componentFunctionInterface {
17
- (): Promise<componentInterface>;
48
+ (options?: optionsInterface): Promise<componentInterface | null>;
18
49
  }
19
50
 
20
51
  // components include a dictionary of name: function.
21
- export const components: {[name: string]: componentFunctionInterface} = {};
52
+ // Renamed to customComponents for clarity; this is for user-registered components.
53
+ export const customComponents: {[name: string]: componentFunctionInterface | null} = {};
22
54
 
23
55
  //In case a promise time-outs, this is what we use as the value in place
24
56
  export const timeoutInstance: componentInterface = {
@@ -30,29 +62,8 @@ export const timeoutInstance: componentInterface = {
30
62
  * in the fingerprint.
31
63
  * @param {string} name - the name identifier of the component
32
64
  * @param {componentFunctionInterface} creationFunction - the function that implements the component
33
- * @returns
65
+ * @returns nothing
34
66
  */
35
- export const includeComponent = (name:string, creationFunction: componentFunctionInterface) => {
36
- if (typeof window !== 'undefined')
37
- components[name] = creationFunction;
38
- }
39
-
40
- /**
41
- * The function turns the map of component functions to a map of Promises when called
42
- * @returns {[name: string]: <Promise>componentInterface}
43
- */
44
- export const getComponentPromises = () => {
45
- return Object.fromEntries(
46
- Object.entries(components)
47
- .filter(([key]) => {
48
- return !options?.exclude?.includes(key)}
49
- )
50
- .filter(([key]) => {
51
- return options?.include?.some(e => e.includes('.'))
52
- ? options?.include?.some(e => e.startsWith(key))
53
- : options?.include?.length === 0 || options?.include?.includes(key)
54
- }
55
- )
56
- .map(([key, value]) => [key, value()])
57
- );
58
- }
67
+ export const includeComponent = (name:string, creationFunction: componentFunctionInterface, options?: optionsInterface) => {
68
+ customComponents[name] = creationFunction;
69
+ };
@@ -1,5 +1,6 @@
1
- import {componentInterface} from '../factory'
2
- import {filterFingerprintData} from './functions'
1
+ import { componentInterface } from '../factory'
2
+ import { filterThumbmarkData } from '../utils/filterComponents'
3
+ import { defaultOptions } from './options';
3
4
 
4
5
  const test_components: componentInterface = {
5
6
  'one': '1',
@@ -9,26 +10,30 @@ const test_components: componentInterface = {
9
10
 
10
11
  describe('component filtering tests', () => {
11
12
  test("excluding top level works", () => {
12
- expect(filterFingerprintData(test_components, ['one'], [])).toMatchObject({
13
+ expect(filterThumbmarkData(test_components,
14
+ { ...defaultOptions, ...{ exclude: ['one']} }
15
+ )).toMatchObject({
13
16
  'two': 2, 'three': {'a': true, 'b': false}
14
17
  })
15
18
  });
16
19
  test("including top level works", () => {
17
- expect(filterFingerprintData(test_components, [], ['one', 'two'])).toMatchObject({
20
+ expect(filterThumbmarkData(test_components, { ...defaultOptions, ...{ include: ['one', 'two']} })).toMatchObject({
18
21
  'one': '1', 'two': 2
19
22
  })
20
23
  });
21
24
  test("excluding low-level works", () => {
22
- expect(filterFingerprintData(test_components, ['two', 'three.a'], [])).toMatchObject({
25
+ expect(filterThumbmarkData(test_components,
26
+ { ...defaultOptions, ...{ exclude: ['two', 'three.a']} }
27
+ )).toMatchObject({
23
28
  'one': '1',
24
29
  'three': {'b': false}
25
30
  })
26
31
  });
27
32
  test("including low-level works", () => {
28
- expect(filterFingerprintData(test_components, [], ['one', 'three.b'])).toMatchObject({
33
+ expect(filterThumbmarkData(test_components,
34
+ { ...defaultOptions, ...{ include: ['one', 'three.b']} })).toMatchObject({
29
35
  'one': '1',
30
36
  'three': {'b': false}
31
37
  })
32
38
  });
33
-
34
39
  });