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.
Files changed (50) hide show
  1. package/dist/cjs/angular/index.cjs +176 -17
  2. package/dist/cjs/angular/index.cjs.map +1 -1
  3. package/dist/cjs/index.cjs +176 -17
  4. package/dist/cjs/index.cjs.map +1 -1
  5. package/dist/cjs/install-wizard.cjs +5 -5
  6. package/dist/cjs/install-wizard.cjs.map +1 -1
  7. package/dist/cjs/react/index.cjs +176 -17
  8. package/dist/cjs/react/index.cjs.map +1 -1
  9. package/dist/cjs/remix/index.cjs +176 -17
  10. package/dist/cjs/remix/index.cjs.map +1 -1
  11. package/dist/cjs/svelte/index.cjs +176 -17
  12. package/dist/cjs/svelte/index.cjs.map +1 -1
  13. package/dist/cjs/vue/index.cjs +176 -17
  14. package/dist/cjs/vue/index.cjs.map +1 -1
  15. package/dist/cjs/wizard/index.cjs +5 -5
  16. package/dist/cjs/wizard/index.cjs.map +1 -1
  17. package/dist/cli/ai-auto-install.js +5 -5
  18. package/dist/cli/ai-auto-install.js.map +1 -1
  19. package/dist/cli/auto-install.js +5 -5
  20. package/dist/cli/auto-install.js.map +1 -1
  21. package/dist/esm/angular/index.js +176 -17
  22. package/dist/esm/angular/index.js.map +1 -1
  23. package/dist/esm/index.js +176 -17
  24. package/dist/esm/index.js.map +1 -1
  25. package/dist/esm/install-wizard.js +5 -5
  26. package/dist/esm/install-wizard.js.map +1 -1
  27. package/dist/esm/react/index.js +176 -17
  28. package/dist/esm/react/index.js.map +1 -1
  29. package/dist/esm/remix/index.js +176 -17
  30. package/dist/esm/remix/index.js.map +1 -1
  31. package/dist/esm/svelte/index.js +176 -17
  32. package/dist/esm/svelte/index.js.map +1 -1
  33. package/dist/esm/vue/index.js +176 -17
  34. package/dist/esm/vue/index.js.map +1 -1
  35. package/dist/esm/wizard/index.js +5 -5
  36. package/dist/esm/wizard/index.js.map +1 -1
  37. package/dist/index.min.js +1 -1
  38. package/dist/index.min.js.map +1 -1
  39. package/dist/types/angular/index.d.ts +22 -0
  40. package/dist/types/index.d.ts +40 -2
  41. package/dist/types/install-wizard.d.ts +1 -1
  42. package/dist/types/react/index.d.ts +22 -0
  43. package/dist/types/remix/index.d.ts +22 -0
  44. package/dist/types/svelte/index.d.ts +22 -0
  45. package/dist/types/wizard/index.d.ts +1 -1
  46. package/package.json +2 -1
  47. package/readme.md +59 -5
  48. package/src/redact.ts +135 -15
  49. package/src/tracker.ts +69 -4
  50. package/src/wizard/core/install-wizard.ts +5 -5
