@journium/react 1.2.1 → 1.3.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/dist/index.mjs CHANGED
@@ -1,4 +1,7 @@
1
- import React, { createContext, useState, useEffect, useContext, useCallback } from 'react';
1
+ import React, { createContext, useState, useMemo, useEffect, useContext, useCallback } from 'react';
2
+
3
+ // @journium/js@1.3.0 is replaced at build time by @rollup/plugin-replace
4
+ const SDK_VERSION$1 = '@journium/js@1.3.0';
2
5
 
3
6
  /**
4
7
  * uuidv7: A JavaScript implementation of UUID version 7
@@ -940,6 +943,7 @@ class JourniumClient {
940
943
  Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
941
944
  }
942
945
  buildIdentityProperties(userProperties = {}) {
946
+ var _a, _b;
943
947
  const identity = this.identityManager.getIdentity();
944
948
  const userAgentInfo = this.identityManager.getUserAgentInfo();
945
949
  return {
@@ -950,7 +954,7 @@ class JourniumClient {
950
954
  $current_url: typeof window !== 'undefined' ? window.location.href : '',
951
955
  $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
952
956
  ...userAgentInfo,
953
- $lib_version: '0.1.0', // TODO: Get from package.json
957
+ $sdk_version: (_b = (_a = this.config.options) === null || _a === void 0 ? void 0 : _a._sdkVersion) !== null && _b !== void 0 ? _b : 'unknown',
954
958
  $platform: 'web',
955
959
  ...userProperties,
956
960
  };
@@ -1091,7 +1095,7 @@ class PageviewTracker {
1091
1095
  $host: url.host,
1092
1096
  $pathname: url.pathname,
1093
1097
  $search: url.search,
1094
- $title: getPageTitle(),
1098
+ $page_title: getPageTitle(),
1095
1099
  $referrer: getReferrer(),
1096
1100
  ...customProperties,
1097
1101
  };
@@ -1167,6 +1171,8 @@ class AutocaptureTracker {
1167
1171
  ignoreClasses: ['journium-ignore'],
1168
1172
  ignoreElements: ['script', 'style', 'noscript'],
1169
1173
  captureContentText: true,
1174
+ dataAttributePrefixes: ['jrnm-'],
1175
+ dataAttributeNames: ['data-testid', 'data-track'],
1170
1176
  ...options,
1171
1177
  };
1172
1178
  }
@@ -1188,6 +1194,8 @@ class AutocaptureTracker {
1188
1194
  ignoreClasses: ['journium-ignore'],
1189
1195
  ignoreElements: ['script', 'style', 'noscript'],
1190
1196
  captureContentText: true,
1197
+ dataAttributePrefixes: ['jrnm-'],
1198
+ dataAttributeNames: ['data-testid', 'data-track'],
1191
1199
  ...options,
1192
1200
  };
1193
1201
  // Restart if it was active before
@@ -1230,10 +1238,7 @@ class AutocaptureTracker {
1230
1238
  return;
1231
1239
  }
1232
1240
  const properties = this.getElementProperties(target, 'click');
1233
- this.client.track('$autocapture', {
1234
- $event_type: 'click',
1235
- ...properties,
1236
- });
1241
+ this.client.track('$autocapture', properties);
1237
1242
  };
1238
1243
  document.addEventListener('click', clickListener, true);
1239
1244
  this.listeners.set('click', clickListener);
@@ -1245,10 +1250,7 @@ class AutocaptureTracker {
1245
1250
  return;
1246
1251
  }
1247
1252
  const properties = this.getFormProperties(target, 'submit');
1248
- this.client.track('$autocapture', {
1249
- $event_type: 'submit',
1250
- ...properties,
1251
- });
1253
+ this.client.track('$autocapture', properties);
1252
1254
  };
1253
1255
  document.addEventListener('submit', submitListener, true);
1254
1256
  this.listeners.set('submit', submitListener);
@@ -1260,10 +1262,7 @@ class AutocaptureTracker {
1260
1262
  return;
1261
1263
  }
1262
1264
  const properties = this.getInputProperties(target, 'change');
1263
- this.client.track('$autocapture', {
1264
- $event_type: 'change',
1265
- ...properties,
1266
- });
1265
+ this.client.track('$autocapture', properties);
1267
1266
  };
1268
1267
  document.addEventListener('change', changeListener, true);
1269
1268
  this.listeners.set('change', changeListener);
@@ -1316,6 +1315,7 @@ class AutocaptureTracker {
1316
1315
  }
1317
1316
  getElementProperties(element, eventType) {
1318
1317
  const properties = {
1318
+ $event_type: eventType,
1319
1319
  $element_tag: element.tagName.toLowerCase(),
1320
1320
  $element_type: this.getElementType(element),
1321
1321
  };
@@ -1325,6 +1325,7 @@ class AutocaptureTracker {
1325
1325
  }
1326
1326
  if (element.className) {
1327
1327
  properties.$element_classes = Array.from(element.classList);
1328
+ properties.$element_semantic_classes = this.extractSemanticClasses(element.classList);
1328
1329
  }
1329
1330
  // Element attributes
1330
1331
  const relevantAttributes = ['name', 'role', 'aria-label', 'data-testid', 'data-track'];
@@ -1334,6 +1335,33 @@ class AutocaptureTracker {
1334
1335
  properties[`$element_${attr.replace('-', '_')}`] = value;
1335
1336
  }
1336
1337
  });
1338
+ // Configurable data-* attribute capture
1339
+ const prefixes = this.options.dataAttributePrefixes || ['jrnm-'];
1340
+ const exactNames = new Set(this.options.dataAttributeNames || ['data-testid', 'data-track']);
1341
+ const relevantSet = new Set(relevantAttributes);
1342
+ let dataAttrCount = 0;
1343
+ for (let i = 0; i < element.attributes.length && dataAttrCount < 10; i++) {
1344
+ const attr = element.attributes.item(i);
1345
+ if (!attr || !attr.name.startsWith('data-'))
1346
+ continue;
1347
+ if (relevantSet.has(attr.name))
1348
+ continue;
1349
+ const suffix = attr.name.slice(5); // strip 'data-'
1350
+ const matchesPrefix = prefixes.some(p => suffix.startsWith(p));
1351
+ const matchesName = exactNames.has(attr.name);
1352
+ if (matchesPrefix || matchesName) {
1353
+ const propName = `$attr_${attr.name.replace(/-/g, '_')}`;
1354
+ properties[propName] = attr.value;
1355
+ dataAttrCount++;
1356
+ }
1357
+ }
1358
+ // Link href as first-class property
1359
+ if (element.tagName.toLowerCase() === 'a') {
1360
+ const href = element.getAttribute('href');
1361
+ if (href) {
1362
+ properties.$element_href = href;
1363
+ }
1364
+ }
1337
1365
  // Element content
1338
1366
  if (this.options.captureContentText) {
1339
1367
  const text = this.getElementText(element);
@@ -1363,10 +1391,13 @@ class AutocaptureTracker {
1363
1391
  properties.$parent_id = element.parentElement.id;
1364
1392
  }
1365
1393
  }
1366
- // URL information
1394
+ // URL and page context
1367
1395
  properties.$current_url = window.location.href;
1368
1396
  properties.$host = window.location.host;
1369
1397
  properties.$pathname = window.location.pathname;
1398
+ properties.$search = window.location.search;
1399
+ properties.$page_title = document.title;
1400
+ properties.$referrer = document.referrer;
1370
1401
  return properties;
1371
1402
  }
1372
1403
  getFormProperties(form, eventType) {
@@ -1515,6 +1546,38 @@ class AutocaptureTracker {
1515
1546
  ids: ids.reverse()
1516
1547
  };
1517
1548
  }
1549
+ extractSemanticClasses(classList) {
1550
+ const results = new Set();
1551
+ for (let i = 0; i < classList.length; i++) {
1552
+ const cls = classList.item(i);
1553
+ if (!cls)
1554
+ continue;
1555
+ const parts = cls.split('__');
1556
+ if (parts.length >= 3) {
1557
+ // CSS module pattern: Module__hash__name → take last segment
1558
+ const last = parts[parts.length - 1];
1559
+ if (last)
1560
+ results.add(last);
1561
+ }
1562
+ else if (parts.length === 2) {
1563
+ // 2-part __ class (e.g., Module__hash) → drop, no semantic name
1564
+ continue;
1565
+ }
1566
+ else {
1567
+ // Single-part class — keep unless it looks like a hash
1568
+ if (!this.isHashLike(cls)) {
1569
+ results.add(cls);
1570
+ }
1571
+ }
1572
+ }
1573
+ return Array.from(results);
1574
+ }
1575
+ isHashLike(value) {
1576
+ // Hash-like: alphanumeric, 5-10 chars, contains both letters and digits
1577
+ return /^[a-zA-Z0-9]{5,10}$/.test(value)
1578
+ && /[a-zA-Z]/.test(value)
1579
+ && /[0-9]/.test(value);
1580
+ }
1518
1581
  isSafeInputType(type) {
1519
1582
  // Don't capture values for sensitive input types
1520
1583
  const sensitiveTypes = ['password', 'email', 'tel', 'credit-card-number'];
@@ -1701,9 +1764,20 @@ class JourniumAnalytics {
1701
1764
  }
1702
1765
  /** Create and return a new JourniumAnalytics instance for the given config. */
