@mparticle/web-sdk 2.59.0 → 2.60.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.
@@ -1213,7 +1213,7 @@ export default function mParticleInstance(this: IMParticleWebSDKInstance, instan
1213
1213
  * @method setOptOut
1214
1214
  * @param {Boolean} isOptingOut boolean to opt out or not. When set to true, opt out of logging.
1215
1215
  */
1216
- this.setOptOut = function(isOptingOut) {
1216
+ this.setOptOut = function(isOptingOut: boolean) {
1217
1217
  const queued = queueIfNotInitialized(function() {
1218
1218
  self.setOptOut(isOptingOut);
1219
1219
  }, self);
@@ -1619,14 +1619,10 @@ function createIdentityCache(mpInstance) {
1619
1619
  // Identity expects mpInstance._Identity.idCache to always exist. DisabledVault
1620
1620
  // ensures no identity response data is written to localStorage when noFunctional is true
1621
1621
  if (mpInstance._CookieConsentManager?.getNoFunctional()) {
1622
- return new DisabledVault(`${mpInstance._Store.storageName}-id-cache`, {
1623
- logger: mpInstance.Logger,
1624
- });
1622
+ return new DisabledVault(`${mpInstance._Store.storageName}-id-cache`);
1625
1623
  }
1626
1624
 
1627
- return new LocalStorageVault(`${mpInstance._Store.storageName}-id-cache`, {
1628
- logger: mpInstance.Logger,
1629
- });
1625
+ return new LocalStorageVault(`${mpInstance._Store.storageName}-id-cache`);
1630
1626
  }
1631
1627
 
1632
1628
  function runPreConfigFetchInitialization(mpInstance, apiKey, config) {
@@ -9,6 +9,7 @@ import {
9
9
  isFunction,
10
10
  AttributeValue,
11
11
  isEmpty,
12
+ obfuscateDevData,
12
13
  } from "./utils";
13
14
  import { SDKIdentityApi } from "./identity.interfaces";
14
15
  import { SDKLoggerApi } from "./sdkRuntimeModels";
@@ -234,7 +235,10 @@ export default class RoktManager {
234
235
  const { attributes } = options;
235
236
  const sandboxValue = attributes?.sandbox || null;
236
237
  const mappedAttributes = this.mapPlacementAttributes(attributes, this.placementAttributesMapping);
237
- this.logger?.verbose(`mParticle.Rokt selectPlacements called with attributes:\n${JSON.stringify(attributes, null, 2)}`);
238
+ if (this.logger?.isVerbose()) {
239
+ const attributesToLog = obfuscateDevData(attributes, this.store?.SDKConfig?.isDevelopmentMode);
240
+ this.logger.verbose(`mParticle.Rokt selectPlacements called with attributes:\n${JSON.stringify(attributesToLog, null, 2)}`);
241
+ }
238
242
 
239
243
  this.currentUser = this.identityService.getCurrentUser();
240
244
  const currentUserIdentities = this.currentUser?.getUserIdentities()?.userIdentities || {};
@@ -273,7 +277,7 @@ export default class RoktManager {
273
277
  newIdentities[this.mappedEmailShaIdentityType] = newHashedEmail;
274
278
  this.logger.warning(`emailsha256 mismatch detected. Current mParticle hashedEmail differs from hashedEmail passed to selectPlacements call. Proceeding to call identify with hashedEmail from selectPlacements call. Please verify your implementation.`);
275
279
  }
276
-
280
+
277
281
  if (!isEmpty(newIdentities)) {
278
282
  // Call identify with the new user identities
279
283
  try {
@@ -288,7 +292,8 @@ export default class RoktManager {
288
292
  });
289
293
  });
290
294
  } catch (error) {
291
- this.logger.error('Failed to identify user with new email: ' + JSON.stringify(error));
295
+ const errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
296
+ this.logger.error('Failed to identify user with updated identities: ' + errorMessage);
292
297
  }
293
298
  }
294
299
 
@@ -504,7 +509,10 @@ export default class RoktManager {
504
509
  return;
505
510
  }
506
511
 
507
- this.logger?.verbose(`RoktManager: Processing queued message: ${message.methodName} with payload: ${JSON.stringify(message.payload)}`);
512
+ if (this.logger?.isVerbose()) {
513
+ const payloadToLog = obfuscateDevData(message.payload, this.store?.SDKConfig?.isDevelopmentMode);
514
+ this.logger.verbose(`RoktManager: Processing queued message: ${message.methodName} with payload: ${JSON.stringify(payloadToLog)}`);
515
+ }
508
516
 
509
517
  // Capture resolve/reject functions before async processing
510
518
  const resolve = message.resolve;
@@ -394,6 +394,7 @@ export interface SDKLoggerApi {
394
394
  error(msg: string, code?: ErrorCodes): void;
395
395
  verbose(msg: string): void;
396
396
  warning(msg: string): void;
397
+ isVerbose(): boolean;
397
398
  setLogLevel(logLevel: LogLevelType): void;
398
399
  }
399
400
 
package/src/utils.ts CHANGED
@@ -426,12 +426,71 @@ const filterDictionaryWithHash = <T>(
426
426
  }
427
427
 
428
428
  const parseConfig = (config: SDKInitConfig, moduleName: string, moduleId: number): IKitConfigs | null => {
429
- return config.kitConfigs?.find((kitConfig: IKitConfigs) =>
430
- kitConfig.name === moduleName &&
429
+ return config.kitConfigs?.find((kitConfig: IKitConfigs) =>
430
+ kitConfig.name === moduleName &&
431
431
  kitConfig.moduleId === moduleId
432
432
  ) || null;
433
433
  }
434
434
 
435
+ /**
436
+ * Obfuscates an object by replacing all primitive values with their type names,
437
+ * while preserving the structure of nested objects and arrays.
438
+ * This is useful for logging data structures without exposing PII.
439
+ *
440
+ * @param value - The value to obfuscate
441
+ * @returns The obfuscated value with types instead of actual values
442
+ *
443
+ * @example
444
+ * obfuscateData({ email: 'user@email.com', age: 30 })
445
+ * // Returns: { email: 'string', age: 'number' }
446
+ *
447
+ * obfuscateData({ tags: ['premium', 'verified'] })
448
+ * // Returns: { tags: ['string', 'string'] }
449
+ */
450
+ const obfuscateData = (value: any): any => {
451
+ // Preserve null and undefined
452
+ if (value === null || value === undefined) {
453
+ return value;
454
+ }
455
+
456
+ // Handle arrays - recursively obfuscate each element
457
+ if (Array.isArray(value)) {
458
+ return value.map(item => obfuscateData(item));
459
+ }
460
+
461
+ // Handle objects - recursively obfuscate each property
462
+ if (isObject(value)) {
463
+ const obfuscated: Dictionary<any> = {};
464
+ for (const key in value) {
465
+ if (value.hasOwnProperty(key)) {
466
+ obfuscated[key] = obfuscateData(value[key]);
467
+ }
468
+ }
469
+ return obfuscated;
470
+ }
471
+
472
+ // For primitives and other types, return the type as a string
473
+ return typeof value;
474
+ };
475
+
476
+ /**
477
+ * For verbose logging: returns raw data when isDevelopmentMode is true, else obfuscated data.
478
+ * Used when logging payloads so PII is not exposed in production.
479
+ *
480
+ * @param data - The value to log
481
+ * @param isDevelopmentMode - When true, returns data unchanged, when false or undefined, returns obfuscated data
482
+ * @returns Raw data when isDevelopmentMode is true, otherwise obfuscated structure
483
+ *
484
+ * @example
485
+ * obfuscateDevData({ email: 'user@email.com' }, true)
486
+ * // Returns: { email: 'user@email.com' }
487
+ *
488
+ * obfuscateDevData({ email: 'user@email.com' }, false)
489
+ * // Returns: { email: 'string' }
490
+ */
491
+ const obfuscateDevData = (data: any, isDevelopmentMode?: boolean): any =>
492
+ isDevelopmentMode ? data : obfuscateData(data);
493
+
435
494
  export {
436
495
  createCookieString,
437
496
  revertCookieString,
@@ -440,6 +499,7 @@ export {
440
499
  AttributeValue,
441
500
  converted,
442
501
  decoded,
502
+ obfuscateDevData,
443
503
  filterDictionaryWithHash,
444
504
  findKeyInObject,
445
505
  generateDeprecationMessage,
@@ -449,6 +509,7 @@ export {
449
509
  inArray,
450
510
  isObject,
451
511
  isStringOrNumber,
512
+ obfuscateData,
452
513
  parseConfig,
453
514
  parseNumber,
454
515
  parseSettingsString,
package/src/vault.ts CHANGED
@@ -1,38 +1,22 @@
1
- import { Logger } from '@mparticle/web-sdk';
2
1
  import { isEmpty, isNumber } from './utils';
3
2
 
4
- export interface IVaultOptions {
5
- logger?: Logger;
6
- offlineStorageEnabled?: boolean;
7
- }
8
-
9
3
  export abstract class BaseVault<StorableItem> {
10
4
  public contents: StorableItem;
11
5
  protected readonly _storageKey: string;
12
- protected logger?: Logger;
13
6
  protected storageObject: Storage;
14
7
 
15
8
  /**
16
9
  *
17
10
  * @param {string} storageKey the local storage key string
18
- * @param {Storage} Web API Storage object that is being used
19
- * @param {IVaultOptions} options A Dictionary of IVaultOptions
11
+ * @param {Storage} storageObject Web API Storage object that is being used
20
12
  */
21
13
  constructor(
22
14
  storageKey: string,
23
- storageObject: Storage,
24
- options?: IVaultOptions
15
+ storageObject: Storage
25
16
  ) {
26
17
  this._storageKey = storageKey;
27
18
  this.storageObject = storageObject;
28
19
 
29
- // Add a fake logger in case one is not provided or needed
30
- this.logger = options?.logger || {
31
- verbose: () => {},
32
- warning: () => {},
33
- error: () => {},
34
- };
35
-
36
20
  this.contents = this.retrieve();
37
21
  }
38
22
 
@@ -51,16 +35,7 @@ export abstract class BaseVault<StorableItem> {
51
35
  stringifiedItem = '';
52
36
  }
53
37
 
54
- try {
55
- this.storageObject.setItem(this._storageKey, stringifiedItem);
56
-
57
- this.logger.verbose(`Saving item to Storage: ${stringifiedItem}`);
58
- } catch (error) {
59
- this.logger.error(
60
- `Cannot Save items to Storage: ${stringifiedItem}`
61
- );
62
- this.logger.error(error as string);
63
- }
38
+ this.storageObject.setItem(this._storageKey, stringifiedItem);
64
39
  }
65
40
 
66
41
  /**
@@ -75,8 +50,6 @@ export abstract class BaseVault<StorableItem> {
75
50
 
76
51
  this.contents = item ? JSON.parse(item) : null;
77
52
 
78
- this.logger.verbose(`Retrieving item from Storage: ${item}`);
79
-
80
53
  return this.contents;
81
54
  }
82
55
 
@@ -86,28 +59,27 @@ export abstract class BaseVault<StorableItem> {
86
59
  * @method purge
87
60
  */
88
61
  public purge(): void {
89
- this.logger.verbose('Purging Storage');
90
62
  this.contents = null;
91
63
  this.storageObject.removeItem(this._storageKey);
92
64
  }
93
65
  }
94
66
 
95
67
  export class LocalStorageVault<StorableItem> extends BaseVault<StorableItem> {
96
- constructor(storageKey: string, options?: IVaultOptions) {
97
- super(storageKey, window.localStorage, options);
68
+ constructor(storageKey: string) {
69
+ super(storageKey, window.localStorage);
98
70
  }
99
71
  }
100
72
 
101
73
  export class SessionStorageVault<StorableItem> extends BaseVault<StorableItem> {
102
- constructor(storageKey: string, options?: IVaultOptions) {
103
- super(storageKey, window.sessionStorage, options);
74
+ constructor(storageKey: string) {
75
+ super(storageKey, window.sessionStorage);
104
76
  }
105
77
  }
106
78
 
107
79
  // DisabledVault is used when persistence is disabled by privacy flags.
108
80
  export class DisabledVault<StorableItem> extends BaseVault<StorableItem> {
109
- constructor(storageKey: string, options?: IVaultOptions) {
110
- super(storageKey, window.localStorage, options);
81
+ constructor(storageKey: string) {
82
+ super(storageKey, window.localStorage);
111
83
  this.contents = null;
112
84
  this.storageObject.removeItem(this._storageKey);
113
85
  }