@reservamos/browser-analytics 0.1.3 → 0.1.4-alpha.10

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 (53) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +23 -11
  3. package/dist/browser-analytics.cjs +3 -3
  4. package/dist/browser-analytics.d.ts +478 -13
  5. package/dist/browser-analytics.esm.js +12545 -12412
  6. package/dist/browser-analytics.iife.js +24 -24
  7. package/package.json +1 -1
  8. package/src/constants/ProductTypes.ts +6 -0
  9. package/src/events/identify/identify.ts +83 -0
  10. package/src/events/identify/identifySchema.ts +15 -0
  11. package/src/events/identify/index.ts +3 -0
  12. package/src/events/interestInHome/index.ts +7 -0
  13. package/src/events/interestInHome/interestInHomeSchema.ts +11 -0
  14. package/src/events/interestInHome/trackInterestInHome.ts +14 -0
  15. package/src/events/interestInSearch/index.ts +7 -0
  16. package/src/events/interestInSearch/interestInSearchSchema.ts +10 -0
  17. package/src/events/interestInSearch/trackInterestInSearch.ts +14 -0
  18. package/src/events/passengersCreated/index.ts +7 -0
  19. package/src/events/passengersCreated/passengersCreatedSchema.ts +56 -0
  20. package/src/events/passengersCreated/trackPassengersCreated.ts +10 -0
  21. package/src/events/paymentAttempt/index.ts +7 -0
  22. package/src/events/paymentAttempt/paymentAttemptSchema.ts +53 -0
  23. package/src/events/paymentAttempt/trackPaymentAttempt.ts +10 -0
  24. package/src/events/pickedDeparture/index.ts +7 -0
  25. package/src/events/pickedDeparture/pickedDepartureSchema.ts +40 -0
  26. package/src/events/pickedDeparture/trackPickedDeparture.ts +14 -0
  27. package/src/events/purchaseAttempt/index.ts +7 -0
  28. package/src/events/purchaseAttempt/purchaseAttemptSchema.ts +79 -0
  29. package/src/events/purchaseAttempt/trackPurchaseAttempt.ts +14 -0
  30. package/src/events/search/index.ts +7 -0
  31. package/src/events/search/searchSchema.ts +31 -0
  32. package/src/events/search/trackSearch.ts +14 -0
  33. package/src/events/seatChange/index.ts +7 -0
  34. package/src/events/seatChange/seatChangeSchema.ts +33 -0
  35. package/src/events/seatChange/trackSeatChange.ts +10 -0
  36. package/src/events/test/index.ts +3 -0
  37. package/src/events/{trackTest.ts → test/trackTest.ts} +4 -2
  38. package/src/events/viewResults/index.ts +7 -0
  39. package/src/events/viewResults/trackViewResults.ts +14 -0
  40. package/src/events/viewResults/viewResultsSchema.ts +29 -0
  41. package/src/index.ts +47 -8
  42. package/src/init.ts +6 -8
  43. package/src/{fingerprint.ts → services/fingerprint.ts} +11 -3
  44. package/src/services/mixpanel.ts +89 -0
  45. package/src/services/validator.ts +154 -0
  46. package/src/track.ts +8 -15
  47. package/src/util/dateValidation.ts +26 -0
  48. package/src/util/productValidation.ts +10 -0
  49. package/src/util/userFingerprintValidation.ts +9 -0
  50. package/src/events/event_schema/PaymentFailedEventSchema.ts +0 -247
  51. package/src/events/event_schema/PurchaseCompleteEventSchema.ts +0 -62
  52. package/src/events/event_schema/validationSchema.ts +0 -135
  53. package/src/mixpanel.ts +0 -36
