@thumbmarkjs/thumbmarkjs 1.4.0 → 1.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thumbmarkjs/thumbmarkjs",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/thumbmark.cjs.js",
@@ -67,7 +67,7 @@ export const getApiPromise = (
67
67
  // 3. Otherwise, initiate a new API call with timeout.
68
68
  const apiEndpoint = options.api_endpoint || DEFAULT_API_ENDPOINT;
69
69
  const endpoint = `${apiEndpoint}/thumbmark`;
70
- const visitorId = getVisitorId();
70
+ const visitorId = getVisitorId(options);
71
71
  const requestBody: any = {
72
72
  components,
73
73
  options,
@@ -100,7 +100,7 @@ export const getApiPromise = (
100
100
  .then(data => {
101
101
  // Handle visitor ID from server response
102
102
  if (data.visitorId && data.visitorId !== visitorId) {
103
- setVisitorId(data.visitorId);
103
+ setVisitorId(data.visitorId, options);
104
104
  }
105
105
  apiPromiseResult = data; // Cache the successful result
106
106
  currentApiPromise = null; // Clear the in-flight promise
package/src/options.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export interface optionsInterface {
2
+ storage_property_name: string,
2
3
  exclude?: string[],
3
4
  include?: string[],
4
5
  permissions_to_check?: PermissionName[],
@@ -23,9 +24,10 @@ export const defaultOptions: optionsInterface = {
23
24
  cache_api_call: true,
24
25
  performance: false,
25
26
  experimental: false,
26
- };
27
+ storage_property_name: 'thumbmark_visitor_id',
28
+ };
27
29
 
28
- export let options = {...defaultOptions};
30
+ export let options = { ...defaultOptions };
29
31
  /**
30
32
  *
31
33
  * @param key @deprecated this function will be removed
@@ -37,12 +39,12 @@ export function setOption<K extends keyof optionsInterface>(key: K, value: optio
37
39
 
38
40
  export const stabilizationExclusionRules = {
39
41
  'private': [
40
- { exclude: ['canvas'], browsers: ['firefox', 'safari>=17', 'brave' ]},
41
- { exclude: ['audio'], browsers: ['samsungbrowser', 'safari' ]},
42
- { exclude: ['fonts'], browsers: ['firefox']},
43
- { exclude: ['audio.sampleHash', 'hardware.deviceMemory', 'header.acceptLanguage.q', 'system.hardwareConcurrency', 'plugins'], browsers: ['brave']},
44
- { exclude: ['tls.extensions'], browsers: ['firefox', 'chrome', 'safari']},
45
- { exclude: ['header.acceptLanguage'], browsers: ['edge', 'chrome']},
42
+ { exclude: ['canvas'], browsers: ['firefox', 'safari>=17', 'brave'] },
43
+ { exclude: ['audio'], browsers: ['samsungbrowser', 'safari'] },
44
+ { exclude: ['fonts'], browsers: ['firefox'] },
45
+ { exclude: ['audio.sampleHash', 'hardware.deviceMemory', 'header.acceptLanguage.q', 'system.hardwareConcurrency', 'plugins'], browsers: ['brave'] },
46
+ { exclude: ['tls.extensions'], browsers: ['firefox', 'chrome', 'safari'] },
47
+ { exclude: ['header.acceptLanguage'], browsers: ['edge', 'chrome'] },
46
48
  ],
47
49
  'iframe': [
48
50
  {
@@ -0,0 +1,93 @@
1
+ import { getVisitorId, setVisitorId } from './visitorId';
2
+ import { optionsInterface, defaultOptions } from '../options';
3
+
4
+ describe('visitorId storage tests', () => {
5
+ beforeEach(() => {
6
+ // Clear localStorage before each test to ensure isolation
7
+ localStorage.clear();
8
+ });
9
+
10
+ describe('storage_property_name option', () => {
11
+ test('should use default storage property name', () => {
12
+ const visitorId = 'test-visitor-123';
13
+ const options = { ...defaultOptions };
14
+
15
+ setVisitorId(visitorId, options);
16
+
17
+ // Verify it was stored with the default property name
18
+ expect(localStorage.getItem('thumbmark_visitor_id')).toBe(visitorId);
19
+ expect(getVisitorId(options)).toBe(visitorId);
20
+ });
21
+
22
+ test('should use custom storage property name', () => {
23
+ const visitorId = 'custom-visitor-456';
24
+ const customOptions: optionsInterface = {
25
+ ...defaultOptions,
26
+ storage_property_name: 'my_custom_visitor_key'
27
+ };
28
+
29
+ setVisitorId(visitorId, customOptions);
30
+
31
+ // Verify it was stored with the custom property name
32
+ expect(localStorage.getItem('my_custom_visitor_key')).toBe(visitorId);
33
+ expect(getVisitorId(customOptions)).toBe(visitorId);
34
+
35
+ // Verify it's NOT in the default location
36
+ expect(localStorage.getItem('thumbmark_visitor_id')).toBeNull();
37
+ });
38
+
39
+ test('should return null when storage property does not exist', () => {
40
+ const options: optionsInterface = {
41
+ ...defaultOptions,
42
+ storage_property_name: 'nonexistent_key'
43
+ };
44
+
45
+ expect(getVisitorId(options)).toBeNull();
46
+ });
47
+
48
+ test('should overwrite existing value for same storage property', () => {
49
+ const oldVisitorId = 'old-visitor';
50
+ const newVisitorId = 'new-visitor';
51
+ const options = { ...defaultOptions };
52
+
53
+ setVisitorId(oldVisitorId, options);
54
+ expect(getVisitorId(options)).toBe(oldVisitorId);
55
+
56
+ setVisitorId(newVisitorId, options);
57
+ expect(getVisitorId(options)).toBe(newVisitorId);
58
+ });
59
+ });
60
+
61
+ describe('error handling', () => {
62
+ test('should handle localStorage.getItem errors gracefully', () => {
63
+ const options = { ...defaultOptions };
64
+
65
+ // Mock localStorage.getItem to throw an error
66
+ const originalGetItem = localStorage.getItem;
67
+ localStorage.getItem = jest.fn(() => {
68
+ throw new Error('Storage quota exceeded');
69
+ });
70
+
71
+ expect(getVisitorId(options)).toBeNull();
72
+
73
+ // Restore original implementation
74
+ localStorage.getItem = originalGetItem;
75
+ });
76
+
77
+ test('should handle localStorage.setItem errors gracefully', () => {
78
+ const options = { ...defaultOptions };
79
+
80
+ // Mock localStorage.setItem to throw an error
81
+ const originalSetItem = localStorage.setItem;
82
+ localStorage.setItem = jest.fn(() => {
83
+ throw new Error('Storage quota exceeded');
84
+ });
85
+
86
+ // Should not throw error
87
+ expect(() => setVisitorId('test-id', options)).not.toThrow();
88
+
89
+ // Restore original implementation
90
+ localStorage.setItem = originalSetItem;
91
+ });
92
+ });
93
+ });
@@ -1,15 +1,15 @@
1
+ import { optionsInterface } from "../options";
2
+
1
3
  /**
2
4
  * Visitor ID storage utilities - localStorage only, server generates IDs
3
5
  */
4
6
 
5
- const VISITOR_ID_KEY = 'thumbmark_visitor_id';
6
-
7
7
  /**
8
8
  * Gets visitor ID from localStorage, returns null if unavailable
9
9
  */
10
- export function getVisitorId(): string | null {
10
+ export function getVisitorId(_options: optionsInterface): string | null {
11
11
  try {
12
- return localStorage.getItem(VISITOR_ID_KEY);
12
+ return localStorage.getItem(_options.storage_property_name);
13
13
  } catch {
14
14
  return null;
15
15
  }
@@ -18,9 +18,9 @@ export function getVisitorId(): string | null {
18
18
  /**
19
19
  * Sets visitor ID in localStorage
20
20
  */
21
- export function setVisitorId(visitorId: string): void {
21
+ export function setVisitorId(visitorId: string, _options: optionsInterface): void {
22
22
  try {
23
- localStorage.setItem(VISITOR_ID_KEY, visitorId);
23
+ localStorage.setItem(_options.storage_property_name, visitorId);
24
24
  } catch {
25
25
  // Ignore storage errors
26
26
  }