@journium/react 1.2.0 → 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/context.d.ts.map +1 -1
- package/dist/index.cjs +100 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +101 -23
- package/dist/index.mjs.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +3 -3
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
|
-
$
|
|
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
|
-
$
|
|
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
|
|
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'];
|
|
@@ -1541,8 +1604,8 @@ class JourniumAnalytics {
|
|
|
1541
1604
|
// This handles cached remote options or local options with autocapture enabled
|
|
1542
1605
|
this.startAutocaptureIfEnabled(initialEffectiveOptions);
|
|
1543
1606
|
}
|
|
1544
|
-
resolvePageviewOptions(autoTrackPageviews) {
|
|
1545
|
-
if (autoTrackPageviews === false) {
|
|
1607
|
+
resolvePageviewOptions(autoTrackPageviews, frameworkHandlesPageviews) {
|
|
1608
|
+
if (autoTrackPageviews === false || frameworkHandlesPageviews) {
|
|
1546
1609
|
return { enabled: false, trackSpaPageviews: false, captureInitialPageview: false };
|
|
1547
1610
|
}
|
|
1548
1611
|
if (autoTrackPageviews === true || autoTrackPageviews === undefined) {
|
|
@@ -1596,7 +1659,7 @@ class JourniumAnalytics {
|
|
|
1596
1659
|
// Only start if effectiveOptions are actually loaded (non-empty)
|
|
1597
1660
|
const hasOptions = effectiveOptions && Object.keys(effectiveOptions).length > 0;
|
|
1598
1661
|
const { enabled: autoTrackPageviews, trackSpaPageviews, captureInitialPageview } = hasOptions
|
|
1599
|
-
? this.resolvePageviewOptions(effectiveOptions.autoTrackPageviews)
|
|
1662
|
+
? this.resolvePageviewOptions(effectiveOptions.autoTrackPageviews, effectiveOptions._frameworkHandlesPageviews)
|
|
1600
1663
|
: { enabled: false, trackSpaPageviews: false, captureInitialPageview: false };
|
|
1601
1664
|
const autocaptureEnabled = hasOptions
|
|
1602
1665
|
? effectiveOptions.autocapture !== false
|
|
@@ -1632,7 +1695,7 @@ class JourniumAnalytics {
|
|
|
1632
1695
|
const hasActualOptions = effectiveOptions && Object.keys(effectiveOptions).length > 0;
|
|
1633
1696
|
if (hasActualOptions) {
|
|
1634
1697
|
// Use same logic as manual startAutocapture() but only start automatically
|
|
1635
|
-
const { enabled: autoTrackPageviews, trackSpaPageviews, captureInitialPageview } = this.resolvePageviewOptions(effectiveOptions.autoTrackPageviews);
|
|
1698
|
+
const { enabled: autoTrackPageviews, trackSpaPageviews, captureInitialPageview } = this.resolvePageviewOptions(effectiveOptions.autoTrackPageviews, effectiveOptions._frameworkHandlesPageviews);
|
|
1636
1699
|
const autocaptureEnabled = effectiveOptions.autocapture !== false;
|
|
1637
1700
|
// Update autocapture tracker options
|
|
1638
1701
|
const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
|
|
@@ -1663,7 +1726,7 @@ class JourniumAnalytics {
|
|
|
1663
1726
|
this.autocaptureTracker.stop();
|
|
1664
1727
|
this.autocaptureStarted = false;
|
|
1665
1728
|
}
|
|
1666
|
-
const { enabled: autoTrackPageviews, trackSpaPageviews, captureInitialPageview } = this.resolvePageviewOptions(effectiveOptions.autoTrackPageviews);
|
|
1729
|
+
const { enabled: autoTrackPageviews, trackSpaPageviews, captureInitialPageview } = this.resolvePageviewOptions(effectiveOptions.autoTrackPageviews, effectiveOptions._frameworkHandlesPageviews);
|
|
1667
1730
|
const autocaptureEnabled = effectiveOptions.autocapture !== false;
|
|
1668
1731
|
const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
|
|
1669
1732
|
this.autocaptureTracker.updateOptions(autocaptureOptions);
|
|
@@ -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(
|
|
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
|
-
}, [
|
|
1815
|
+
}, [configWithSdk]);
|
|
1738
1816
|
return (React.createElement(JourniumContext.Provider, { value: { analytics, config, effectiveOptions } }, children));
|
|
1739
1817
|
};
|
|
1740
1818
|
/**
|