@@ -0,0 +1,14 @@
1
+ import type { ViewResultsProps } from './viewResultsSchema';
2
+ import { trackEvent } from '@/track';
3
+
4
+ const EVENT_NAME = 'View Results';
5
+
6
+ /**
7
+ * Tracks a View Results event.
8
+ * @param {ViewResultsProps} eventData - The data associated with the View Results event.
9
+ */
10
+ function trackViewResults(eventData: ViewResultsProps) {
11
+ trackEvent(EVENT_NAME, eventData);
12
+ }
13
+
14
+ export default trackViewResults;
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod';
2
+ import dateValidation from '@/util/dateValidation';
3
+ import productValidation from '@/util/productValidation';
4
+
5
+ const viewResultsSchema = z.object({
6
+ 'Bus count': z.number().int('Bus count must be an integer'),
7
+ 'Departure': z.string().refine(dateValidation, {
8
+ message: 'Invalid departure datetime format',
9
+ }),
10
+ 'Departure Delta': z
11
+ .number()
12
+ .int('Departure Delta must be an integer')
13
+ .optional(),
14
+ 'Destination': z.string().min(1, 'Destination is required').optional(),
15
+ 'Destination Terminal': z
16
+ .string()
17
+ .min(1, 'Destination terminal is required')
18
+ .optional(),
19
+ 'Has Frequent Buses': z.boolean().default(false).optional(),
20
+ 'Origin': z.string().min(1, 'Origin is required'),
21
+ 'Origin Terminal': z.string().min(1, 'Origin terminal is required'),
22
+ 'Route': z.string().min(1, 'Route is required'),
23
+ 'product': productValidation,
24
+ });
25
+
26
+ type ViewResultsProps = z.infer<typeof viewResultsSchema>;
27
+
28
+ export type { ViewResultsProps };
29
+ export default viewResultsSchema;
package/src/index.ts CHANGED
@@ -1,12 +1,51 @@
1
- import { trackTest } from './events/trackTest';
2
- import { init } from './init';
1
+ import type { InterestInHomeProps } from '@/events/interestInHome';
2
+ import type { InterestInSearchProps } from '@/events/interestInSearch';
3
+ import type { PassengersCreatedProps } from '@/events/passengersCreated';
4
+ import type { PaymentAttemptProps } from '@/events/paymentAttempt';
5
+ import type { PickedDepartureProps } from '@/events/pickedDeparture';
6
+ import type { PurchaseAttemptProps } from '@/events/purchaseAttempt';
7
+ import type { SearchProps } from '@/events/search';
8
+ import type { SeatChangeProps } from '@/events/seatChange';
9
+ import type { ViewResultsProps } from '@/events/viewResults';
10
+ import identify from '@/events/identify';
11
+ import trackInterestInHome from '@/events/interestInHome';
12
+ import trackInterestInSearch from '@/events/interestInSearch';
13
+ import trackPassengersCreated from '@/events/passengersCreated';
14
+ import trackPaymentAttempt from '@/events/paymentAttempt';
15
+ import trackPickedDeparture from '@/events/pickedDeparture';
16
+ import trackPurchaseAttempt from '@/events/purchaseAttempt';
17
+ import trackSearch from '@/events/search';
18
+ import trackSeatChange from '@/events/seatChange';
19
+ import trackTest from '@/events/test';
20
+ import trackViewResults from '@/events/viewResults';
21
+ import { init } from '@/init';
3
22
 