@@ -12889,6 +12889,8 @@ class RedactionManager {
12889
12889
  constructor(options) {
12890
12890
  this.redactedText = '[REDACTED]';
12891
12891
  this.unredactedFields = new Set(); // Fields that user wants to unredact
12892
+ this.redactedFields = new Set(); // Fields that user wants to redact
12893
+ this.redactionMode = 'privacy-first';
12892
12894
  this.excludeSelectors = [
12893
12895
  '[data-no-redact="true"]',
12894
12896
  '.human-behavior-no-redact'
@@ -12899,10 +12901,56 @@ class RedactionManager {
12899
12901
  if (options === null || options === void 0 ? void 0 : options.excludeSelectors) {
12900
12902
  this.excludeSelectors = [...this.excludeSelectors, ...options.excludeSelectors];
12901
12903
  }
12904
+ // Handle new redaction strategy
12905
+ if (options === null || options === void 0 ? void 0 : options.redactionStrategy) {
12906
+ this.redactionMode = options.redactionStrategy.mode;
12907
+ if (this.redactionMode === 'privacy-first') {
12908
+ // Privacy-first: everything redacted by default, unredact specific fields
12909
+ if (options.redactionStrategy.unredactFields) {
12910
+ this.setFieldsToUnredact(options.redactionStrategy.unredactFields);
12911
+ }
12912
+ }
12913
+ else {
12914
+ // Visibility-first: everything visible by default, redact specific fields
12915
+ if (options.redactionStrategy.redactFields) {
12916
+ this.setFieldsToRedact(options.redactionStrategy.redactFields);
12917
+ }
12918
+ }
12919
+ }
12920
+ // Handle legacy redactFields (backward compatibility)
12921
+ if (options === null || options === void 0 ? void 0 : options.legacyRedactFields) {
12922
+ this.setFieldsToUnredact(options.legacyRedactFields);
12923
+ }
12924
+ // Handle legacy userFields
12902
12925
  if (options === null || options === void 0 ? void 0 : options.userFields) {
12903
12926
  this.setFieldsToUnredact(options.userFields);
12904
12927
  }
12905
12928
  }
12929
+ /**
12930
+ * Set specific fields to be redacted (for visibility-first mode)
12931
+ * @param fields Array of CSS selectors for fields to redact
12932
+ */
12933
+ setFieldsToRedact(fields) {
12934
+ this.redactedFields.clear();
12935
+ // Always include password fields in redacted list
12936
+ const passwordFields = [
12937
+ 'input[type="password"]',
12938
+ 'input[type="password" i]',
12939
+ '[type="password"]',
12940
+ '[type="password" i]'
12941
+ ];
12942
+ // Add password fields and user-specified fields
12943
+ [...passwordFields, ...fields].forEach(field => {
12944
+ this.redactedFields.add(field);
12945
+ });
12946
+ if (this.redactedFields.size > 0) {
12947
+ logDebug(`Redaction: Active for ${this.redactedFields.size} field(s):`, Array.from(this.redactedFields));
12948
+ }
12949
+ else {
12950
+ logDebug('Redaction: No fields to redact');
12951
+ }
12952
+ this.applyRedactionClasses();
12953
+ }
12906
12954
  /**
12907
12955
  * Set specific fields to be unredacted (everything else stays redacted by rrweb)
12908
12956
  * @param fields Array of CSS selectors for fields to unredact
@@ -12957,6 +13005,12 @@ class RedactionManager {
12957
13005
  hasUnredactedFields() {
12958
13006
  return this.unredactedFields.size > 0;
12959
13007
  }
13008
+ /**
13009
+ * Get the current redaction mode
13010
+ */
13011
+ getRedactionMode() {
13012
+ return this.redactionMode;
13013
+ }
12960
13014
  /**
12961
13015
  * Get the currently unredacted fields
12962
13016
  */
@@ -12968,30 +13022,42 @@ class RedactionManager {
12968
13022
  * Returns null if no fields are unredacted (everything stays redacted)
12969
13023
  */
12970
13024
  getMaskTextSelector() {
12971
- if (this.unredactedFields.size === 0) {
12972
- return null; // Everything stays redacted
13025
+ if (this.redactionMode === 'privacy-first') {
13026
+ // Privacy-first: mask everything except unredacted fields
13027
+ if (this.unredactedFields.size === 0) {
13028
+ return null; // Everything stays redacted
13029
+ }
13030
+ return Array.from(this.unredactedFields).join(',');
13031
+ }
13032
+ else {
13033
+ // Visibility-first: mask only redacted fields
13034
+ if (this.redactedFields.size === 0) {
13035
+ return null; // Nothing to redact
13036
+ }
13037
+ return Array.from(this.redactedFields).join(',');
12973
13038
  }
12974
- return Array.from(this.unredactedFields).join(',');
12975
13039
  }
12976
13040
  /**
12977
- * Check if an element should be unredacted
13041
+ * Apply redaction classes to DOM elements (for visibility-first mode)
13042
+ * Adds 'rr-mask' class to elements that should be redacted
12978
13043
  */
12979
- shouldUnredactElement(element) {
12980
- if (this.unredactedFields.size === 0) {
12981
- return false; // Nothing unredacted
13044
+ applyRedactionClasses() {
13045
+ if (this.redactedFields.size === 0) {
13046
+ return;
12982
13047
  }
12983
- // Check if any selector matches this element
12984
- for (const selector of this.unredactedFields) {
13048
+ // Add 'rr-mask' class to redacted elements
13049
+ this.redactedFields.forEach(selector => {
12985
13050
  try {
12986
- if (element.matches(selector)) {
12987
- return true;
12988
- }
13051
+ const elements = document.querySelectorAll(selector);
13052
+ elements.forEach(element => {
13053
+ element.classList.add('rr-mask');
13054
+ });
13055
+ logDebug(`Added rr-mask class to ${elements.length} element(s) for selector: ${selector}`);
12989
13056
  }
12990
13057
  catch (e) {
12991
13058
  logWarn(`Invalid selector: ${selector}`);
12992
13059
  }
12993
- }
12994
- return false;
13060
+ });
12995
13061
  }
12996
13062
  /**
12997
13063
  * Apply unredaction classes to DOM elements
@@ -13049,6 +13115,47 @@ class RedactionManager {
13049
13115
  isElementUnredacted(element) {
13050
13116
  return this.shouldUnredactElement(element);
13051
13117
  }
13118
+ /**
13119
+ * Check if an element should be unredacted
13120
+ */
13121
+ shouldUnredactElement(element) {
13122
+ if (this.redactionMode === 'privacy-first') {
13123
+ // Privacy-first: check if element is in unredacted fields
13124
+ if (this.unredactedFields.size === 0) {
13125
+ return false; // Nothing unredacted
13126
+ }
13127
+ // Check if any selector matches this element
13128
+ for (const selector of this.unredactedFields) {
13129
+ try {
13130
+ if (element.matches(selector)) {
13131
+ return true;
13132
+ }
13133
+ }
13134
+ catch (e) {
13135
+ logWarn(`Invalid selector: ${selector}`);
13136
+ }
13137
+ }
13138
+ return false;
13139
+ }
13140
+ else {
13141
+ // Visibility-first: check if element is NOT in redacted fields
13142
+ if (this.redactedFields.size === 0) {
13143
+ return true; // Nothing redacted, everything visible
13144
+ }
13145
+ // Check if any selector matches this element
13146
+ for (const selector of this.redactedFields) {
13147
+ try {
13148
+ if (element.matches(selector)) {
13149
+ return false; // Element is redacted
13150
+ }
13151
+ }
13152
+ catch (e) {
13153
+ logWarn(`Invalid selector: ${selector}`);
13154
+ }
13155
+ }
13156
+ return true; // Element is not redacted
13157
+ }
13158
+ }
13052
13159
  }
13053
13160
  // Export a default instance
13054
13161
  new RedactionManager();
@@ -13616,10 +13723,23 @@ class HumanBehaviorTracker {
13616
13723
  });
13617
13724
  // Store canvas recording preference
13618
13725
  tracker.recordCanvas = (_a = options === null || options === void 0 ? void 0 : options.recordCanvas) !== null && _a !== void 0 ? _a : false;
13619
- // Set unredacted fields if specified
13726
+ // Set unredacted fields if specified (legacy support)
13620
13727
  if (options === null || options === void 0 ? void 0 : options.redactFields) {
13621
13728
  tracker.setUnredactedFields(options.redactFields);
13622
13729
  }
13730
+ // Handle new redaction strategy
13731
+ if (options === null || options === void 0 ? void 0 : options.redactionStrategy) {
13732
+ if (options.redactionStrategy.mode === 'privacy-first') {
13733
+ if (options.redactionStrategy.unredactFields) {
13734
+ tracker.setUnredactedFields(options.redactionStrategy.unredactFields);
13735
+ }
13736
+ }
13737
+ else {
13738
+ if (options.redactionStrategy.redactFields) {
13739
+ tracker.setRedactedFields(options.redactionStrategy.redactFields);
13740
+ }
13741
+ }
13742
+ }
13623
13743
  // Setup automatic tracking if enabled
13624
13744
  if ((options === null || options === void 0 ? void 0 : options.enableAutomaticTracking) !== false) {
13625
13745
  tracker.setupAutomaticTracking(options === null || options === void 0 ? void 0 : options.automaticTrackingOptions);
@@ -13665,7 +13785,10 @@ class HumanBehaviorTracker {
13665
13785
  ingestionUrl: ingestionUrl || defaultIngestionUrl
13666
13786
  });
13667
13787
  this.apiKey = apiKey;
13668
- this.redactionManager = new RedactionManager();
13788
+ this.redactionManager = new RedactionManager({
13789
+ redactionStrategy: options === null || options === void 0 ? void 0 : options.redactionStrategy,
13790
+ legacyRedactFields: options === null || options === void 0 ? void 0 : options.redactFields // For backward compatibility
13791
+ });
13669
13792
  // Initialize property manager
13670
13793
  this.propertyManager = new PropertyManager({
13671
13794
  enableAutomaticProperties: (options === null || options === void 0 ? void 0 : options.enableAutomaticProperties) !== false,
@@ -14243,6 +14366,31 @@ class HumanBehaviorTracker {
14243
14366
  if (!userResponse.ok) {
14244
14367
  throw new Error(`Failed to identify user: ${userResponse.statusText}`);
14245
14368
  }
14369
+ // Get IP info and GeoIP data
14370
+ try {
14371
+ const ipResponse = yield fetch(`${this.api['baseUrl']}/api/ingestion/ip-info`, {
14372
+ method: 'POST',
14373
+ headers: {
14374
+ 'Content-Type': 'application/json',
14375
+ 'Authorization': `Bearer ${this.apiKey}`
14376
+ },
14377
+ body: JSON.stringify({
14378
+ sessionId: this.sessionId,
14379
+ clientIP: null, // Let server detect from headers
14380
+ ipDetectionMethod: 'headers',
14381
+ timestamp: new Date().toISOString()
14382
+ })
14383
+ });
14384
+ if (ipResponse.ok) {
14385
+ logDebug('✅ IP info and GeoIP data retrieved successfully');
14386
+ }
14387
+ else {
14388
+ logDebug(`⚠️ IP info request failed: ${ipResponse.statusText}`);
14389
+ }
14390
+ }
14391
+ catch (error) {
14392
+ logDebug(`⚠️ IP info request error: ${error}`);
14393
+ }
14246
14394
  // Don't update endUserId - keep it as the original UUID
14247
14395
  return originalEndUserId || '';
14248
14396
  });
@@ -14282,7 +14430,7 @@ class HumanBehaviorTracker {
14282
14430
  // ✅ HUMANBEHAVIOR'S CUSTOM SETTINGS
14283
14431
  maskTextSelector: this.redactionManager.getMaskTextSelector() || undefined,
14284
14432
  maskTextFn: undefined,
14285
- maskAllInputs: true, // HumanBehavior default
14433
+ maskAllInputs: this.redactionManager.getRedactionMode() === 'privacy-first', // Configurable based on strategy
14286
14434
  maskInputOptions: { password: true }, // HumanBehavior default
14287
14435
  maskInputFn: undefined,
14288
14436
  slimDOMOptions: {},
@@ -14591,6 +14739,17 @@ class HumanBehaviorTracker {
14591
14739
  this.redactionManager = new RedactionManager(options);
14592
14740
  });
14593
14741
  }
14742
+ /**
14743
+ * Set specific fields to be redacted (for visibility-first mode)
14744
+ * @param fields Array of CSS selectors for fields to redact
14745
+ */
14746
+ setRedactedFields(fields) {
14747
+ this.redactionManager.setFieldsToRedact(fields);
14748
+ // ✅ RESTART RECORDING WITH NEW SETTINGS - Ensures redaction is applied
14749
+ if (this.recordInstance) {
14750
+ this.restartWithNewRedaction();
14751
+ }
14752
+ }
14594
14753
  /**
14595
14754
  * Set specific fields to be unredacted (everything else stays redacted by rrweb)
14596
14755
  * @param fields Array of CSS selectors for fields to unredact (e.g., ['#username', '#comment'])