1703
1766
  const init = (config) => {
1767
+ var _a;
1768
+ // Set SDK version if not already set by a framework SDK (React, Next.js, Angular)
1769
+ if (!((_a = config.options) === null || _a === void 0 ? void 0 : _a._sdkVersion)) {
1770
+ config = {
1771
+ ...config,
1772
+ options: { ...config.options, _sdkVersion: SDK_VERSION$1 },
1773
+ };
1774
+ }
1704
1775
  return new JourniumAnalytics(config);
1705
1776
  };
1706
1777
 
1778
+ // @journium/react@1.3.0 is replaced at build time by @rollup/plugin-replace
1779
+ const SDK_VERSION = '@journium/react@1.3.0';
1780
+
1707
1781
  const JourniumContext = createContext(undefined);
1708
1782
  /**
1709
1783
  * Provides the Journium analytics instance to the React tree.
@@ -1713,8 +1787,12 @@ const JourniumContext = createContext(undefined);
1713
1787
  const JourniumProvider = ({ children, config, }) => {
1714
1788
  const [analytics, setAnalytics] = useState(null);
1715
1789
  const [effectiveOptions, setEffectiveOptions] = useState(null);
1790
+ const configWithSdk = useMemo(() => ({
1791
+ ...config,
1792
+ options: { _sdkVersion: SDK_VERSION, ...config.options },
1793
+ }), [config]);
1716
1794
  useEffect(() => {
1717
- const analyticsInstance = init(config);
1795
+ const analyticsInstance = init(configWithSdk);
1718
1796
  // Get initial effective options (may be empty during remote-first initialization)
1719
1797
  const initialEffective = analyticsInstance.getEffectiveOptions();
1720
1798
  setEffectiveOptions(initialEffective);
@@ -1734,7 +1812,7 @@ const JourniumProvider = ({ children, config, }) => {
1734
1812
  setAnalytics(null);
1735
1813
  setEffectiveOptions(null);
1736
1814
  };
1737
- }, [config]);
1815
+ }, [configWithSdk]);
1738
1816
  return (React.createElement(JourniumContext.Provider, { value: { analytics, config, effectiveOptions } }, children));
1739
1817
  };
1740
1818
  /**