@mparticle/web-sdk 2.57.0 → 2.58.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/mparticle.js CHANGED
@@ -203,7 +203,7 @@ var mParticle = (function () {
203
203
  Base64: Base64$1
204
204
  };
205
205
 
206
- var version = "2.57.0";
206
+ var version = "2.58.0";
207
207
 
208
208
  var Constants = {
209
209
  sdkVersion: version,
@@ -328,7 +328,9 @@ var mParticle = (function () {
328
328
  configUrl: 'jssdkcdns.mparticle.com/JS/v2/',
329
329
  identityUrl: 'identity.mparticle.com/v1/',
330
330
  aliasUrl: 'jssdks.mparticle.com/v1/identity/',
331
- userAudienceUrl: 'nativesdks.mparticle.com/v1/'
331
+ userAudienceUrl: 'nativesdks.mparticle.com/v1/',
332
+ loggingUrl: 'apps.rokt-api.com/v1/log',
333
+ errorUrl: 'apps.rokt-api.com/v1/errors'
332
334
  },
333
335
  // These are the paths that are used to construct the CNAME urls
334
336
  CNAMEUrlPaths: {
@@ -337,7 +339,9 @@ var mParticle = (function () {
337
339
  v3SecureServiceUrl: '/webevents/v3/JS/',
338
340
  configUrl: '/tags/JS/v2/',
339
341
  identityUrl: '/identity/v1/',
340
- aliasUrl: '/webevents/v1/identity/'
342
+ aliasUrl: '/webevents/v1/identity/',
343
+ loggingUrl: '/v1/log',
344
+ errorUrl: '/v1/errors'
341
345
  },
342
346
  Base64CookieKeys: {
343
347
  csm: 1,
@@ -4432,6 +4436,8 @@ var mParticle = (function () {
4432
4436
  for (var prop in Constants.DefaultBaseUrls) {
4433
4437
  sdkConfig[prop] = Constants.DefaultBaseUrls[prop];
4434
4438
  }
4439
+ // Always initialize flags to at least an empty object to prevent undefined access
4440
+ sdkConfig.flags = sdkConfig.flags || {};
4435
4441
  return sdkConfig;
4436
4442
  }
4437
4443
  // TODO: Merge this with SDKStoreApi in sdkRuntimeModels
@@ -4484,6 +4490,8 @@ var mParticle = (function () {
4484
4490
  version: null,
4485
4491
  isInfoSet: false
4486
4492
  },
4493
+ roktAccountId: null,
4494
+ integrationName: null,
4487
4495
  // Placeholder for in-memory persistence model
4488
4496
  persistenceData: {
4489
4497
  gs: {}
@@ -4500,10 +4508,6 @@ var mParticle = (function () {
4500
4508
  if (!config.hasOwnProperty('flags')) {
4501
4509
  this.SDKConfig.flags = {};
4502
4510
  }
4503
- // We process the initial config that is passed via the SDK init
4504
- // and then we will reprocess the config within the processConfig
4505
- // function when the config is updated from the server
4506
- // https://go.mparticle.com/work/SQDSDKS-6317
4507
4511
  this.SDKConfig.flags = processFlags(config);
4508
4512
  if (config.deviceId) {
4509
4513
  this.deviceId = config.deviceId;
@@ -4517,9 +4521,6 @@ var mParticle = (function () {
4517
4521
  for (var baseUrlKeys in baseUrls) {
4518
4522
  this.SDKConfig[baseUrlKeys] = baseUrls[baseUrlKeys];
4519
4523
  }
4520
- if (config.hasOwnProperty('logLevel')) {
4521
- this.SDKConfig.logLevel = config.logLevel;
4522
- }
4523
4524
  this.SDKConfig.useNativeSdk = !!config.useNativeSdk;
4524
4525
  this.SDKConfig.kits = config.kits || {};
4525
4526
  this.SDKConfig.sideloadedKits = config.sideloadedKits || [];
@@ -4742,6 +4743,18 @@ var mParticle = (function () {
4742
4743
  this.setUserIdentities = function (mpid, userIdentities) {
4743
4744
  _this._setPersistence(mpid, 'ui', userIdentities);
4744
4745
  };
4746
+ this.getRoktAccountId = function () {
4747
+ return _this.roktAccountId;
4748
+ };
4749
+ this.setRoktAccountId = function (accountId) {
4750
+ _this.roktAccountId = accountId;
4751
+ };
4752
+ this.getIntegrationName = function () {
4753
+ return _this.integrationName;
4754
+ };
4755
+ this.setIntegrationName = function (integrationName) {
4756
+ _this.integrationName = integrationName;
4757
+ };
4745
4758
  this.addMpidToSessionHistory = function (mpid, previousMPID) {
4746
4759
  var indexOfMPID = _this.currentSessionMPIDs.indexOf(mpid);
4747
4760
  if (mpid && previousMPID !== mpid && indexOfMPID < 0) {
@@ -4766,7 +4779,10 @@ var mParticle = (function () {
4766
4779
  // We should reprocess the flags and baseUrls in case they have changed when we request an updated config
4767
4780
  // such as if the SDK is being self-hosted and the flags are different on the server config
4768
4781
  // https://go.mparticle.com/work/SQDSDKS-6317
4769
- _this.SDKConfig.flags = processFlags(config);
4782
+ // Only update flags if the config actually has flags (don't overwrite with empty object)
4783
+ if (config.flags) {
4784
+ _this.SDKConfig.flags = processFlags(config);
4785
+ }
4770
4786
  var baseUrls = processBaseUrls(config, _this.SDKConfig.flags, apiKey);
4771
4787
  for (var baseUrlKeys in baseUrls) {
4772
4788
  _this.SDKConfig[baseUrlKeys] = baseUrls[baseUrlKeys];
@@ -4866,7 +4882,7 @@ var mParticle = (function () {
4866
4882
  for (var baseUrlKey in defaultBaseUrls) {
4867
4883
  // Any custom endpoints passed to mpConfig will take priority over direct
4868
4884
  // mapping to the silo. The most common use case is a customer provided CNAME.
4869
- if (baseUrlKey === 'configUrl') {
4885
+ if (baseUrlKey === 'configUrl' || baseUrlKey === 'loggingUrl' || baseUrlKey === 'errorUrl') {
4870
4886
  directBaseUrls[baseUrlKey] = config[baseUrlKey] || defaultBaseUrls[baseUrlKey];
4871
4887
  continue;
4872
4888
  }
@@ -4881,10 +4897,11 @@ var mParticle = (function () {
4881
4897
  }
4882
4898
 
4883
4899
  var Logger = /** @class */function () {
4884
- function Logger(config) {
4900
+ function Logger(config, reportingLogger) {
4885
4901
  var _a, _b;
4886
4902
  this.logLevel = (_a = config.logLevel) !== null && _a !== void 0 ? _a : LogLevelType.Warning;
4887
4903
  this.logger = (_b = config.logger) !== null && _b !== void 0 ? _b : new ConsoleLogger();
4904
+ this.reportingLogger = reportingLogger;
4888
4905
  }
4889
4906
  Logger.prototype.verbose = function (msg) {
4890
4907
  if (this.logLevel === LogLevelType.None) return;
@@ -4898,10 +4915,14 @@ var mParticle = (function () {
4898
4915
  this.logger.warning(msg);
4899
4916
  }
4900
4917
  };
4901
- Logger.prototype.error = function (msg) {
4918
+ Logger.prototype.error = function (msg, codeForReporting) {
4919
+ var _a;
4902
4920
  if (this.logLevel === LogLevelType.None) return;
4903
4921
  if (this.logger.error) {
4904
4922
  this.logger.error(msg);
4923
+ if (codeForReporting) {
4924
+ (_a = this.reportingLogger) === null || _a === void 0 ? void 0 : _a.error(msg, codeForReporting);
4925
+ }
4905
4926
  }
4906
4927
  };
4907
4928
  Logger.prototype.setLogLevel = function (newLogLevel) {
@@ -9161,6 +9182,17 @@ var mParticle = (function () {
9161
9182
  };
9162
9183
  }
9163
9184
 
9185
+ var ErrorCodes = {
9186
+ UNKNOWN_ERROR: 'UNKNOWN_ERROR',
9187
+ UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',
9188
+ IDENTITY_REQUEST: 'IDENTITY_REQUEST'
9189
+ };
9190
+ var WSDKErrorSeverity = {
9191
+ ERROR: 'ERROR',
9192
+ INFO: 'INFO',
9193
+ WARNING: 'WARNING'
9194
+ };
9195
+
9164
9196
  var HTTPCodes$1 = Constants.HTTPCodes,
9165
9197
  Messages$1 = Constants.Messages,
9166
9198
  IdentityMethods = Constants.IdentityMethods;
@@ -9373,7 +9405,7 @@ var mParticle = (function () {
9373
9405
  mpInstance.captureTiming("".concat(requestCount, "-identityRequestEnd"));
9374
9406
  }
9375
9407
  errorMessage = err_1.message || err_1.toString();
9376
- Logger.error('Error sending identity request to servers - ' + errorMessage);
9408
+ Logger.error('Error sending identity request to servers' + ' - ' + errorMessage, ErrorCodes.IDENTITY_REQUEST);
9377
9409
  (_d = mpInstance.processQueueOnIdentityFailure) === null || _d === void 0 ? void 0 : _d.call(mpInstance);
9378
9410
  invokeCallback(callback, HTTPCodes$1.noHttpCoverage, errorMessage);
9379
9411
  return [3 /*break*/, 10];
@@ -9793,13 +9825,19 @@ var mParticle = (function () {
9793
9825
  configurable: true
9794
9826
  });
9795
9827
  RoktManager.prototype.attachKit = function (kit) {
9796
- var _a, _b;
9828
+ var _a, _b, _c, _d;
9797
9829
  this.kit = kit;
9830
+ if ((_a = kit.settings) === null || _a === void 0 ? void 0 : _a.accountId) {
9831
+ this.store.setRoktAccountId(kit.settings.accountId);
9832
+ }
9833
+ if (kit.integrationName) {
9834
+ (_b = this.store) === null || _b === void 0 ? void 0 : _b.setIntegrationName(kit.integrationName);
9835
+ }
9798
9836
  this.processMessageQueue();
9799
9837
  try {
9800
- (_a = this.onReadyCallback) === null || _a === void 0 ? void 0 : _a.call(this);
9838
+ (_c = this.onReadyCallback) === null || _c === void 0 ? void 0 : _c.call(this);
9801
9839
  } catch (e) {
9802
- (_b = this.logger) === null || _b === void 0 ? void 0 : _b.error('RoktManager: Error in onReadyCallback: ' + e);
9840
+ (_d = this.logger) === null || _d === void 0 ? void 0 : _d.error('RoktManager: Error in onReadyCallback: ' + e);
9803
9841
  }
9804
9842
  };
9805
9843
  /**
@@ -10239,6 +10277,138 @@ var mParticle = (function () {
10239
10277
  return CookieConsentManager;
10240
10278
  }();
10241
10279
 
10280
+ // Header key constants
10281
+ var HEADER_ACCEPT = 'Accept';
10282
+ var HEADER_CONTENT_TYPE = 'Content-Type';
10283
+ var HEADER_ROKT_LAUNCHER_VERSION = 'rokt-launcher-version';
10284
+ var HEADER_ROKT_LAUNCHER_INSTANCE_GUID = 'rokt-launcher-instance-guid';
10285
+ var HEADER_ROKT_WSDK_VERSION = 'rokt-wsdk-version';
10286
+ var ReportingLogger = /** @class */function () {
10287
+ function ReportingLogger(config, sdkVersion, store, launcherInstanceGuid, rateLimiter) {
10288
+ var _this = this;
10289
+ this.sdkVersion = sdkVersion;
10290
+ this.launcherInstanceGuid = launcherInstanceGuid;
10291
+ this.reporter = 'mp-wsdk';
10292
+ this.isFeatureFlagEnabled = function () {
10293
+ return _this.isLoggingEnabled;
10294
+ };
10295
+ this.loggingUrl = "https://".concat(config.loggingUrl || Constants.DefaultBaseUrls.loggingUrl);
10296
+ this.errorUrl = "https://".concat(config.errorUrl || Constants.DefaultBaseUrls.errorUrl);
10297
+ this.isLoggingEnabled = config.isLoggingEnabled || false;
10298
+ this.store = store !== null && store !== void 0 ? store : null;
10299
+ this.isEnabled = this.isReportingEnabled();
10300
+ this.rateLimiter = rateLimiter !== null && rateLimiter !== void 0 ? rateLimiter : new RateLimiter();
10301
+ }
10302
+ ReportingLogger.prototype.setStore = function (store) {
10303
+ this.store = store;
10304
+ };
10305
+ ReportingLogger.prototype.info = function (msg, code) {
10306
+ this.sendLog(WSDKErrorSeverity.INFO, msg, code);
10307
+ };
10308
+ ReportingLogger.prototype.error = function (msg, code, stackTrace) {
10309
+ this.sendError(WSDKErrorSeverity.ERROR, msg, code, stackTrace);
10310
+ };
10311
+ ReportingLogger.prototype.warning = function (msg, code) {
10312
+ this.sendError(WSDKErrorSeverity.WARNING, msg, code);
10313
+ };
10314
+ ReportingLogger.prototype.sendToServer = function (url, severity, msg, code, stackTrace) {
10315
+ if (!this.canSendLog(severity)) return;
10316
+ try {
10317
+ var logRequest = this.buildLogRequest(severity, msg, code, stackTrace);
10318
+ var uploader = new FetchUploader(url);
10319
+ var payload = {
10320
+ method: 'POST',
10321
+ headers: this.getHeaders(),
10322
+ body: JSON.stringify(logRequest)
10323
+ };
10324
+ uploader.upload(payload)["catch"](function (error) {
10325
+ console.error('ReportingLogger: Failed to send log', error);
10326
+ });
10327
+ } catch (error) {
10328
+ console.error('ReportingLogger: Failed to send log', error);
10329
+ }
10330
+ };
10331
+ ReportingLogger.prototype.sendLog = function (severity, msg, code, stackTrace) {
10332
+ this.sendToServer(this.loggingUrl, severity, msg, code, stackTrace);
10333
+ };
10334
+ ReportingLogger.prototype.sendError = function (severity, msg, code, stackTrace) {
10335
+ this.sendToServer(this.errorUrl, severity, msg, code, stackTrace);
10336
+ };
10337
+ ReportingLogger.prototype.buildLogRequest = function (severity, msg, code, stackTrace) {
10338
+ var _a, _b;
10339
+ return {
10340
+ additionalInformation: {
10341
+ message: msg,
10342
+ version: this.getVersion()
10343
+ },
10344
+ severity: severity,
10345
+ code: code !== null && code !== void 0 ? code : ErrorCodes.UNKNOWN_ERROR,
10346
+ url: this.getUrl(),
10347
+ deviceInfo: this.getUserAgent(),
10348
+ stackTrace: stackTrace,
10349
+ reporter: this.reporter,
10350
+ // Integration will be set to integrationName once the kit connects via RoktManager.attachKit()
10351
+ integration: (_b = (_a = this.store) === null || _a === void 0 ? void 0 : _a.getIntegrationName()) !== null && _b !== void 0 ? _b : 'mp-wsdk'
10352
+ };
10353
+ };
10354
+ ReportingLogger.prototype.getVersion = function () {
10355
+ var _a, _b, _c;
10356
+ return (_c = (_b = (_a = this.store) === null || _a === void 0 ? void 0 : _a.getIntegrationName) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : "mParticle_wsdkv_".concat(this.sdkVersion);
10357
+ };
10358
+ ReportingLogger.prototype.isReportingEnabled = function () {
10359
+ return this.isDebugModeEnabled() || this.isRoktDomainPresent() && this.isFeatureFlagEnabled();
10360
+ };
10361
+ ReportingLogger.prototype.isRoktDomainPresent = function () {
10362
+ return typeof window !== 'undefined' && Boolean(window['ROKT_DOMAIN']);
10363
+ };
10364
+ ReportingLogger.prototype.isDebugModeEnabled = function () {
10365
+ var _a, _b, _c, _d;
10366
+ return typeof window !== 'undefined' && ((_d = (_c = (_b = (_a = window.location) === null || _a === void 0 ? void 0 : _a.search) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === null || _c === void 0 ? void 0 : _c.includes('mp_enable_logging=true')) !== null && _d !== void 0 ? _d : false);
10367
+ };
10368
+ ReportingLogger.prototype.canSendLog = function (severity) {
10369
+ return this.isEnabled && !this.isRateLimited(severity);
10370
+ };
10371
+ ReportingLogger.prototype.isRateLimited = function (severity) {
10372
+ return this.rateLimiter.incrementAndCheck(severity);
10373
+ };
10374
+ ReportingLogger.prototype.getUrl = function () {
10375
+ var _a;
10376
+ return typeof window !== 'undefined' ? (_a = window.location) === null || _a === void 0 ? void 0 : _a.href : undefined;
10377
+ };
10378
+ ReportingLogger.prototype.getUserAgent = function () {
10379
+ var _a;
10380
+ return typeof window !== 'undefined' ? (_a = window.navigator) === null || _a === void 0 ? void 0 : _a.userAgent : undefined;
10381
+ };
10382
+ ReportingLogger.prototype.getHeaders = function () {
10383
+ var _a;
10384
+ var _b, _c;
10385
+ var headers = (_a = {}, _a[HEADER_ACCEPT] = 'text/plain;charset=UTF-8', _a[HEADER_CONTENT_TYPE] = 'application/json', _a[HEADER_ROKT_LAUNCHER_VERSION] = this.getVersion(), _a[HEADER_ROKT_WSDK_VERSION] = 'joint', _a);
10386
+ if (this.launcherInstanceGuid) {
10387
+ headers[HEADER_ROKT_LAUNCHER_INSTANCE_GUID] = this.launcherInstanceGuid;
10388
+ }
10389
+ var accountId = (_c = (_b = this.store) === null || _b === void 0 ? void 0 : _b.getRoktAccountId) === null || _c === void 0 ? void 0 : _c.call(_b);
10390
+ if (accountId) {
10391
+ headers['rokt-account-id'] = accountId;
10392
+ }
10393
+ return headers;
10394
+ };
10395
+ return ReportingLogger;
10396
+ }();
10397
+ var RateLimiter = /** @class */function () {
10398
+ function RateLimiter() {
10399
+ this.rateLimits = new Map([[WSDKErrorSeverity.ERROR, 10], [WSDKErrorSeverity.WARNING, 10], [WSDKErrorSeverity.INFO, 10]]);
10400
+ this.logCount = new Map();
10401
+ }
10402
+ RateLimiter.prototype.incrementAndCheck = function (severity) {
10403
+ var count = this.logCount.get(severity) || 0;
10404
+ var limit = this.rateLimits.get(severity) || 10;
10405
+ var newCount = count + 1;
10406
+ this.logCount.set(severity, newCount);
10407
+ return newCount > limit;
10408
+ };
10409
+ return RateLimiter;
10410
+ }();
10411
+
10242
10412
  var Messages = Constants.Messages,
10243
10413
  HTTPCodes = Constants.HTTPCodes,
10244
10414
  FeatureFlags = Constants.FeatureFlags,
@@ -10363,12 +10533,14 @@ var mParticle = (function () {
10363
10533
  console.error('Cannot reset mParticle', error);
10364
10534
  }
10365
10535
  };
10366
- this._resetForTests = function (config, keepPersistence, instance) {
10536
+ this._resetForTests = function (config, keepPersistence, instance, reportingLogger) {
10367
10537
  if (instance._Store) {
10368
10538
  delete instance._Store;
10369
10539
  }
10370
- instance.Logger = new Logger(config);
10540
+ instance.Logger = new Logger(config, reportingLogger);
10371
10541
  instance._Store = new Store(config, instance);
10542
+ // Update ReportingLogger with the new Store reference to avoid stale data
10543
+ reportingLogger === null || reportingLogger === void 0 ? void 0 : reportingLogger.setStore(instance._Store);
10372
10544
  instance._Store.isLocalStorageAvailable = instance._Persistence.determineLocalStorageAvailability(window.localStorage);
10373
10545
  instance._Events.stopTracking();
10374
10546
  if (!keepPersistence) {
@@ -11185,6 +11357,9 @@ var mParticle = (function () {
11185
11357
  window[launcherInstanceGuidKey] = self._Helpers.generateUniqueId();
11186
11358
  }
11187
11359
  };
11360
+ this.getLauncherInstanceGuid = function () {
11361
+ return window[launcherInstanceGuidKey];
11362
+ };
11188
11363
  this.captureTiming = function (metricsName) {
11189
11364
  var _a;
11190
11365
  if (typeof window !== 'undefined' && ((_a = window.performance) === null || _a === void 0 ? void 0 : _a.mark)) {
@@ -11194,6 +11369,7 @@ var mParticle = (function () {
11194
11369
  }
11195
11370
  // Some (server) config settings need to be returned before they are set on SDKConfig in a self hosted environment
11196
11371
  function completeSDKInitialization(apiKey, config, mpInstance) {
11372
+ var _a;
11197
11373
  var kitBlocker = createKitBlocker(config, mpInstance);
11198
11374
  var getFeatureFlag = mpInstance._Helpers.getFeatureFlag;
11199
11375
  mpInstance._APIClient = new APIClient(mpInstance, kitBlocker);
@@ -11239,6 +11415,8 @@ var mParticle = (function () {
11239
11415
  // Configure Rokt Manager with user and filtered user
11240
11416
  var roktConfig = parseConfig(config, 'Rokt', 181);
11241
11417
  if (roktConfig) {
11418
+ var accountId = (_a = roktConfig.settings) === null || _a === void 0 ? void 0 : _a.accountId;
11419
+ mpInstance._Store.setRoktAccountId(accountId);
11242
11420
  var userAttributeFilters = roktConfig.userAttributeFilters;
11243
11421
  var roktFilteredUser = filteredMparticleUser(currentUserMPID, {
11244
11422
  userAttributeFilters: userAttributeFilters
@@ -11336,9 +11514,11 @@ var mParticle = (function () {
11336
11514
  }
11337
11515
  function runPreConfigFetchInitialization(mpInstance, apiKey, config) {
11338
11516
  var _a;
11339
- mpInstance.Logger = new Logger(config);
11517
+ mpInstance._ReportingLogger = new ReportingLogger(config, Constants.sdkVersion, undefined, mpInstance.getLauncherInstanceGuid());
11518
+ mpInstance.Logger = new Logger(config, mpInstance._ReportingLogger);
11340
11519
  mpInstance._Store = new Store(config, mpInstance, apiKey);
11341
11520
  window.mParticle.Store = mpInstance._Store;
11521
+ mpInstance._ReportingLogger.setStore(mpInstance._Store);
11342
11522
  mpInstance.Logger.verbose(StartingInitialization);
11343
11523
  // Initialize CookieConsentManager with privacy flags from launcherOptions
11344
11524
  var _b = (_a = config === null || config === void 0 ? void 0 : config.launcherOptions) !== null && _a !== void 0 ? _a : {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mparticle/web-sdk",
3
- "version": "2.57.0",
3
+ "version": "2.58.0",
4
4
  "description": "mParticle core SDK for web applications",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
package/src/constants.ts CHANGED
@@ -139,6 +139,8 @@ const Constants = {
139
139
  identityUrl: 'identity.mparticle.com/v1/',
140
140
  aliasUrl: 'jssdks.mparticle.com/v1/identity/',
141
141
  userAudienceUrl: 'nativesdks.mparticle.com/v1/',
142
+ loggingUrl: 'apps.rokt-api.com/v1/log',
143
+ errorUrl: 'apps.rokt-api.com/v1/errors',
142
144
  },
143
145
  // These are the paths that are used to construct the CNAME urls
144
146
  CNAMEUrlPaths: {
@@ -148,6 +150,8 @@ const Constants = {
148
150
  configUrl: '/tags/JS/v2/',
149
151
  identityUrl: '/identity/v1/',
150
152
  aliasUrl: '/webevents/v1/identity/',
153
+ loggingUrl: '/v1/log',
154
+ errorUrl: '/v1/errors',
151
155
  },
152
156
  Base64CookieKeys: {
153
157
  csm: 1,
@@ -25,6 +25,7 @@ import {
25
25
  IIdentityResponse,
26
26
  } from './identity-user-interfaces';
27
27
  import { IMParticleWebSDKInstance } from './mp-instance';
28
+ import { ErrorCodes } from './logging/types';
28
29
 
29
30
  const { HTTPCodes, Messages, IdentityMethods } = Constants;
30
31
 
@@ -326,10 +327,13 @@ export default function IdentityAPIClient(
326
327
  const requestCount = mpInstance._Store.identifyRequestCount;
327
328
  mpInstance.captureTiming(`${requestCount}-identityRequestEnd`);
328
329
  }
329
-
330
+
330
331
  const errorMessage = (err as Error).message || err.toString();
331
- Logger.error('Error sending identity request to servers - ' + errorMessage);
332
-
332
+ Logger.error(
333
+ 'Error sending identity request to servers' + ' - ' + errorMessage,
334
+ ErrorCodes.IDENTITY_REQUEST
335
+ );
336
+
333
337
  mpInstance.processQueueOnIdentityFailure?.();
334
338
  invokeCallback(callback, HTTPCodes.noHttpCoverage, errorMessage);
335
339
  }
package/src/logger.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { LogLevelType, SDKInitConfig, SDKLoggerApi } from './sdkRuntimeModels';
2
+ import { ReportingLogger } from './logging/reportingLogger';
3
+ import { ErrorCodes } from './logging/types';
2
4
 
3
5
  export type ILoggerConfig = Pick<SDKInitConfig, 'logLevel' | 'logger'>;
4
6
  export type IConsoleLogger = Partial<Pick<SDKLoggerApi, 'error' | 'warning' | 'verbose'>>;
@@ -6,10 +8,14 @@ export type IConsoleLogger = Partial<Pick<SDKLoggerApi, 'error' | 'warning' | 'v
6
8
  export class Logger {
7
9
  private logLevel: LogLevelType;
8
10
  private logger: IConsoleLogger;
11
+ private reportingLogger: ReportingLogger;
9
12
 
10
- constructor(config: ILoggerConfig) {
13
+ constructor(config: ILoggerConfig,
14
+ reportingLogger?: ReportingLogger,
15
+ ) {
11
16
  this.logLevel = config.logLevel ?? LogLevelType.Warning;
12
17
  this.logger = config.logger ?? new ConsoleLogger();
18
+ this.reportingLogger = reportingLogger;
13
19
  }
14
20
 
15
21
  public verbose(msg: string): void {
@@ -22,21 +28,24 @@ export class Logger {
22
28
  }
23
29
 
24
30
  public warning(msg: string): void {
25
- if(this.logLevel === LogLevelType.None)
31
+ if(this.logLevel === LogLevelType.None)
26
32
  return;
27
33
 
28
- if (this.logger.warning &&
34
+ if (this.logger.warning &&
29
35
  (this.logLevel === LogLevelType.Verbose || this.logLevel === LogLevelType.Warning)) {
30
36
  this.logger.warning(msg);
31
37
  }
32
38
  }
33
39
 
34
- public error(msg: string): void {
35
- if(this.logLevel === LogLevelType.None)
40
+ public error(msg: string, codeForReporting?: ErrorCodes): void {
41
+ if(this.logLevel === LogLevelType.None)
36
42
  return;
37
43
 
38
44
  if (this.logger.error) {
39
45
  this.logger.error(msg);
46
+ if (codeForReporting) {
47
+ this.reportingLogger?.error(msg, codeForReporting);
48
+ }
40
49
  }
41
50
  }
42
51
 
@@ -0,0 +1,192 @@
1
+ import { ErrorCodes, LogRequestBody, WSDKErrorSeverity } from "./types";
2
+ import { FetchUploader, IFetchPayload } from "../uploaders";
3
+ import { IStore, SDKConfig } from "../store";
4
+ import { SDKInitConfig } from "../sdkRuntimeModels";
5
+ import Constants from "../constants";
6
+
7
+ // Header key constants
8
+ const HEADER_ACCEPT = 'Accept' as const;
9
+ const HEADER_CONTENT_TYPE = 'Content-Type' as const;
10
+ const HEADER_ROKT_LAUNCHER_VERSION = 'rokt-launcher-version' as const;
11
+ const HEADER_ROKT_LAUNCHER_INSTANCE_GUID = 'rokt-launcher-instance-guid' as const;
12
+ const HEADER_ROKT_WSDK_VERSION = 'rokt-wsdk-version' as const;
13
+
14
+ interface IReportingLoggerPayload extends IFetchPayload {
15
+ headers: IFetchPayload['headers'] & {
16
+ [HEADER_ROKT_LAUNCHER_INSTANCE_GUID]?: string;
17
+ [HEADER_ROKT_LAUNCHER_VERSION]: string;
18
+ [HEADER_ROKT_WSDK_VERSION]: string;
19
+ };
20
+ body: string;
21
+ }
22
+
23
+ export class ReportingLogger {
24
+ private readonly isEnabled: boolean;
25
+ private readonly reporter: string = 'mp-wsdk';
26
+ private readonly rateLimiter: IRateLimiter;
27
+ private store: IStore | null;
28
+ private readonly loggingUrl: string;
29
+ private readonly errorUrl: string;
30
+ private readonly isLoggingEnabled: boolean;
31
+
32
+ constructor(
33
+ config: SDKConfig | SDKInitConfig | any,
34
+ private readonly sdkVersion: string,
35
+ store?: IStore,
36
+ private readonly launcherInstanceGuid?: string,
37
+ rateLimiter?: IRateLimiter,
38
+ ) {
39
+ this.loggingUrl = `https://${config.loggingUrl || Constants.DefaultBaseUrls.loggingUrl}`;
40
+ this.errorUrl = `https://${config.errorUrl || Constants.DefaultBaseUrls.errorUrl}`;
41
+ this.isLoggingEnabled = config.isLoggingEnabled || false;
42
+ this.store = store ?? null;
43
+ this.isEnabled = this.isReportingEnabled();
44
+ this.rateLimiter = rateLimiter ?? new RateLimiter();
45
+ }
46
+
47
+ public setStore(store: IStore): void {
48
+ this.store = store;
49
+ }
50
+
51
+ public info(msg: string, code?: ErrorCodes) {
52
+ this.sendLog(WSDKErrorSeverity.INFO, msg, code);
53
+ }
54
+
55
+ public error(msg: string, code?: ErrorCodes, stackTrace?: string) {
56
+ this.sendError(WSDKErrorSeverity.ERROR, msg, code, stackTrace);
57
+ }
58
+
59
+ public warning(msg: string, code?: ErrorCodes) {
60
+ this.sendError(WSDKErrorSeverity.WARNING, msg, code);
61
+ }
62
+
63
+ private sendToServer(url: string, severity: WSDKErrorSeverity, msg: string, code?: ErrorCodes, stackTrace?: string): void {
64
+ if (!this.canSendLog(severity))
65
+ return;
66
+
67
+ try {
68
+ const logRequest = this.buildLogRequest(severity, msg, code, stackTrace);
69
+ const uploader = new FetchUploader(url);
70
+ const payload: IReportingLoggerPayload = {
71
+ method: 'POST',
72
+ headers: this.getHeaders(),
73
+ body: JSON.stringify(logRequest),
74
+ };
75
+ uploader.upload(payload).catch((error) => {
76
+ console.error('ReportingLogger: Failed to send log', error);
77
+ });
78
+ } catch (error) {
79
+ console.error('ReportingLogger: Failed to send log', error);
80
+ }
81
+ }
82
+
83
+ private sendLog(severity: WSDKErrorSeverity, msg: string, code?: ErrorCodes, stackTrace?: string): void {
84
+ this.sendToServer(this.loggingUrl, severity, msg, code, stackTrace);
85
+ }
86
+
87
+ private sendError(severity: WSDKErrorSeverity, msg: string, code?: ErrorCodes, stackTrace?: string): void {
88
+ this.sendToServer(this.errorUrl, severity, msg, code, stackTrace);
89
+ }
90
+
91
+ private buildLogRequest(severity: WSDKErrorSeverity, msg: string, code?: ErrorCodes, stackTrace?: string): LogRequestBody {
92
+ return {
93
+ additionalInformation: {
94
+ message: msg,
95
+ version: this.getVersion(),
96
+ },
97
+ severity: severity,
98
+ code: code ?? ErrorCodes.UNKNOWN_ERROR,
99
+ url: this.getUrl(),
100
+ deviceInfo: this.getUserAgent(),
101
+ stackTrace: stackTrace,
102
+ reporter: this.reporter,
103
+ // Integration will be set to integrationName once the kit connects via RoktManager.attachKit()
104
+ integration: this.store?.getIntegrationName() ?? 'mp-wsdk'
105
+ };
106
+ }
107
+
108
+ private getVersion(): string {
109
+ return this.store?.getIntegrationName?.() ?? `mParticle_wsdkv_${this.sdkVersion}`;
110
+ }
111
+
112
+ private isReportingEnabled(): boolean {
113
+ return this.isDebugModeEnabled() ||
114
+ (this.isRoktDomainPresent() && this.isFeatureFlagEnabled());
115
+ }
116
+
117
+ private isRoktDomainPresent(): boolean {
118
+ return typeof window !== 'undefined' && Boolean(window['ROKT_DOMAIN']);
119
+ }
120
+
121
+ private isFeatureFlagEnabled = (): boolean => this.isLoggingEnabled;
122
+
123
+ private isDebugModeEnabled(): boolean {
124
+ return (
125
+ typeof window !== 'undefined' &&
126
+ (window.
127
+ location?.
128
+ search?.
129
+ toLowerCase()?.
130
+ includes('mp_enable_logging=true') ?? false)
131
+ );
132
+ }
133
+
134
+ private canSendLog(severity: WSDKErrorSeverity): boolean {
135
+ return this.isEnabled && !this.isRateLimited(severity);
136
+ }
137
+
138
+ private isRateLimited(severity: WSDKErrorSeverity): boolean {
139
+ return this.rateLimiter.incrementAndCheck(severity);
140
+ }
141
+
142
+ private getUrl(): string | undefined {
143
+ return typeof window !== 'undefined' ? window.location?.href : undefined;
144
+ }
145
+
146
+ private getUserAgent(): string | undefined {
147
+ return typeof window !== 'undefined' ? window.navigator?.userAgent : undefined;
148
+ }
149
+
150
+ private getHeaders(): IReportingLoggerPayload['headers'] {
151
+ const headers: IReportingLoggerPayload['headers'] = {
152
+ [HEADER_ACCEPT]: 'text/plain;charset=UTF-8',
153
+ [HEADER_CONTENT_TYPE]: 'application/json',
154
+ [HEADER_ROKT_LAUNCHER_VERSION]: this.getVersion(),
155
+ [HEADER_ROKT_WSDK_VERSION]: 'joint',
156
+ };
157
+
158
+ if (this.launcherInstanceGuid) {
159
+ headers[HEADER_ROKT_LAUNCHER_INSTANCE_GUID] = this.launcherInstanceGuid;
160
+ }
161
+
162
+ const accountId = this.store?.getRoktAccountId?.();
163
+ if (accountId) {
164
+ headers['rokt-account-id'] = accountId;
165
+ }
166
+
167
+ return headers;
168
+ }
169
+ }
170
+
171
+ export interface IRateLimiter {
172
+ incrementAndCheck(severity: WSDKErrorSeverity): boolean;
173
+ }
174
+
175
+ export class RateLimiter implements IRateLimiter {
176
+ private readonly rateLimits: Map<WSDKErrorSeverity, number> = new Map([
177
+ [WSDKErrorSeverity.ERROR, 10],
178
+ [WSDKErrorSeverity.WARNING, 10],
179
+ [WSDKErrorSeverity.INFO, 10],
180
+ ]);
181
+ private logCount: Map<WSDKErrorSeverity, number> = new Map();
182
+
183
+ public incrementAndCheck(severity: WSDKErrorSeverity): boolean {
184
+ const count = this.logCount.get(severity) || 0;
185
+ const limit = this.rateLimits.get(severity) || 10;
186
+
187
+ const newCount = count + 1;
188
+ this.logCount.set(severity, newCount);
189
+
190
+ return newCount > limit;
191
+ }
192
+ }