user-analytics-tracker 2.2.0 → 3.0.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/CHANGELOG.md CHANGED
@@ -1,3 +1,41 @@
1
+ # [3.0.0](https://github.com/switch-org/analytics-tracker/compare/v2.2.0...v3.0.0) (2025-12-30)
2
+
3
+
4
+ ### Features
5
+
6
+ * optimize essential mode with deduplication and reduced deviceInfo fields ([b64eefa](https://github.com/switch-org/analytics-tracker/commit/b64eefa4992e0570b1011fa029d0db8de89c321b))
7
+
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ * Essential mode deviceInfo now stores only 8 fields instead of 15
12
+
13
+ ## Changes
14
+
15
+ ### Core Optimizations
16
+ - Reduce deviceInfo essential fields from 15 to 8
17
+ - Implement automatic deduplication between location and customData.ipLocation
18
+ - Remove duplicate fields from location object
19
+ - Add null/undefined value removal from filtered objects
20
+ - Improve nested object handling
21
+
22
+ ### Field Storage Improvements
23
+ - Add generic field storage transformer for all data types
24
+ - Support essential/all/custom modes for all data types
25
+ - Implement recursive null value cleaning
26
+ - Add mode-aware deduplication logic
27
+
28
+ ### Documentation
29
+ - Merge and consolidate essential mode documentation
30
+ - Add comprehensive essential-mode-guide.md
31
+ - Update documentation index
32
+ - Add example payload JSON
33
+
34
+ ## Impact
35
+ - Significantly smaller payloads (~30-40% reduction)
36
+ - No duplicate data storage
37
+ - All crucial information preserved
38
+
1
39
  # [2.2.0](https://github.com/switch-org/analytics-tracker/compare/v2.1.0...v2.2.0) (2025-12-30)
2
40
 
3
41
 
package/README.md CHANGED
@@ -93,6 +93,21 @@ const analytics = useAnalytics({
93
93
 
94
94
  // Metrics configuration
95
95
  enableMetrics: false, // Enable metrics collection (default: false)
96
+
97
+ // Field storage configuration (optional) - control which fields are stored
98
+ fieldStorage: {
99
+ ipLocation: { mode: 'essential' }, // IP location fields
100
+ deviceInfo: { mode: 'essential' }, // Device info fields
101
+ networkInfo: { mode: 'essential' }, // Network info fields
102
+ location: { mode: 'essential' }, // Location fields
103
+ attribution: { mode: 'essential' }, // Attribution fields
104
+ // Each can be: 'essential' (default) | 'all' | 'custom'
105
+ // For 'custom': specify fields array
106
+ // For 'all': specify exclude array
107
+ },
108
+
109
+ // Legacy: IP Location storage (backward compatible)
110
+ ipLocationFields: { mode: 'essential' },
96
111
  },
97
112
  });
98
113
  ```
package/dist/index.cjs.js CHANGED
@@ -4,6 +4,97 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var react = require('react');
6
6
 
7
+ /**
8
+ * Core types for the analytics tracker package
9
+ */
10
+ /**
11
+ * Default essential fields for IP location storage
12
+ * These fields are stored when mode is 'essential' (default)
13
+ */
14
+ const DEFAULT_ESSENTIAL_IP_FIELDS = [
15
+ // Core identification
16
+ 'ip',
17
+ 'country',
18
+ 'countryCode',
19
+ 'region',
20
+ 'city',
21
+ // Geographic coordinates (stored here, not duplicated in location)
22
+ 'lat',
23
+ 'lon',
24
+ // Additional geographic info
25
+ 'continent',
26
+ 'continentCode',
27
+ // Network info
28
+ 'type',
29
+ 'isEu',
30
+ 'isp',
31
+ 'connection',
32
+ 'connection.asn',
33
+ 'connection.org',
34
+ 'connection.isp',
35
+ 'connection.domain',
36
+ // Timezone (stored here, not duplicated in location)
37
+ 'timezone',
38
+ 'timezoneDetails',
39
+ 'timezoneDetails.id',
40
+ 'timezoneDetails.abbr',
41
+ 'timezoneDetails.utc',
42
+ // Flag (only emoji in essential mode)
43
+ 'flag.emoji',
44
+ ];
45
+ /**
46
+ * Default essential fields for Device Info storage
47
+ */
48
+ const DEFAULT_ESSENTIAL_DEVICE_FIELDS = [
49
+ 'type',
50
+ 'os',
51
+ 'osVersion',
52
+ 'browser',
53
+ 'browserVersion',
54
+ 'deviceModel',
55
+ 'deviceBrand',
56
+ 'userAgent',
57
+ ];
58
+ /**
59
+ * Default essential fields for Network Info storage
60
+ */
61
+ const DEFAULT_ESSENTIAL_NETWORK_FIELDS = [
62
+ 'type',
63
+ 'effectiveType',
64
+ 'downlink',
65
+ 'rtt',
66
+ 'saveData',
67
+ ];
68
+ /**
69
+ * Default essential fields for Location Info storage
70
+ */
71
+ const DEFAULT_ESSENTIAL_LOCATION_FIELDS = [
72
+ // Minimal location fields - only coordinates and source
73
+ // All IP-related data (ip, country, city, etc.) is stored in customData.ipLocation to avoid duplication
74
+ 'lat',
75
+ 'lon',
76
+ 'source',
77
+ 'ts',
78
+ ];
79
+ /**
80
+ * Default essential fields for Attribution Info storage
81
+ */
82
+ const DEFAULT_ESSENTIAL_ATTRIBUTION_FIELDS = [
83
+ 'landingUrl',
84
+ 'path',
85
+ 'hostname',
86
+ 'referrerUrl',
87
+ 'referrerDomain',
88
+ 'navigationType',
89
+ 'isReload',
90
+ 'isBackForward',
91
+ 'utm_source',
92
+ 'utm_medium',
93
+ 'utm_campaign',
94
+ 'utm_term',
95
+ 'utm_content',
96
+ ];
97
+
7
98
  /**
8
99
  * Network Type Detector
9
100
  * Detects WiFi, Mobile Data (Cellular), Hotspot, Ethernet, or Unknown
@@ -2010,18 +2101,206 @@ class MetricsCollector {
2010
2101
  // Global metrics collector instance
2011
2102
  const metricsCollector = new MetricsCollector();
2012
2103
 
2104
+ /**
2105
+ * Generic field storage transformer
2106
+ * Filters object fields based on storage configuration
2107
+ */
2108
+ /**
2109
+ * Filter object fields based on storage configuration
2110
+ *
2111
+ * @param data - The data object to filter
2112
+ * @param config - Storage configuration
2113
+ * @param defaultEssentialFields - Default essential fields for this data type
2114
+ * @returns Filtered data object with only configured fields
2115
+ */
2116
+ function filterFieldsByConfig(data, config, defaultEssentialFields) {
2117
+ if (!data) {
2118
+ return null;
2119
+ }
2120
+ const mode = config?.mode || 'essential';
2121
+ let fieldsToInclude = [];
2122
+ if (mode === 'essential') {
2123
+ // Use default essential fields
2124
+ fieldsToInclude = [...defaultEssentialFields];
2125
+ }
2126
+ else if (mode === 'all') {
2127
+ // Include all fields, then exclude specified ones
2128
+ fieldsToInclude = ['*']; // Special marker for "all fields"
2129
+ }
2130
+ else if (mode === 'custom' && config) {
2131
+ // Use custom field list
2132
+ fieldsToInclude = config.fields || [];
2133
+ }
2134
+ // If mode is 'all', just exclude specified fields
2135
+ if (mode === 'all') {
2136
+ const filtered = { ...data };
2137
+ if (config?.exclude && config.exclude.length > 0) {
2138
+ const excludeSet = new Set(config.exclude);
2139
+ Object.keys(filtered).forEach(key => {
2140
+ if (excludeSet.has(key)) {
2141
+ delete filtered[key];
2142
+ }
2143
+ });
2144
+ // Handle nested exclusions
2145
+ if (filtered.connection && excludeSet.has('connection')) {
2146
+ delete filtered.connection;
2147
+ }
2148
+ if (filtered.timezoneDetails && excludeSet.has('timezoneDetails')) {
2149
+ delete filtered.timezoneDetails;
2150
+ }
2151
+ if (filtered.flag && excludeSet.has('flag')) {
2152
+ delete filtered.flag;
2153
+ }
2154
+ if (filtered.firstTouch && excludeSet.has('firstTouch')) {
2155
+ delete filtered.firstTouch;
2156
+ }
2157
+ if (filtered.lastTouch && excludeSet.has('lastTouch')) {
2158
+ delete filtered.lastTouch;
2159
+ }
2160
+ }
2161
+ return filtered;
2162
+ }
2163
+ // For 'essential' or 'custom' mode, only include specified fields
2164
+ const filtered = {};
2165
+ const includeSet = new Set(fieldsToInclude);
2166
+ // Helper to check if a field path should be included
2167
+ const shouldInclude = (fieldPath) => {
2168
+ // Direct match - most specific
2169
+ if (includeSet.has(fieldPath))
2170
+ return true;
2171
+ // For nested fields (e.g., 'flag.emoji'), only include if explicitly listed
2172
+ // Don't auto-include all children just because parent is included
2173
+ const parts = fieldPath.split('.');
2174
+ if (parts.length > 1) {
2175
+ // For nested fields, require explicit inclusion
2176
+ // This prevents 'flag' from including all 'flag.*' fields
2177
+ return includeSet.has(fieldPath);
2178
+ }
2179
+ // For top-level fields only, check if parent path is included
2180
+ // This allows 'connection' to work when all connection.* fields are listed
2181
+ return false;
2182
+ };
2183
+ // Helper to check if a parent object should be created (for nested objects)
2184
+ const shouldIncludeParent = (parentPath) => {
2185
+ // Check if any child of this parent is included
2186
+ for (const field of fieldsToInclude) {
2187
+ if (field.startsWith(parentPath + '.')) {
2188
+ return true;
2189
+ }
2190
+ }
2191
+ // Also check if parent itself is explicitly included
2192
+ return includeSet.has(parentPath);
2193
+ };
2194
+ // Filter top-level fields
2195
+ Object.keys(data).forEach(key => {
2196
+ if (shouldInclude(key)) {
2197
+ filtered[key] = data[key];
2198
+ }
2199
+ });
2200
+ // Handle nested objects - only create if at least one child field is included
2201
+ if (data.connection && shouldIncludeParent('connection')) {
2202
+ filtered.connection = {};
2203
+ if (shouldInclude('connection.asn'))
2204
+ filtered.connection.asn = data.connection.asn;
2205
+ if (shouldInclude('connection.org'))
2206
+ filtered.connection.org = data.connection.org;
2207
+ if (shouldInclude('connection.isp'))
2208
+ filtered.connection.isp = data.connection.isp;
2209
+ if (shouldInclude('connection.domain'))
2210
+ filtered.connection.domain = data.connection.domain;
2211
+ // If no connection fields were included, remove the object
2212
+ if (Object.keys(filtered.connection).length === 0) {
2213
+ delete filtered.connection;
2214
+ }
2215
+ }
2216
+ if (data.timezoneDetails && shouldIncludeParent('timezoneDetails')) {
2217
+ filtered.timezoneDetails = {};
2218
+ if (shouldInclude('timezoneDetails.id'))
2219
+ filtered.timezoneDetails.id = data.timezoneDetails.id;
2220
+ if (shouldInclude('timezoneDetails.abbr'))
2221
+ filtered.timezoneDetails.abbr = data.timezoneDetails.abbr;
2222
+ if (shouldInclude('timezoneDetails.utc'))
2223
+ filtered.timezoneDetails.utc = data.timezoneDetails.utc;
2224
+ if (shouldInclude('timezoneDetails.isDst'))
2225
+ filtered.timezoneDetails.isDst = data.timezoneDetails.isDst;
2226
+ if (shouldInclude('timezoneDetails.offset'))
2227
+ filtered.timezoneDetails.offset = data.timezoneDetails.offset;
2228
+ if (shouldInclude('timezoneDetails.currentTime'))
2229
+ filtered.timezoneDetails.currentTime = data.timezoneDetails.currentTime;
2230
+ // If no timezoneDetails fields were included, remove the object
2231
+ if (Object.keys(filtered.timezoneDetails).length === 0) {
2232
+ delete filtered.timezoneDetails;
2233
+ }
2234
+ }
2235
+ if (data.flag && shouldIncludeParent('flag')) {
2236
+ filtered.flag = {};
2237
+ // Only include specific flag fields if they're explicitly in the include list
2238
+ if (shouldInclude('flag.emoji'))
2239
+ filtered.flag.emoji = data.flag.emoji;
2240
+ if (shouldInclude('flag.img'))
2241
+ filtered.flag.img = data.flag.img;
2242
+ if (shouldInclude('flag.emojiUnicode'))
2243
+ filtered.flag.emojiUnicode = data.flag.emojiUnicode;
2244
+ // If no specific flag fields are included, don't add the flag object
2245
+ if (Object.keys(filtered.flag).length === 0) {
2246
+ delete filtered.flag;
2247
+ }
2248
+ }
2249
+ if (data.firstTouch && shouldInclude('firstTouch')) {
2250
+ filtered.firstTouch = data.firstTouch;
2251
+ }
2252
+ if (data.lastTouch && shouldInclude('lastTouch')) {
2253
+ filtered.lastTouch = data.lastTouch;
2254
+ }
2255
+ // Remove null and undefined values to reduce payload size
2256
+ const cleanValue = (val) => {
2257
+ if (val === null || val === undefined) {
2258
+ return undefined; // Will be filtered out
2259
+ }
2260
+ // For objects, recursively clean nested null/undefined values
2261
+ if (typeof val === 'object' && !Array.isArray(val) && val !== null) {
2262
+ const cleaned = {};
2263
+ let hasAnyValue = false;
2264
+ Object.keys(val).forEach(key => {
2265
+ const cleanedChild = cleanValue(val[key]);
2266
+ if (cleanedChild !== undefined) {
2267
+ cleaned[key] = cleanedChild;
2268
+ hasAnyValue = true;
2269
+ }
2270
+ });
2271
+ return hasAnyValue ? cleaned : undefined;
2272
+ }
2273
+ // For arrays, clean each element
2274
+ if (Array.isArray(val)) {
2275
+ const cleaned = val.map(cleanValue).filter(item => item !== undefined);
2276
+ return cleaned.length > 0 ? cleaned : undefined;
2277
+ }
2278
+ return val;
2279
+ };
2280
+ const cleaned = {};
2281
+ Object.keys(filtered).forEach(key => {
2282
+ const cleanedValue = cleanValue(filtered[key]);
2283
+ if (cleanedValue !== undefined) {
2284
+ cleaned[key] = cleanedValue;
2285
+ }
2286
+ });
2287
+ return cleaned;
2288
+ }
2289
+
2013
2290
  /**
2014
2291
  * Transform IP location data from API format (snake_case) to backend-expected format (camelCase)
2015
- * This ensures compatibility with the analytics backend integration
2292
+ * Supports configurable field storage to optimize storage capacity
2016
2293
  *
2017
2294
  * @param ipLocation - Raw IP location data from ipwho.is API
2018
- * @returns Transformed IP location data matching backend schema
2295
+ * @param config - Optional configuration for which fields to store
2296
+ * @returns Transformed IP location data matching backend schema (only includes configured fields)
2019
2297
  */
2020
- function transformIPLocationForBackend(ipLocation) {
2298
+ function transformIPLocationForBackend(ipLocation, config) {
2021
2299
  if (!ipLocation) {
2022
2300
  return null;
2023
2301
  }
2024
2302
  // Transform to match backend expected format (camelCase)
2303
+ // Build complete object first, then filter based on configuration
2025
2304
  const transformed = {
2026
2305
  // Basic fields
2027
2306
  ip: ipLocation.ip,
@@ -2056,9 +2335,11 @@ function transformIPLocationForBackend(ipLocation) {
2056
2335
  timezoneDetails: ipLocation.timezone && typeof ipLocation.timezone === 'object' ? {
2057
2336
  id: ipLocation.timezone.id,
2058
2337
  abbr: ipLocation.timezone.abbr,
2338
+ utc: ipLocation.timezone.utc,
2339
+ // Exclude these in essential mode: isDst, offset, currentTime
2340
+ // They will be filtered out by filterFieldsByConfig if not in essential fields
2059
2341
  isDst: ipLocation.timezone.is_dst,
2060
2342
  offset: ipLocation.timezone.offset,
2061
- utc: ipLocation.timezone.utc,
2062
2343
  currentTime: ipLocation.timezone.current_time,
2063
2344
  } : undefined,
2064
2345
  // Flag - transform to camelCase
@@ -2074,7 +2355,8 @@ function transformIPLocationForBackend(ipLocation) {
2074
2355
  delete transformed[key];
2075
2356
  }
2076
2357
  });
2077
- return transformed;
2358
+ // Filter fields based on configuration using generic filter
2359
+ return filterFieldsByConfig(transformed, config, DEFAULT_ESSENTIAL_IP_FIELDS);
2078
2360
  }
2079
2361
 
2080
2362
  /**
@@ -2340,20 +2622,49 @@ class AnalyticsService {
2340
2622
  * Track user journey with full context
2341
2623
  */
2342
2624
  static async trackUserJourney({ sessionId, pageUrl, networkInfo, deviceInfo, location, attribution, ipLocation, userId, customData, pageVisits = 1, interactions = 0, }) {
2343
- // Transform IP location data to match backend expected format (camelCase)
2344
- const transformedIPLocation = transformIPLocationForBackend(ipLocation);
2625
+ // Get field storage config (support both new and legacy format)
2626
+ const fieldStorage = this.config.fieldStorage || {};
2627
+ const ipLocationConfig = fieldStorage.ipLocation || this.config.ipLocationFields;
2628
+ // Transform and filter all data types based on configuration
2629
+ const transformedIPLocation = transformIPLocationForBackend(ipLocation, ipLocationConfig);
2630
+ const filteredDeviceInfo = filterFieldsByConfig(deviceInfo, fieldStorage.deviceInfo, DEFAULT_ESSENTIAL_DEVICE_FIELDS);
2631
+ const filteredNetworkInfo = filterFieldsByConfig(networkInfo, fieldStorage.networkInfo, DEFAULT_ESSENTIAL_NETWORK_FIELDS);
2632
+ // For location: In essential mode, remove duplicate fields that are already in customData.ipLocation
2633
+ // This prevents storing the same data twice (e.g., ip, country, city, region, timezone)
2634
+ const locationConfig = fieldStorage.location;
2635
+ const locationMode = locationConfig?.mode || 'essential';
2636
+ let filteredLocation = filterFieldsByConfig(location, locationConfig, DEFAULT_ESSENTIAL_LOCATION_FIELDS);
2637
+ // In essential mode, if we have IP location data, remove duplicate fields from location
2638
+ // to avoid storing the same data twice
2639
+ if (locationMode === 'essential' && transformedIPLocation && filteredLocation) {
2640
+ // Remove fields that are duplicated in customData.ipLocation
2641
+ const duplicateFields = ['ip', 'country', 'countryCode', 'city', 'region', 'timezone'];
2642
+ const minimalLocation = { ...filteredLocation };
2643
+ duplicateFields.forEach(field => {
2644
+ delete minimalLocation[field];
2645
+ });
2646
+ // Only keep essential location fields: lat, lon, source, ts
2647
+ filteredLocation = {
2648
+ lat: minimalLocation.lat,
2649
+ lon: minimalLocation.lon,
2650
+ source: minimalLocation.source,
2651
+ ts: minimalLocation.ts,
2652
+ };
2653
+ }
2654
+ const filteredAttribution = filterFieldsByConfig(attribution, fieldStorage.attribution, DEFAULT_ESSENTIAL_ATTRIBUTION_FIELDS);
2345
2655
  await this.trackEvent({
2346
2656
  sessionId,
2347
2657
  pageUrl,
2348
- networkInfo,
2349
- deviceInfo,
2350
- location,
2351
- attribution,
2352
- ipLocation,
2658
+ networkInfo: filteredNetworkInfo || undefined,
2659
+ deviceInfo: filteredDeviceInfo || undefined,
2660
+ location: filteredLocation || undefined,
2661
+ attribution: filteredAttribution || undefined,
2662
+ // Don't include raw ipLocation - we have the filtered/transformed version in customData
2663
+ ipLocation: undefined,
2353
2664
  userId: userId ?? sessionId,
2354
2665
  customData: {
2355
2666
  ...customData,
2356
- // Store transformed IP location in customData for backend integration
2667
+ // Store transformed and filtered IP location in customData for backend integration
2357
2668
  ...(transformedIPLocation && { ipLocation: transformedIPLocation }),
2358
2669
  },
2359
2670
  eventName: 'page_view', // Auto-tracked as page view
@@ -2430,15 +2741,41 @@ class AnalyticsService {
2430
2741
  const ipLocationData = locationData && typeof locationData === 'object'
2431
2742
  ? locationData?.ipLocationData
2432
2743
  : undefined;
2433
- // Transform IP location data to match backend expected format
2434
- const transformedIPLocation = transformIPLocationForBackend(ipLocationData);
2744
+ // Get field storage config (support both new and legacy format)
2745
+ const fieldStorage = this.config.fieldStorage || {};
2746
+ const ipLocationConfig = fieldStorage.ipLocation || this.config.ipLocationFields;
2747
+ // Transform and filter all data types based on configuration
2748
+ const transformedIPLocation = transformIPLocationForBackend(ipLocationData, ipLocationConfig);
2749
+ const filteredDeviceInfo = filterFieldsByConfig(context?.deviceInfo || autoContext?.deviceInfo, fieldStorage.deviceInfo, DEFAULT_ESSENTIAL_DEVICE_FIELDS);
2750
+ const filteredNetworkInfo = filterFieldsByConfig(context?.networkInfo || autoContext?.networkInfo, fieldStorage.networkInfo, DEFAULT_ESSENTIAL_NETWORK_FIELDS);
2751
+ // For location: In essential mode, remove duplicate fields that are already in customData.ipLocation
2752
+ const locationConfig = fieldStorage.location;
2753
+ const locationMode = locationConfig?.mode || 'essential';
2754
+ let filteredLocation = filterFieldsByConfig((context?.location || autoContext?.location), locationConfig, DEFAULT_ESSENTIAL_LOCATION_FIELDS);
2755
+ // In essential mode, if we have IP location data, remove duplicate fields from location
2756
+ if (locationMode === 'essential' && transformedIPLocation && filteredLocation) {
2757
+ // Remove fields that are duplicated in customData.ipLocation
2758
+ const duplicateFields = ['ip', 'country', 'countryCode', 'city', 'region', 'timezone'];
2759
+ const minimalLocation = { ...filteredLocation };
2760
+ duplicateFields.forEach(field => {
2761
+ delete minimalLocation[field];
2762
+ });
2763
+ // Only keep essential location fields: lat, lon, source, ts
2764
+ filteredLocation = {
2765
+ lat: minimalLocation.lat,
2766
+ lon: minimalLocation.lon,
2767
+ source: minimalLocation.source,
2768
+ ts: minimalLocation.ts,
2769
+ };
2770
+ }
2771
+ const filteredAttribution = filterFieldsByConfig(context?.attribution || autoContext?.attribution, fieldStorage.attribution, DEFAULT_ESSENTIAL_ATTRIBUTION_FIELDS);
2435
2772
  await this.trackEvent({
2436
2773
  sessionId: finalSessionId,
2437
2774
  pageUrl: finalPageUrl,
2438
- networkInfo: context?.networkInfo || autoContext?.networkInfo,
2439
- deviceInfo: context?.deviceInfo || autoContext?.deviceInfo,
2440
- location: context?.location || autoContext?.location,
2441
- attribution: context?.attribution || autoContext?.attribution,
2775
+ networkInfo: filteredNetworkInfo || undefined,
2776
+ deviceInfo: filteredDeviceInfo || undefined,
2777
+ location: filteredLocation || undefined,
2778
+ attribution: filteredAttribution || undefined,
2442
2779
  userId: context?.userId || finalSessionId,
2443
2780
  eventName,
2444
2781
  eventParameters: parameters || {},
@@ -2624,6 +2961,8 @@ function useAnalytics(options = {}) {
2624
2961
  logLevel: config.logLevel,
2625
2962
  enableMetrics: config.enableMetrics,
2626
2963
  sessionTimeout: config.sessionTimeout,
2964
+ fieldStorage: config.fieldStorage,
2965
+ ipLocationFields: config.ipLocationFields, // Legacy support
2627
2966
  });
2628
2967
  }
2629
2968
  }, [
@@ -2911,6 +3250,11 @@ function useAnalytics(options = {}) {
2911
3250
 
2912
3251
  exports.AnalyticsService = AnalyticsService;
2913
3252
  exports.AttributionDetector = AttributionDetector;
3253
+ exports.DEFAULT_ESSENTIAL_ATTRIBUTION_FIELDS = DEFAULT_ESSENTIAL_ATTRIBUTION_FIELDS;
3254
+ exports.DEFAULT_ESSENTIAL_DEVICE_FIELDS = DEFAULT_ESSENTIAL_DEVICE_FIELDS;
3255
+ exports.DEFAULT_ESSENTIAL_IP_FIELDS = DEFAULT_ESSENTIAL_IP_FIELDS;
3256
+ exports.DEFAULT_ESSENTIAL_LOCATION_FIELDS = DEFAULT_ESSENTIAL_LOCATION_FIELDS;
3257
+ exports.DEFAULT_ESSENTIAL_NETWORK_FIELDS = DEFAULT_ESSENTIAL_NETWORK_FIELDS;
2914
3258
  exports.DeviceDetector = DeviceDetector;
2915
3259
  exports.LocationDetector = LocationDetector;
2916
3260
  exports.NetworkDetector = NetworkDetector;
@@ -2919,6 +3263,7 @@ exports.checkAndSetLocationConsent = checkAndSetLocationConsent;
2919
3263
  exports.clearLocationConsent = clearLocationConsent;
2920
3264
  exports.clearSession = clearSession;
2921
3265
  exports.default = useAnalytics;
3266
+ exports.filterFieldsByConfig = filterFieldsByConfig;
2922
3267
  exports.getCompleteIPLocation = getCompleteIPLocation;
2923
3268
  exports.getIPFromRequest = getIPFromRequest;
2924
3269
  exports.getIPLocation = getIPLocation;