UNPKG

@amplitude/analytics-browser

Version:
448 lines 26.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AmplitudeBrowser = void 0; var tslib_1 = require("tslib"); var analytics_core_1 = require("@amplitude/analytics-core"); var default_tracking_1 = require("./default-tracking"); var snippet_helper_1 = require("./utils/snippet-helper"); var context_1 = require("./plugins/context"); var config_1 = require("./config"); var plugin_page_view_tracking_browser_1 = require("@amplitude/plugin-page-view-tracking-browser"); var form_interaction_tracking_1 = require("./plugins/form-interaction-tracking"); var file_download_tracking_1 = require("./plugins/file-download-tracking"); var constants_1 = require("./constants"); var det_notification_1 = require("./det-notification"); var network_connectivity_checker_1 = require("./plugins/network-connectivity-checker"); var joined_config_1 = require("./config/joined-config"); var plugin_autocapture_browser_1 = require("@amplitude/plugin-autocapture-browser"); var plugin_network_capture_browser_1 = require("@amplitude/plugin-network-capture-browser"); var plugin_web_vitals_browser_1 = require("@amplitude/plugin-web-vitals-browser"); var web_attribution_1 = require("./attribution/web-attribution"); var lib_prefix_1 = require("./lib-prefix"); var version_1 = require("./version"); var plugin_page_url_enrichment_browser_1 = require("@amplitude/plugin-page-url-enrichment-browser"); /** * Exported for `@amplitude/unified` or integration with blade plugins. * If you only use `@amplitude/analytics-browser`, use `amplitude.init()` or `amplitude.createInstance()` instead. */ var AmplitudeBrowser = /** @class */ (function (_super) { tslib_1.__extends(AmplitudeBrowser, _super); function AmplitudeBrowser() { var _this = _super !== null && _super.apply(this, arguments) || this; // Backdoor to set diagnostics sample rate // by calling amplitude._setDiagnosticsSampleRate(1); before amplitude.init() _this._diagnosticsSampleRate = 0; return _this; } AmplitudeBrowser.prototype.init = function (apiKey, userIdOrOptions, maybeOptions) { if (apiKey === void 0) { apiKey = ''; } var userId; var options; if (arguments.length > 2) { userId = userIdOrOptions; options = maybeOptions; } else { if (typeof userIdOrOptions === 'string') { userId = userIdOrOptions; options = undefined; } else { userId = userIdOrOptions === null || userIdOrOptions === void 0 ? void 0 : userIdOrOptions.userId; options = userIdOrOptions; } } return (0, analytics_core_1.returnWrapper)(this._init(tslib_1.__assign(tslib_1.__assign({}, options), { userId: userId, apiKey: apiKey }))); }; AmplitudeBrowser.prototype._init = function (options) { var _a, _b, _c, _d, _e; return tslib_1.__awaiter(this, void 0, void 0, function () { var browserOptions, remoteConfigClient, diagnosticsClient, attributionTrackingOptions, queryParams, ampTimestamp, isWithinTimeLimit, querySessionId, connector; var _this = this; return tslib_1.__generator(this, function (_f) { switch (_f.label) { case 0: // Step 1: Block concurrent initialization if (this.initializing) { return [2 /*return*/]; } this.initializing = true; return [4 /*yield*/, (0, config_1.useBrowserConfig)(options.apiKey, options, this)]; case 1: browserOptions = _f.sent(); if (!((_a = browserOptions.remoteConfig) === null || _a === void 0 ? void 0 : _a.fetchRemoteConfig)) return [3 /*break*/, 3]; remoteConfigClient = new analytics_core_1.RemoteConfigClient(browserOptions.apiKey, browserOptions.loggerProvider, browserOptions.serverZone, /* istanbul ignore next */ (_b = browserOptions.remoteConfig) === null || _b === void 0 ? void 0 : _b.serverUrl); // Wait for initial remote config before proceeding. // Delivery mode is all, meaning it calls the callback with whoever (cache or remote) returns first. return [4 /*yield*/, new Promise(function (resolve) { // Disable coverage for this line because remote config client will always be defined in this case. // istanbul ignore next remoteConfigClient === null || remoteConfigClient === void 0 ? void 0 : remoteConfigClient.subscribe('configs.analyticsSDK.browserSDK', 'all', function (remoteConfig, source, lastFetch) { browserOptions.loggerProvider.debug('Remote configuration received:', JSON.stringify({ remoteConfig: remoteConfig, source: source, lastFetch: lastFetch, }, null, 2)); if (remoteConfig) { (0, joined_config_1.updateBrowserConfigWithRemoteConfig)(remoteConfig, browserOptions); } // Resolve the promise on first callback (initial config) resolve(); }); })]; case 2: // Wait for initial remote config before proceeding. // Delivery mode is all, meaning it calls the callback with whoever (cache or remote) returns first. _f.sent(); _f.label = 3; case 3: diagnosticsClient = new analytics_core_1.DiagnosticsClient(browserOptions.apiKey, browserOptions.loggerProvider, browserOptions.serverZone, { enabled: browserOptions.enableDiagnostics, sampleRate: browserOptions.diagnosticsSampleRate || this._diagnosticsSampleRate, }); diagnosticsClient.setTag('library', "".concat(lib_prefix_1.LIBPREFIX, "/").concat(version_1.VERSION)); return [4 /*yield*/, _super.prototype._init.call(this, browserOptions)]; case 4: _f.sent(); this.logBrowserOptions(browserOptions); this.config.diagnosticsClient = diagnosticsClient; this.config.remoteConfigClient = remoteConfigClient; if (!(0, default_tracking_1.isAttributionTrackingEnabled)(this.config.defaultTracking)) return [3 /*break*/, 6]; attributionTrackingOptions = (0, default_tracking_1.getAttributionTrackingConfig)(this.config); this.webAttribution = new web_attribution_1.WebAttribution(attributionTrackingOptions, this.config); // Fetch the current campaign, check if need to track web attribution later return [4 /*yield*/, this.webAttribution.init()]; case 5: // Fetch the current campaign, check if need to track web attribution later _f.sent(); _f.label = 6; case 6: queryParams = (0, analytics_core_1.getQueryParams)(); ampTimestamp = queryParams.ampTimestamp ? Number(queryParams.ampTimestamp) : undefined; isWithinTimeLimit = ampTimestamp ? Date.now() < ampTimestamp : true; // if an identify object is provided, call it on init if (this.config.identify) { this.identify(this.config.identify); } querySessionId = isWithinTimeLimit && !Number.isNaN(Number(queryParams.ampSessionId)) ? Number(queryParams.ampSessionId) : undefined; this.setSessionId((_e = (_d = (_c = options.sessionId) !== null && _c !== void 0 ? _c : querySessionId) !== null && _d !== void 0 ? _d : this.config.sessionId) !== null && _e !== void 0 ? _e : Date.now()); connector = (0, analytics_core_1.getAnalyticsConnector)(options.instanceName); connector.identityStore.setIdentity({ userId: this.config.userId, deviceId: this.config.deviceId, }); if (!(this.config.offline !== analytics_core_1.OfflineDisabled)) return [3 /*break*/, 8]; return [4 /*yield*/, this.add((0, network_connectivity_checker_1.networkConnectivityCheckerPlugin)()).promise]; case 7: _f.sent(); _f.label = 8; case 8: return [4 /*yield*/, this.add(new analytics_core_1.Destination({ diagnosticsClient: diagnosticsClient })).promise]; case 9: _f.sent(); return [4 /*yield*/, this.add(new context_1.Context()).promise]; case 10: _f.sent(); return [4 /*yield*/, this.add(new analytics_core_1.IdentityEventSender()).promise]; case 11: _f.sent(); // Notify if DET is enabled (0, det_notification_1.detNotify)(this.config); if (!(0, default_tracking_1.isFileDownloadTrackingEnabled)(this.config.defaultTracking)) return [3 /*break*/, 13]; this.config.loggerProvider.debug('Adding file download tracking plugin'); return [4 /*yield*/, this.add((0, file_download_tracking_1.fileDownloadTracking)()).promise]; case 12: _f.sent(); _f.label = 13; case 13: if (!(0, default_tracking_1.isFormInteractionTrackingEnabled)(this.config.defaultTracking)) return [3 /*break*/, 15]; this.config.loggerProvider.debug('Adding form interaction plugin'); return [4 /*yield*/, this.add((0, form_interaction_tracking_1.formInteractionTracking)()).promise]; case 14: _f.sent(); _f.label = 15; case 15: if (!(0, default_tracking_1.isPageViewTrackingEnabled)(this.config.defaultTracking)) return [3 /*break*/, 17]; this.config.loggerProvider.debug('Adding page view tracking plugin'); return [4 /*yield*/, this.add((0, plugin_page_view_tracking_browser_1.pageViewTrackingPlugin)((0, default_tracking_1.getPageViewTrackingConfig)(this.config))).promise]; case 16: _f.sent(); _f.label = 17; case 17: if (!(0, default_tracking_1.isElementInteractionsEnabled)(this.config.autocapture)) return [3 /*break*/, 19]; this.config.loggerProvider.debug('Adding user interactions plugin (autocapture plugin)'); return [4 /*yield*/, this.add((0, plugin_autocapture_browser_1.autocapturePlugin)((0, default_tracking_1.getElementInteractionsConfig)(this.config), { diagnosticsClient: diagnosticsClient })).promise]; case 18: _f.sent(); _f.label = 19; case 19: if (!(0, default_tracking_1.isFrustrationInteractionsEnabled)(this.config.autocapture)) return [3 /*break*/, 21]; this.config.loggerProvider.debug('Adding frustration interactions plugin'); return [4 /*yield*/, this.add((0, plugin_autocapture_browser_1.frustrationPlugin)((0, default_tracking_1.getFrustrationInteractionsConfig)(this.config))).promise]; case 20: _f.sent(); _f.label = 21; case 21: if (!(0, default_tracking_1.isNetworkTrackingEnabled)(this.config.autocapture)) return [3 /*break*/, 23]; this.config.loggerProvider.debug('Adding network tracking plugin'); return [4 /*yield*/, this.add((0, plugin_network_capture_browser_1.plugin)((0, default_tracking_1.getNetworkTrackingConfig)(this.config))).promise]; case 22: _f.sent(); _f.label = 23; case 23: if (!(0, default_tracking_1.isWebVitalsEnabled)(this.config.autocapture)) return [3 /*break*/, 25]; this.config.loggerProvider.debug('Adding web vitals plugin'); return [4 /*yield*/, this.add((0, plugin_web_vitals_browser_1.webVitalsPlugin)()).promise]; case 24: _f.sent(); _f.label = 25; case 25: if (!(0, default_tracking_1.isPageUrlEnrichmentEnabled)(this.config.autocapture)) return [3 /*break*/, 27]; this.config.loggerProvider.debug('Adding referrer page url plugin'); return [4 /*yield*/, this.add((0, plugin_page_url_enrichment_browser_1.pageUrlEnrichmentPlugin)()).promise]; case 26: _f.sent(); _f.label = 27; case 27: this.initializing = false; // Step 6: Run queued dispatch functions return [4 /*yield*/, this.runQueuedFunctions('dispatchQ')]; case 28: // Step 6: Run queued dispatch functions _f.sent(); // Step 7: Add the event receiver after running remaining queued functions. connector.eventBridge.setEventReceiver(function (event) { var _a = event.eventProperties || {}, time = _a.time, cleanEventProperties = tslib_1.__rest(_a, ["time"]); var eventOptions = typeof time === 'number' ? { time: time } : undefined; void _this.track(event.eventType, cleanEventProperties, eventOptions); }); return [2 /*return*/]; } }); }); }; AmplitudeBrowser.prototype.getUserId = function () { var _a; return (_a = this.config) === null || _a === void 0 ? void 0 : _a.userId; }; AmplitudeBrowser.prototype.setUserId = function (userId) { if (!this.config) { this.q.push(this.setUserId.bind(this, userId)); return; } this.config.loggerProvider.debug('function setUserId: ', userId); if (userId !== this.config.userId || userId === undefined) { this.config.userId = userId; // eslint-disable-next-line @typescript-eslint/no-unsafe-call this.timeline.onIdentityChanged({ userId: userId }); (0, analytics_core_1.setConnectorUserId)(userId, this.config.instanceName); } }; AmplitudeBrowser.prototype.getDeviceId = function () { var _a; return (_a = this.config) === null || _a === void 0 ? void 0 : _a.deviceId; }; AmplitudeBrowser.prototype.setDeviceId = function (deviceId) { if (!this.config) { this.q.push(this.setDeviceId.bind(this, deviceId)); return; } this.config.loggerProvider.debug('function setDeviceId: ', deviceId); if (deviceId !== this.config.deviceId) { this.config.deviceId = deviceId; // eslint-disable-next-line @typescript-eslint/no-unsafe-call this.timeline.onIdentityChanged({ deviceId: deviceId }); (0, analytics_core_1.setConnectorDeviceId)(deviceId, this.config.instanceName); } }; AmplitudeBrowser.prototype.reset = function () { this.setDeviceId((0, analytics_core_1.UUID)()); this.setUserId(undefined); }; AmplitudeBrowser.prototype.getIdentity = function () { var _a, _b; return { deviceId: (_a = this.config) === null || _a === void 0 ? void 0 : _a.deviceId, userId: (_b = this.config) === null || _b === void 0 ? void 0 : _b.userId, userProperties: this.userProperties, }; }; AmplitudeBrowser.prototype.getOptOut = function () { var _a; return (_a = this.config) === null || _a === void 0 ? void 0 : _a.optOut; }; AmplitudeBrowser.prototype.getSessionId = function () { var _a; return (_a = this.config) === null || _a === void 0 ? void 0 : _a.sessionId; }; AmplitudeBrowser.prototype.setSessionId = function (sessionId) { var _a; var promises = []; if (!this.config) { this.q.push(this.setSessionId.bind(this, sessionId)); return (0, analytics_core_1.returnWrapper)(Promise.resolve()); } // Prevents starting a new session with the same session ID if (sessionId === this.config.sessionId) { return (0, analytics_core_1.returnWrapper)(Promise.resolve()); } this.config.loggerProvider.debug('function setSessionId: ', sessionId); var previousSessionId = this.getSessionId(); if (previousSessionId !== sessionId) { // eslint-disable-next-line @typescript-eslint/no-unsafe-call this.timeline.onSessionIdChanged(sessionId); } var lastEventTime = this.config.lastEventTime; var lastEventId = (_a = this.config.lastEventId) !== null && _a !== void 0 ? _a : -1; this.config.sessionId = sessionId; this.config.lastEventTime = undefined; this.config.pageCounter = 0; if ((0, default_tracking_1.isSessionTrackingEnabled)(this.config.defaultTracking)) { if (previousSessionId && lastEventTime) { promises.push(this.track(constants_1.DEFAULT_SESSION_END_EVENT, undefined, { device_id: this.previousSessionDeviceId, event_id: ++lastEventId, session_id: previousSessionId, time: lastEventTime + 1, user_id: this.previousSessionUserId, }).promise); } this.config.lastEventTime = this.config.sessionId; } // Fire web attribution event when enable webAttribution tracking // 1. has new campaign (call setSessionId from init function) // 2. or shouldTrackNewCampaign (call setSessionId from async process(event) when there has new campaign and resetSessionOnNewCampaign = true ) var isCampaignEventTracked = this.trackCampaignEventIfNeeded(++lastEventId, promises); if ((0, default_tracking_1.isSessionTrackingEnabled)(this.config.defaultTracking)) { promises.push(this.track(constants_1.DEFAULT_SESSION_START_EVENT, undefined, { event_id: isCampaignEventTracked ? ++lastEventId : lastEventId, session_id: this.config.sessionId, time: this.config.lastEventTime, }).promise); } this.previousSessionDeviceId = this.config.deviceId; this.previousSessionUserId = this.config.userId; return (0, analytics_core_1.returnWrapper)(Promise.all(promises)); }; AmplitudeBrowser.prototype.extendSession = function () { if (!this.config) { this.q.push(this.extendSession.bind(this)); return; } this.config.lastEventTime = Date.now(); }; AmplitudeBrowser.prototype.setTransport = function (transport) { if (!this.config) { this.q.push(this.setTransport.bind(this, transport)); return; } this.config.transportProvider = (0, config_1.createTransport)(transport); }; AmplitudeBrowser.prototype.identify = function (identify, eventOptions) { if ((0, snippet_helper_1.isInstanceProxy)(identify)) { var queue = identify._q; identify._q = []; identify = (0, snippet_helper_1.convertProxyObjectToRealObject)(new analytics_core_1.Identify(), queue); } if (eventOptions === null || eventOptions === void 0 ? void 0 : eventOptions.user_id) { this.setUserId(eventOptions.user_id); } if (eventOptions === null || eventOptions === void 0 ? void 0 : eventOptions.device_id) { this.setDeviceId(eventOptions.device_id); } return _super.prototype.identify.call(this, identify, eventOptions); }; AmplitudeBrowser.prototype.groupIdentify = function (groupType, groupName, identify, eventOptions) { if ((0, snippet_helper_1.isInstanceProxy)(identify)) { var queue = identify._q; identify._q = []; identify = (0, snippet_helper_1.convertProxyObjectToRealObject)(new analytics_core_1.Identify(), queue); } return _super.prototype.groupIdentify.call(this, groupType, groupName, identify, eventOptions); }; AmplitudeBrowser.prototype.revenue = function (revenue, eventOptions) { if ((0, snippet_helper_1.isInstanceProxy)(revenue)) { var queue = revenue._q; revenue._q = []; revenue = (0, snippet_helper_1.convertProxyObjectToRealObject)(new analytics_core_1.Revenue(), queue); } return _super.prototype.revenue.call(this, revenue, eventOptions); }; AmplitudeBrowser.prototype.trackCampaignEventIfNeeded = function (lastEventId, promises) { if (!this.webAttribution || !this.webAttribution.shouldTrackNewCampaign) { return false; } var campaignEvent = this.webAttribution.generateCampaignEvent(lastEventId); if (promises) { promises.push(this.track(campaignEvent).promise); } else { this.track(campaignEvent); } this.config.loggerProvider.log('Tracking attribution.'); return true; }; AmplitudeBrowser.prototype.process = function (event) { return tslib_1.__awaiter(this, void 0, void 0, function () { var currentTime, isEventInNewSession, shouldSetSessionIdOnNewCampaign; return tslib_1.__generator(this, function (_a) { currentTime = Date.now(); isEventInNewSession = (0, analytics_core_1.isNewSession)(this.config.sessionTimeout, this.config.lastEventTime); shouldSetSessionIdOnNewCampaign = this.webAttribution && this.webAttribution.shouldSetSessionIdOnNewCampaign(); if (event.event_type !== constants_1.DEFAULT_SESSION_START_EVENT && event.event_type !== constants_1.DEFAULT_SESSION_END_EVENT && (!event.session_id || event.session_id === this.getSessionId())) { if (isEventInNewSession || shouldSetSessionIdOnNewCampaign) { this.setSessionId(currentTime); if (shouldSetSessionIdOnNewCampaign) { this.config.loggerProvider.log('Created a new session for new campaign.'); } } else if (!isEventInNewSession) { // Web attribution should be tracked during the middle of a session // if there has been a chance in the campaign information. this.trackCampaignEventIfNeeded(); } } // Set user properties if (event.event_type === analytics_core_1.SpecialEventType.IDENTIFY && event.user_properties) { this.userProperties = this.getOperationAppliedUserProperties(event.user_properties); } return [2 /*return*/, _super.prototype.process.call(this, event)]; }); }); }; AmplitudeBrowser.prototype.logBrowserOptions = function (browserConfig) { try { var browserConfigCopy = tslib_1.__assign(tslib_1.__assign({}, browserConfig), { apiKey: browserConfig.apiKey.substring(0, 10) + '********' }); this.config.loggerProvider.debug('Initialized Amplitude with BrowserConfig:', JSON.stringify(browserConfigCopy)); } catch (e) { /* istanbul ignore next */ this.config.loggerProvider.error('Error logging browser config', e); } }; /** * @experimental * WARNING: This method is for internal testing only and is not part of the public API. * It may be changed or removed at any time without notice. * * Sets the diagnostics sample rate before amplitude.init() * @param sampleRate - The sample rate to set */ AmplitudeBrowser.prototype._setDiagnosticsSampleRate = function (sampleRate) { if (sampleRate > 1 || sampleRate < 0) { return; } // Set diagnostics sample rate before initializing the config if (!this.config) { this._diagnosticsSampleRate = sampleRate; return; } }; return AmplitudeBrowser; }(analytics_core_1.AmplitudeCore)); exports.AmplitudeBrowser = AmplitudeBrowser; //# sourceMappingURL=browser-client.js.map