humanbehavior-js 0.4.23 → 0.4.25
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/cjs/angular/index.cjs +176 -17
- package/dist/cjs/angular/index.cjs.map +1 -1
- package/dist/cjs/index.cjs +176 -17
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/install-wizard.cjs +5 -5
- package/dist/cjs/install-wizard.cjs.map +1 -1
- package/dist/cjs/react/index.cjs +176 -17
- package/dist/cjs/react/index.cjs.map +1 -1
- package/dist/cjs/remix/index.cjs +176 -17
- package/dist/cjs/remix/index.cjs.map +1 -1
- package/dist/cjs/svelte/index.cjs +176 -17
- package/dist/cjs/svelte/index.cjs.map +1 -1
- package/dist/cjs/vue/index.cjs +176 -17
- package/dist/cjs/vue/index.cjs.map +1 -1
- package/dist/cjs/wizard/index.cjs +5 -5
- package/dist/cjs/wizard/index.cjs.map +1 -1
- package/dist/cli/ai-auto-install.js +5 -5
- package/dist/cli/ai-auto-install.js.map +1 -1
- package/dist/cli/auto-install.js +5 -5
- package/dist/cli/auto-install.js.map +1 -1
- package/dist/esm/angular/index.js +176 -17
- package/dist/esm/angular/index.js.map +1 -1
- package/dist/esm/index.js +176 -17
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/install-wizard.js +5 -5
- package/dist/esm/install-wizard.js.map +1 -1
- package/dist/esm/react/index.js +176 -17
- package/dist/esm/react/index.js.map +1 -1
- package/dist/esm/remix/index.js +176 -17
- package/dist/esm/remix/index.js.map +1 -1
- package/dist/esm/svelte/index.js +176 -17
- package/dist/esm/svelte/index.js.map +1 -1
- package/dist/esm/vue/index.js +176 -17
- package/dist/esm/vue/index.js.map +1 -1
- package/dist/esm/wizard/index.js +5 -5
- package/dist/esm/wizard/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/types/angular/index.d.ts +22 -0
- package/dist/types/index.d.ts +40 -2
- package/dist/types/install-wizard.d.ts +1 -1
- package/dist/types/react/index.d.ts +22 -0
- package/dist/types/remix/index.d.ts +22 -0
- package/dist/types/svelte/index.d.ts +22 -0
- package/dist/types/wizard/index.d.ts +1 -1
- package/package.json +2 -1
- package/readme.md +59 -5
- package/src/redact.ts +135 -15
- package/src/tracker.ts +69 -4
- package/src/wizard/core/install-wizard.ts +5 -5
package/dist/cjs/index.cjs
CHANGED
|
@@ -12908,6 +12908,8 @@ class RedactionManager {
|
|
|
12908
12908
|
constructor(options) {
|
|
12909
12909
|
this.redactedText = '[REDACTED]';
|
|
12910
12910
|
this.unredactedFields = new Set(); // Fields that user wants to unredact
|
|
12911
|
+
this.redactedFields = new Set(); // Fields that user wants to redact
|
|
12912
|
+
this.redactionMode = 'privacy-first';
|
|
12911
12913
|
this.excludeSelectors = [
|
|
12912
12914
|
'[data-no-redact="true"]',
|
|
12913
12915
|
'.human-behavior-no-redact'
|
|
@@ -12918,10 +12920,56 @@ class RedactionManager {
|
|
|
12918
12920
|
if (options === null || options === void 0 ? void 0 : options.excludeSelectors) {
|
|
12919
12921
|
this.excludeSelectors = [...this.excludeSelectors, ...options.excludeSelectors];
|
|
12920
12922
|
}
|
|
12923
|
+
// Handle new redaction strategy
|
|
12924
|
+
if (options === null || options === void 0 ? void 0 : options.redactionStrategy) {
|
|
12925
|
+
this.redactionMode = options.redactionStrategy.mode;
|
|
12926
|
+
if (this.redactionMode === 'privacy-first') {
|
|
12927
|
+
// Privacy-first: everything redacted by default, unredact specific fields
|
|
12928
|
+
if (options.redactionStrategy.unredactFields) {
|
|
12929
|
+
this.setFieldsToUnredact(options.redactionStrategy.unredactFields);
|
|
12930
|
+
}
|
|
12931
|
+
}
|
|
12932
|
+
else {
|
|
12933
|
+
// Visibility-first: everything visible by default, redact specific fields
|
|
12934
|
+
if (options.redactionStrategy.redactFields) {
|
|
12935
|
+
this.setFieldsToRedact(options.redactionStrategy.redactFields);
|
|
12936
|
+
}
|
|
12937
|
+
}
|
|
12938
|
+
}
|
|
12939
|
+
// Handle legacy redactFields (backward compatibility)
|
|
12940
|
+
if (options === null || options === void 0 ? void 0 : options.legacyRedactFields) {
|
|
12941
|
+
this.setFieldsToUnredact(options.legacyRedactFields);
|
|
12942
|
+
}
|
|
12943
|
+
// Handle legacy userFields
|
|
12921
12944
|
if (options === null || options === void 0 ? void 0 : options.userFields) {
|
|
12922
12945
|
this.setFieldsToUnredact(options.userFields);
|
|
12923
12946
|
}
|
|
12924
12947
|
}
|
|
12948
|
+
/**
|
|
12949
|
+
* Set specific fields to be redacted (for visibility-first mode)
|
|
12950
|
+
* @param fields Array of CSS selectors for fields to redact
|
|
12951
|
+
*/
|
|
12952
|
+
setFieldsToRedact(fields) {
|
|
12953
|
+
this.redactedFields.clear();
|
|
12954
|
+
// Always include password fields in redacted list
|
|
12955
|
+
const passwordFields = [
|
|
12956
|
+
'input[type="password"]',
|
|
12957
|
+
'input[type="password" i]',
|
|
12958
|
+
'[type="password"]',
|
|
12959
|
+
'[type="password" i]'
|
|
12960
|
+
];
|
|
12961
|
+
// Add password fields and user-specified fields
|
|
12962
|
+
[...passwordFields, ...fields].forEach(field => {
|
|
12963
|
+
this.redactedFields.add(field);
|
|
12964
|
+
});
|
|
12965
|
+
if (this.redactedFields.size > 0) {
|
|
12966
|
+
logDebug(`Redaction: Active for ${this.redactedFields.size} field(s):`, Array.from(this.redactedFields));
|
|
12967
|
+
}
|
|
12968
|
+
else {
|
|
12969
|
+
logDebug('Redaction: No fields to redact');
|
|
12970
|
+
}
|
|
12971
|
+
this.applyRedactionClasses();
|
|
12972
|
+
}
|
|
12925
12973
|
/**
|
|
12926
12974
|
* Set specific fields to be unredacted (everything else stays redacted by rrweb)
|
|
12927
12975
|
* @param fields Array of CSS selectors for fields to unredact
|
|
@@ -12976,6 +13024,12 @@ class RedactionManager {
|
|
|
12976
13024
|
hasUnredactedFields() {
|
|
12977
13025
|
return this.unredactedFields.size > 0;
|
|
12978
13026
|
}
|
|
13027
|
+
/**
|
|
13028
|
+
* Get the current redaction mode
|
|
13029
|
+
*/
|
|
13030
|
+
getRedactionMode() {
|
|
13031
|
+
return this.redactionMode;
|
|
13032
|
+
}
|
|
12979
13033
|
/**
|
|
12980
13034
|
* Get the currently unredacted fields
|
|
12981
13035
|
*/
|
|
@@ -12987,30 +13041,42 @@ class RedactionManager {
|
|
|
12987
13041
|
* Returns null if no fields are unredacted (everything stays redacted)
|
|
12988
13042
|
*/
|
|
12989
13043
|
getMaskTextSelector() {
|
|
12990
|
-
if (this.
|
|
12991
|
-
|
|
13044
|
+
if (this.redactionMode === 'privacy-first') {
|
|
13045
|
+
// Privacy-first: mask everything except unredacted fields
|
|
13046
|
+
if (this.unredactedFields.size === 0) {
|
|
13047
|
+
return null; // Everything stays redacted
|
|
13048
|
+
}
|
|
13049
|
+
return Array.from(this.unredactedFields).join(',');
|
|
13050
|
+
}
|
|
13051
|
+
else {
|
|
13052
|
+
// Visibility-first: mask only redacted fields
|
|
13053
|
+
if (this.redactedFields.size === 0) {
|
|
13054
|
+
return null; // Nothing to redact
|
|
13055
|
+
}
|
|
13056
|
+
return Array.from(this.redactedFields).join(',');
|
|
12992
13057
|
}
|
|
12993
|
-
return Array.from(this.unredactedFields).join(',');
|
|
12994
13058
|
}
|
|
12995
13059
|
/**
|
|
12996
|
-
*
|
|
13060
|
+
* Apply redaction classes to DOM elements (for visibility-first mode)
|
|
13061
|
+
* Adds 'rr-mask' class to elements that should be redacted
|
|
12997
13062
|
*/
|
|
12998
|
-
|
|
12999
|
-
if (this.
|
|
13000
|
-
return
|
|
13063
|
+
applyRedactionClasses() {
|
|
13064
|
+
if (this.redactedFields.size === 0) {
|
|
13065
|
+
return;
|
|
13001
13066
|
}
|
|
13002
|
-
//
|
|
13003
|
-
|
|
13067
|
+
// Add 'rr-mask' class to redacted elements
|
|
13068
|
+
this.redactedFields.forEach(selector => {
|
|
13004
13069
|
try {
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
|
|
13070
|
+
const elements = document.querySelectorAll(selector);
|
|
13071
|
+
elements.forEach(element => {
|
|
13072
|
+
element.classList.add('rr-mask');
|
|
13073
|
+
});
|
|
13074
|
+
logDebug(`Added rr-mask class to ${elements.length} element(s) for selector: ${selector}`);
|
|
13008
13075
|
}
|
|
13009
13076
|
catch (e) {
|
|
13010
13077
|
logWarn(`Invalid selector: ${selector}`);
|
|
13011
13078
|
}
|
|
13012
|
-
}
|
|
13013
|
-
return false;
|
|
13079
|
+
});
|
|
13014
13080
|
}
|
|
13015
13081
|
/**
|
|
13016
13082
|
* Apply unredaction classes to DOM elements
|
|
@@ -13068,6 +13134,47 @@ class RedactionManager {
|
|
|
13068
13134
|
isElementUnredacted(element) {
|
|
13069
13135
|
return this.shouldUnredactElement(element);
|
|
13070
13136
|
}
|
|
13137
|
+
/**
|
|
13138
|
+
* Check if an element should be unredacted
|
|
13139
|
+
*/
|
|
13140
|
+
shouldUnredactElement(element) {
|
|
13141
|
+
if (this.redactionMode === 'privacy-first') {
|
|
13142
|
+
// Privacy-first: check if element is in unredacted fields
|
|
13143
|
+
if (this.unredactedFields.size === 0) {
|
|
13144
|
+
return false; // Nothing unredacted
|
|
13145
|
+
}
|
|
13146
|
+
// Check if any selector matches this element
|
|
13147
|
+
for (const selector of this.unredactedFields) {
|
|
13148
|
+
try {
|
|
13149
|
+
if (element.matches(selector)) {
|
|
13150
|
+
return true;
|
|
13151
|
+
}
|
|
13152
|
+
}
|
|
13153
|
+
catch (e) {
|
|
13154
|
+
logWarn(`Invalid selector: ${selector}`);
|
|
13155
|
+
}
|
|
13156
|
+
}
|
|
13157
|
+
return false;
|
|
13158
|
+
}
|
|
13159
|
+
else {
|
|
13160
|
+
// Visibility-first: check if element is NOT in redacted fields
|
|
13161
|
+
if (this.redactedFields.size === 0) {
|
|
13162
|
+
return true; // Nothing redacted, everything visible
|
|
13163
|
+
}
|
|
13164
|
+
// Check if any selector matches this element
|
|
13165
|
+
for (const selector of this.redactedFields) {
|
|
13166
|
+
try {
|
|
13167
|
+
if (element.matches(selector)) {
|
|
13168
|
+
return false; // Element is redacted
|
|
13169
|
+
}
|
|
13170
|
+
}
|
|
13171
|
+
catch (e) {
|
|
13172
|
+
logWarn(`Invalid selector: ${selector}`);
|
|
13173
|
+
}
|
|
13174
|
+
}
|
|
13175
|
+
return true; // Element is not redacted
|
|
13176
|
+
}
|
|
13177
|
+
}
|
|
13071
13178
|
}
|
|
13072
13179
|
// Export a default instance
|
|
13073
13180
|
const redactionManager = new RedactionManager();
|
|
@@ -13635,10 +13742,23 @@ class HumanBehaviorTracker {
|
|
|
13635
13742
|
});
|
|
13636
13743
|
// Store canvas recording preference
|
|
13637
13744
|
tracker.recordCanvas = (_a = options === null || options === void 0 ? void 0 : options.recordCanvas) !== null && _a !== void 0 ? _a : false;
|
|
13638
|
-
// Set unredacted fields if specified
|
|
13745
|
+
// Set unredacted fields if specified (legacy support)
|
|
13639
13746
|
if (options === null || options === void 0 ? void 0 : options.redactFields) {
|
|
13640
13747
|
tracker.setUnredactedFields(options.redactFields);
|
|
13641
13748
|
}
|
|
13749
|
+
// Handle new redaction strategy
|
|
13750
|
+
if (options === null || options === void 0 ? void 0 : options.redactionStrategy) {
|
|
13751
|
+
if (options.redactionStrategy.mode === 'privacy-first') {
|
|
13752
|
+
if (options.redactionStrategy.unredactFields) {
|
|
13753
|
+
tracker.setUnredactedFields(options.redactionStrategy.unredactFields);
|
|
13754
|
+
}
|
|
13755
|
+
}
|
|
13756
|
+
else {
|
|
13757
|
+
if (options.redactionStrategy.redactFields) {
|
|
13758
|
+
tracker.setRedactedFields(options.redactionStrategy.redactFields);
|
|
13759
|
+
}
|
|
13760
|
+
}
|
|
13761
|
+
}
|
|
13642
13762
|
// Setup automatic tracking if enabled
|
|
13643
13763
|
if ((options === null || options === void 0 ? void 0 : options.enableAutomaticTracking) !== false) {
|
|
13644
13764
|
tracker.setupAutomaticTracking(options === null || options === void 0 ? void 0 : options.automaticTrackingOptions);
|
|
@@ -13684,7 +13804,10 @@ class HumanBehaviorTracker {
|
|
|
13684
13804
|
ingestionUrl: ingestionUrl || defaultIngestionUrl
|
|
13685
13805
|
});
|
|
13686
13806
|
this.apiKey = apiKey;
|
|
13687
|
-
this.redactionManager = new RedactionManager(
|
|
13807
|
+
this.redactionManager = new RedactionManager({
|
|
13808
|
+
redactionStrategy: options === null || options === void 0 ? void 0 : options.redactionStrategy,
|
|
13809
|
+
legacyRedactFields: options === null || options === void 0 ? void 0 : options.redactFields // For backward compatibility
|
|
13810
|
+
});
|
|
13688
13811
|
// Initialize property manager
|
|
13689
13812
|
this.propertyManager = new PropertyManager({
|
|
13690
13813
|
enableAutomaticProperties: (options === null || options === void 0 ? void 0 : options.enableAutomaticProperties) !== false,
|
|
@@ -14262,6 +14385,31 @@ class HumanBehaviorTracker {
|
|
|
14262
14385
|
if (!userResponse.ok) {
|
|
14263
14386
|
throw new Error(`Failed to identify user: ${userResponse.statusText}`);
|
|
14264
14387
|
}
|
|
14388
|
+
// Get IP info and GeoIP data
|
|
14389
|
+
try {
|
|
14390
|
+
const ipResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/ip-info`, {
|
|
14391
|
+
method: 'POST',
|
|
14392
|
+
headers: {
|
|
14393
|
+
'Content-Type': 'application/json',
|
|
14394
|
+
'Authorization': `Bearer ${this.apiKey}`
|
|
14395
|
+
},
|
|
14396
|
+
body: JSON.stringify({
|
|
14397
|
+
sessionId: this.sessionId,
|
|
14398
|
+
clientIP: null, // Let server detect from headers
|
|
14399
|
+
ipDetectionMethod: 'headers',
|
|
14400
|
+
timestamp: new Date().toISOString()
|
|
14401
|
+
})
|
|
14402
|
+
});
|
|
14403
|
+
if (ipResponse.ok) {
|
|
14404
|
+
logDebug('✅ IP info and GeoIP data retrieved successfully');
|
|
14405
|
+
}
|
|
14406
|
+
else {
|
|
14407
|
+
logDebug(`⚠️ IP info request failed: ${ipResponse.statusText}`);
|
|
14408
|
+
}
|
|
14409
|
+
}
|
|
14410
|
+
catch (error) {
|
|
14411
|
+
logDebug(`⚠️ IP info request error: ${error}`);
|
|
14412
|
+
}
|
|
14265
14413
|
// Don't update endUserId - keep it as the original UUID
|
|
14266
14414
|
return originalEndUserId || '';
|
|
14267
14415
|
});
|
|
@@ -14301,7 +14449,7 @@ class HumanBehaviorTracker {
|
|
|
14301
14449
|
// ✅ HUMANBEHAVIOR'S CUSTOM SETTINGS
|
|
14302
14450
|
maskTextSelector: this.redactionManager.getMaskTextSelector() || undefined,
|
|
14303
14451
|
maskTextFn: undefined,
|
|
14304
|
-
maskAllInputs:
|
|
14452
|
+
maskAllInputs: this.redactionManager.getRedactionMode() === 'privacy-first', // Configurable based on strategy
|
|
14305
14453
|
maskInputOptions: { password: true }, // HumanBehavior default
|
|
14306
14454
|
maskInputFn: undefined,
|
|
14307
14455
|
slimDOMOptions: {},
|
|
@@ -14610,6 +14758,17 @@ class HumanBehaviorTracker {
|
|
|
14610
14758
|
this.redactionManager = new RedactionManager(options);
|
|
14611
14759
|
});
|
|
14612
14760
|
}
|
|
14761
|
+
/**
|
|
14762
|
+
* Set specific fields to be redacted (for visibility-first mode)
|
|
14763
|
+
* @param fields Array of CSS selectors for fields to redact
|
|
14764
|
+
*/
|
|
14765
|
+
setRedactedFields(fields) {
|
|
14766
|
+
this.redactionManager.setFieldsToRedact(fields);
|
|
14767
|
+
// ✅ RESTART RECORDING WITH NEW SETTINGS - Ensures redaction is applied
|
|
14768
|
+
if (this.recordInstance) {
|
|
14769
|
+
this.restartWithNewRedaction();
|
|
14770
|
+
}
|
|
14771
|
+
}
|
|
14613
14772
|
/**
|
|
14614
14773
|
* Set specific fields to be unredacted (everything else stays redacted by rrweb)
|
|
14615
14774
|
* @param fields Array of CSS selectors for fields to unredact (e.g., ['#username', '#comment'])
|