4
- export { init } from './init';
5
- export { trackTest } from './events/trackTest';
6
-
7
- const tracker = {
23
+ const analytics = {
8
24
  init,
9
- trackTest,
25
+ identify,
26
+ track: {
27
+ test: trackTest,
28
+ search: trackSearch,
29
+ seatChange: trackSeatChange,
30
+ interestInHome: trackInterestInHome,
31
+ interestInSearch: trackInterestInSearch,
32
+ viewResults: trackViewResults,
33
+ passengersCreated: trackPassengersCreated,
34
+ paymentAttempt: trackPaymentAttempt,
35
+ purchaseAttempt: trackPurchaseAttempt,
36
+ pickedDeparture: trackPickedDeparture,
37
+ },
10
38
  };
11
39
 
12
- export default tracker;
40
+ export type {
41
+ PassengersCreatedProps,
42
+ PaymentAttemptProps,
43
+ SearchProps,
44
+ SeatChangeProps,
45
+ ViewResultsProps,
46
+ InterestInHomeProps,
47
+ InterestInSearchProps,
48
+ PurchaseAttemptProps,
49
+ PickedDepartureProps,
50
+ };
51
+ export default analytics;
package/src/init.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import { z } from 'zod';
2
- import validator, {
3
- InitConfigSchema,
4
- } from './events/event_schema/validationSchema';
5
- import { initFingerprint } from './fingerprint'; // Import the new fingerprint initialization function
6
- import { initMixpanel, isMixpanelReady } from './mixpanel'; // Import Mixpanel functions
2
+ import fingerprintService from '@/services/fingerprint';
3
+ import mixpanelService from '@/services/mixpanel';
4
+ import validator, { InitConfigSchema } from './services/validator';
7
5
 
8
6
  /**
9
7
  * Configuration object for initializing the tracking library.
@@ -30,11 +28,11 @@ export async function init(config: InitConfig) {
30
28
 
31
29
  const { mixpanelToken, debug = false, identificationKey } = config;
32
30
 
33
- await initMixpanel(mixpanelToken, debug);
31
+ await mixpanelService.init(mixpanelToken, debug);
34
32
 
35
33
  // Only mixpanel is required to be ready to dispatch the 'Tracker Ready' event
36
34
  try {
37
- await initFingerprint(identificationKey);
35
+ await fingerprintService.initFingerprint(identificationKey);
38
36
  } catch (error) {
39
37
  console.error('Error initializing identification service:', error);
40
38
  }
@@ -50,5 +48,5 @@ export async function init(config: InitConfig) {
50
48
  * @returns {boolean} Returns true if the Mixpanel tracker is ready, otherwise false.
51
49
  */
52
50
  export function isTrackerReady(): boolean {
53
- return isMixpanelReady();
51
+ return mixpanelService.isReady();
54
52
  }
@@ -68,7 +68,7 @@ const setCachedFingerprint = (fingerprint: string): void => {
68
68
  * @returns {Promise<void>} A promise that resolves when initialization is complete.
69
69
  * @throws {Error} Throws an error if initialization fails.
70
70
  */
71
- export async function initFingerprint(apiKey: string): Promise<void> {
71
+ async function initFingerprint(apiKey: string): Promise<void> {
72
72
  window.fingerprintConfig = {};
73
73
  window.fpClient = new FpjsClient({
74
74
  loadOptions: {
@@ -90,7 +90,7 @@ export async function initFingerprint(apiKey: string): Promise<void> {
90
90
  * @returns {Promise<string>} A promise that resolves with the fingerprint.
91
91
  * @throws {Error} Throws an error if retrieval fails.
92
92
  */
93
- export async function getFingerprint(cacheOnly = false): Promise<string> {
93
+ async function getFingerprint(cacheOnly = false): Promise<string> {
94
94
  if (!window.fingerprintConfig) {
95
95
  throw new Error('Fingerprint configuration is not initialized.');
96
96
  }
@@ -126,6 +126,14 @@ export async function getFingerprint(cacheOnly = false): Promise<string> {
126
126
  * and is ready to use by checking if the `fpClient` instance exists on the window object.
127
127
  * @returns {boolean} Returns true if the identification service is ready, otherwise false.
128
128
  */
129
- export function isFingerprintReady(): boolean {
129
+ function isFingerprintReady(): boolean {
130
130
  return window.fpClient !== undefined;
131
131
  }
132
+
133
+ const fingerprintService = {
134
+ initFingerprint,
135
+ getFingerprint,
136
+ isFingerprintReady,
137
+ };
138
+
139
+ export default fingerprintService;
@@ -0,0 +1,89 @@
1
+ import mixpanel from 'mixpanel-browser';
2
+
3
+ declare global {
4
+ interface Window {
5
+ mixpanel: typeof mixpanel;
6
+ }
7
+ }
8
+
9
+ /**
10
+ * Initializes Mixpanel with the provided token and debug flag.
11
+ * @param {string} mixpanelToken - The Mixpanel token used for authenticating API requests.
12
+ * @param {boolean} debug - Optional flag to enable or disable debug mode.
13
+ * @returns {Promise<void>} A promise that resolves when Mixpanel is initialized.
14
+ */
15
+ function init(mixpanelToken: string, debug = false): Promise<void> {
16
+ return new Promise<void>((resolve) => {
17
+ mixpanel.init(mixpanelToken, {
18
+ debug,
19
+ loaded: () => {
20
+ window.mixpanel = mixpanel;
21
+ resolve();
22
+ },
23
+ });
24
+ });
25
+ }
26
+
27
+ /**
28
+ * Identifies a user in Mixpanel and sets their properties.
29
+ * @param {string} userId - The unique identifier for the user.
30
+ * @param {Record<string, unknown>} properties - An object containing user properties to be set.
31
+ * @returns {void}
32
+ * @throws {Error} If Mixpanel is not initialized (handled internally).
33
+ */
34
+ function identify(userId: string, properties: Record<string, unknown>): void {
35
+ if (!isReady()) {
36
+ return;
37
+ }
38
+
39
+ mixpanel.identify(userId);
40
+ mixpanel.people.set(properties);
41
+ }
42
+
43
+ /**
44
+ * Appends a unique value to a property for the currently identified user in Mixpanel.
45
+ * If the property doesn't exist, it creates an array with the value.
46
+ * If the property exists, it adds the value only if it's not already present.
47
+ * @param {string} key - The name of the property to append to.
48
+ * @param {unknown} value - The value to append to the property.
49
+ * @throws {Error} If Mixpanel is not initialized (handled internally).
50
+ */
51
+ function attachProperty(key: string, value: unknown): void {
52
+ if (!isReady()) {
53
+ return;
54
+ }
55
+
56
+ mixpanel.people.union(key, [value]);
57
+ }
58
+
59
+ /**
60
+ * Checks if the Mixpanel tracker is ready.
61
+ * @returns {boolean} Returns true if the Mixpanel tracker is ready, otherwise false.
62
+ */
63
+ function isReady(): boolean {
64
+ return window.mixpanel !== undefined;
65
+ }
66
+ /**
67
+ * Tracks an event in Mixpanel with the given name and properties.
68
+ * @param {string} eventName - The name of the event to track.
69
+ * @param {Record<string, unknown>} properties - An object containing event properties.
70
+ * @returns {void}
71
+ * @throws {Error} If Mixpanel is not initialized (handled internally).
72
+ */
73
+ function track(eventName: string, properties: Record<string, unknown>): void {
74
+ if (!isReady()) {
75
+ return;
76
+ }
77
+
78
+ mixpanel.track(eventName, properties);
79
+ }
80
+
81
+ const mixpanelService = {
82
+ init,
83
+ isReady,
84
+ track,
85
+ identify,
86
+ attachProperty,
87
+ };
88
+
89
+ export default mixpanelService;
@@ -0,0 +1,154 @@
1
+ import { z, ZodError, ZodSchema } from 'zod';
2
+ import IdentifySchema from '@/events/identify/identifySchema';
3
+ import { interestInHomeSchema } from '@/events/interestInHome';
4
+ import { interestInSearchSchema } from '@/events/interestInSearch';
5
+ import { passengersCreatedSchema } from '@/events/passengersCreated';
6
+ import { paymentAttemptSchema } from '@/events/paymentAttempt';
7
+ import { pickedDepartureSchema } from '@/events/pickedDeparture';
8
+ import { purchaseAttemptSchema } from '@/events/purchaseAttempt';
9
+ import { searchSchema } from '@/events/search';
10
+ import { seatChangeSchema } from '@/events/seatChange';
11
+ import { viewResultsSchema } from '@/events/viewResults';
12
+
13
+ const TrackTestEventSchema = z.object({}); // Allow empty object for track test event
14
+ export const InitConfigSchema = z.object({
15
+ /**
16
+ * The Mixpanel token used for authenticating API requests.
17
+ */
18
+ mixpanelToken: z.string().min(1, 'Mixpanel token is required'),
19
+ /**
20
+ * Optional flag to enable or disable debug mode.
21
+ * When set to true, additional debug information will be logged.
22
+ */
23
+ debug: z.boolean().optional(),
24
+ /**
25
+ * Key for tracking user identities.
26
+ */
27
+ identificationKey: z.string().min(1, 'Identification key is required'),
28
+ });
29
+ interface CustomError {
30
+ field: string;
31
+ error_type: string;
32
+ expected: string;
33
+ received: string;
34
+ message: string;
35
+ suggestion: string;
36
+ }
37
+ // Error formatting
38
+ const SchemaErrorFormatter = (error: ZodError): CustomError[] => {
39
+ return error.issues.map((issue) => {
40
+ let error_type = 'INVALID_FIELD';
41
+ let expected = '';
42
+ let received = '';
43
+ let suggestion = '';
44
+
45
+ if (issue.code === 'invalid_type') {
46
+ error_type = 'TYPE_MISMATCH';
47
+ expected = issue.expected;
48
+ received = issue.received;
49
+ suggestion = `Expected ${expected} but received ${received}. Please provide a value of type ${expected}.`;
50
+ } else if (issue.code === 'too_small') {
51
+ error_type = 'VALUE_TOO_SMALL';
52
+ suggestion = `Increase the value to at least ${issue.minimum}.`;
53
+ } else if (issue.code === 'too_big') {
54
+ error_type = 'VALUE_TOO_BIG';
55
+ suggestion = `Reduce the value to no more than ${issue.maximum}.`;
56
+ } else {
57
+ error_type = 'INVALID_FIELD';
58
+ suggestion = `Ensure the field matches the expected format and value type.`;
59
+ }
60
+ return {
61
+ field: issue.path.join('.'),
62
+ error_type,
63
+ expected,
64
+ received,
65
+ message: issue.message,
66
+ suggestion,
67
+ };
68
+ });
69
+ };
70
+
71
+ // Mapping event names to Zod schemas
72
+ const eventSchemas: Record<string, z.ZodSchema> = {
73
+ 'Track Test': TrackTestEventSchema,
74
+ 'Search': searchSchema,
75
+ 'Seat Change': seatChangeSchema,
76
+ 'Interest In Home': interestInHomeSchema,
77
+ 'Passengers Created': passengersCreatedSchema,
78
+ 'Payment Attempt': paymentAttemptSchema,
79
+ 'Interest In Search': interestInSearchSchema,
80
+ 'View Results': viewResultsSchema,
81
+ 'Purchase Attempt': purchaseAttemptSchema,
82
+ 'Picked Departure': pickedDepartureSchema,
83
+ };
84
+
85
+ type EventData = z.infer<(typeof eventSchemas)[keyof typeof eventSchemas]>;
86
+
87
+ /**
88
+ * Validates the event data against the predefined schema for the given event name.
89
+ * @param {string} eventName - The name of the event to validate.
90
+ * @param {EventData} eventData - The data of the event to validate.
91
+ * @throws {Error} - Throws an error if validation fails.
92
+ */
93
+ function parseEventProps(eventName: string, eventData: EventData): void {
94
+ const eventSchema = eventSchemas[eventName];
95
+
96
+ if (!eventSchema) {
97
+ throw { message: `Event ${eventName} not found` };
98
+ }
99
+
100
+ try {
101
+ eventSchema.parse(eventData);
102
+ } catch (error) {
103
+ if (error instanceof ZodError) {
104
+ const errors = SchemaErrorFormatter(error);
105
+ throw { message: 'Schema validation failed', errors };
106
+ }
107
+ throw { message: 'Unknown validation error' };
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Generic function to validate properties against a given schema.
113
+ * @param {object} properties - The properties to validate.
114
+ * @param {ZodSchema} schema - The schema to validate against.
115
+ * @throws {Error} - Throws an error if validation fails.
116
+ */
117
+ function validateProps(properties: object, schema: ZodSchema): void {
118
+ try {
119
+ schema.parse(properties);
120
+ } catch (error) {
121
+ if (error instanceof ZodError) {
122
+ const errors = SchemaErrorFormatter(error);
123
+ throw { message: 'Schema validation failed', errors };
124
+ }
125
+ throw { message: 'Unknown validation error' };
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Validates the initialization properties.
131
+ * @param {object} initProps - The properties to validate for init.
132
+ * @throws {Error} - Throws an error if validation fails.
133
+ */
134
+ function parseInitProps(initProps: object): void {
135
+ validateProps(initProps, InitConfigSchema);
136
+ }
137
+
138
+ /**
139
+ * Validates the properties for user identification.
140
+ * @param {Record<string, unknown>} properties - The properties to validate for user identification.
141
+ * @throws {Error} - Throws an error if validation fails.
142
+ */
143
+ function parseIdentifyProps(properties: Record<string, unknown>): void {
144
+ validateProps(properties, IdentifySchema);
145
+ }
146
+
147
+ const validatorService = {
148
+ parseEventProps,
149
+ parseInitProps,
150
+ parseIdentifyProps,
151
+ };
152
+
153
+ // Export the validator object
154
+ export default validatorService;
package/src/track.ts CHANGED
@@ -1,12 +1,11 @@
1
- import mixpanel from 'mixpanel-browser';
2
- import validator from './events/event_schema/validationSchema';
3
- import { getFingerprint } from './fingerprint';
4
- import { isMixpanelReady } from './mixpanel';
1
+ import fingerprintService from '@/services/fingerprint';
2
+ import mixpanelService from '@/services/mixpanel';
3
+ import validator from '@/services/validator';
5
4
 
6
5
  /**
7
6
  * List of events that trigger the fingerprint to be sent with the event. other eventes will only fetch the cached fingerprint.
8
7
  */
9
- const FP_TRIGGER_EVENTS = ['Track Test'];
8
+ const FP_TRIGGER_EVENTS = ['Search', 'View Results'];
10
9
 
11
10
  /**
12
11
  * Base function to track events with Mixpanel.
@@ -19,20 +18,15 @@ export async function trackEvent(
19
18
  eventName: string,
20
19
  eventProperties: object,
21
20
  ): Promise<void> {
22
- if (!isMixpanelReady()) {
21
+ if (!mixpanelService.isReady()) {
23
22
  throw new Error('Mixpanel is not initialized.');
24
23
  }
25
24
 
26
25
  try {
27
- const validationResult = validator.parseEventProps(
28
- eventName,
29
- eventProperties,
30
- );
31
- console.log('validation schema result===>', validationResult);
32
-
26
+ validator.parseEventProps(eventName, eventProperties);
33
27
  const cacheOnly = !FP_TRIGGER_EVENTS.includes(eventName);
34
28
 
35
- const fingerprint = await getFingerprint(cacheOnly);
29
+ const fingerprint = await fingerprintService.getFingerprint(cacheOnly);
36
30
  const defaultProperties = {
37
31
  'User Fingerprint': fingerprint,
38
32
  };
@@ -42,9 +36,8 @@ export async function trackEvent(
42
36
  ...eventProperties,
43
37
  };
44
38
 
45
- mixpanel.track(eventName, properties);
39
+ mixpanelService.track(eventName, properties);
46
40
  } catch (error) {
47
41
  console.error('Error tracking event:', error);
48
- throw error;
49
42
  }
50
43
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Validates a date string to ensure it matches specific date formats.
3
+ * @param {string} val - The date string to validate.
4
+ * @returns {boolean} True if the date string is valid, false otherwise.
5
+ */
6
+ const dateValidation = (val: string): boolean => {
7
+ // Check for valid date format (YYYY-MM-DD) or datetime format (YYYY-MM-DDTHH:mm:ss)
8
+ const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
9
+ const dateTimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/;
10
+
11
+ // New regex to ensure at least YYYY-MM-DD is provided
12
+ const minimalDateRegex = /^\d{4}-\d{2}-\d{2}/;
13
+
14
+ if (dateRegex.test(val) || dateTimeRegex.test(val)) {
15
+ return true;
16
+ }
17
+
18
+ // Check if the string starts with YYYY-MM-DD and is a valid date
19
+ if (minimalDateRegex.test(val) && !isNaN(Date.parse(val))) {
20
+ return true;
21
+ }
22
+
23
+ return false;
24
+ };
25
+
26
+ export default dateValidation;
@@ -0,0 +1,10 @@
1
+ import { z } from 'zod';
2
+ import PRODUCT_TYPES, { ProductType } from '@/constants/ProductTypes';
3
+
4
+ const productValidation = z
5
+ .enum(PRODUCT_TYPES)
6
+ .refine((val): val is ProductType => PRODUCT_TYPES.includes(val), {
7
+ message: `Product must be one of: ${PRODUCT_TYPES.join(', ')}`,
8
+ });
9
+
10
+ export default productValidation;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Validates the user fingerprint value.
3
+ * @param {null | string} val - The user fingerprint value to validate.
4
+ * @returns {boolean} True if the value is either null or a string, false otherwise.
5
+ */
6
+ const userFingerPrintValidation = (val: null | string) =>
7
+ val === null || typeof val === 'string';
8
+
9
+ export default userFingerPrintValidation;