UNPKG

@amplitude/analytics-browser

Version:
419 lines 23 kB
import { __assign, __awaiter, __extends, __generator } from "tslib"; import { Config, Logger, LogLevel, MemoryStorage, UUID, CookieStorage, getCookieName, FetchTransport, getQueryParams, isDomainEqual, decodeCookieValue, } from '@amplitude/analytics-core'; import { LocalStorage } from './storage/local-storage'; import { SessionStorage } from './storage/session-storage'; import { XHRTransport } from './transports/xhr'; import { SendBeaconTransport } from './transports/send-beacon'; import { parseLegacyCookies } from './cookie-migration'; import { DEFAULT_IDENTITY_STORAGE, DEFAULT_SERVER_ZONE } from './constants'; import { VERSION } from './version'; // Exported for testing purposes only. Do not expose to public interface. var BrowserConfig = /** @class */ (function (_super) { __extends(BrowserConfig, _super); function BrowserConfig(apiKey, appVersion, cookieStorage, cookieOptions, defaultTracking, autocapture, deviceId, flushIntervalMillis, flushMaxRetries, flushQueueSize, identityStorage, ingestionMetadata, instanceName, lastEventId, lastEventTime, loggerProvider, logLevel, minIdLength, offline, optOut, partnerId, plan, serverUrl, serverZone, sessionId, sessionTimeout, storageProvider, trackingOptions, transport, useBatch, fetchRemoteConfig, userId, pageCounter, debugLogsEnabled, networkTrackingOptions, identify, enableDiagnostics, diagnosticsSampleRate, diagnosticsClient, remoteConfig) { if (cookieStorage === void 0) { cookieStorage = new MemoryStorage(); } if (cookieOptions === void 0) { cookieOptions = { domain: '', expiration: 365, sameSite: 'Lax', secure: false, upgrade: true, }; } if (flushIntervalMillis === void 0) { flushIntervalMillis = 1000; } if (flushMaxRetries === void 0) { flushMaxRetries = 5; } if (flushQueueSize === void 0) { flushQueueSize = 30; } if (identityStorage === void 0) { identityStorage = DEFAULT_IDENTITY_STORAGE; } if (loggerProvider === void 0) { loggerProvider = new Logger(); } if (logLevel === void 0) { logLevel = LogLevel.Warn; } if (offline === void 0) { offline = false; } if (optOut === void 0) { optOut = false; } if (serverUrl === void 0) { serverUrl = ''; } if (serverZone === void 0) { serverZone = DEFAULT_SERVER_ZONE; } if (sessionTimeout === void 0) { sessionTimeout = 30 * 60 * 1000; } if (storageProvider === void 0) { storageProvider = new LocalStorage({ loggerProvider: loggerProvider }); } if (trackingOptions === void 0) { trackingOptions = { ipAddress: true, language: true, platform: true, }; } if (transport === void 0) { transport = 'fetch'; } if (useBatch === void 0) { useBatch = false; } if (fetchRemoteConfig === void 0) { fetchRemoteConfig = true; } if (enableDiagnostics === void 0) { enableDiagnostics = true; } if (diagnosticsSampleRate === void 0) { diagnosticsSampleRate = 0; } var _this = this; var _a; _this = _super.call(this, { apiKey: apiKey, storageProvider: storageProvider, transportProvider: createTransport(transport) }) || this; _this.apiKey = apiKey; _this.appVersion = appVersion; _this.cookieOptions = cookieOptions; _this.defaultTracking = defaultTracking; _this.autocapture = autocapture; _this.flushIntervalMillis = flushIntervalMillis; _this.flushMaxRetries = flushMaxRetries; _this.flushQueueSize = flushQueueSize; _this.identityStorage = identityStorage; _this.ingestionMetadata = ingestionMetadata; _this.instanceName = instanceName; _this.loggerProvider = loggerProvider; _this.logLevel = logLevel; _this.minIdLength = minIdLength; _this.offline = offline; _this.partnerId = partnerId; _this.plan = plan; _this.serverUrl = serverUrl; _this.serverZone = serverZone; _this.sessionTimeout = sessionTimeout; _this.storageProvider = storageProvider; _this.trackingOptions = trackingOptions; _this.transport = transport; _this.useBatch = useBatch; _this.fetchRemoteConfig = fetchRemoteConfig; _this.networkTrackingOptions = networkTrackingOptions; _this.identify = identify; _this.enableDiagnostics = enableDiagnostics; _this.diagnosticsSampleRate = diagnosticsSampleRate; _this.diagnosticsClient = diagnosticsClient; _this.remoteConfig = remoteConfig; _this.version = VERSION; _this._optOut = false; _this._cookieStorage = cookieStorage; _this.deviceId = deviceId; _this.lastEventId = lastEventId; _this.lastEventTime = lastEventTime; _this.optOut = optOut; _this.sessionId = sessionId; _this.pageCounter = pageCounter; _this.userId = userId; _this.debugLogsEnabled = debugLogsEnabled; _this.loggerProvider.enable(debugLogsEnabled ? LogLevel.Debug : _this.logLevel); _this.networkTrackingOptions = networkTrackingOptions; _this.identify = identify; _this.enableDiagnostics = enableDiagnostics; _this.diagnosticsSampleRate = diagnosticsSampleRate; _this.diagnosticsClient = diagnosticsClient; // Note: The canonical logic for determining fetchRemoteConfig is in shouldFetchRemoteConfig(). // This logic is duplicated here to maintain the BrowserConfig constructor contract and ensure // the config object has the correct fetchRemoteConfig value set on its properties. // The value passed to this constructor should already be computed via shouldFetchRemoteConfig(). var _fetchRemoteConfig = (_a = remoteConfig === null || remoteConfig === void 0 ? void 0 : remoteConfig.fetchRemoteConfig) !== null && _a !== void 0 ? _a : fetchRemoteConfig; _this.remoteConfig = _this.remoteConfig || {}; _this.remoteConfig.fetchRemoteConfig = _fetchRemoteConfig; _this.fetchRemoteConfig = _fetchRemoteConfig; return _this; } Object.defineProperty(BrowserConfig.prototype, "cookieStorage", { get: function () { return this._cookieStorage; }, set: function (cookieStorage) { if (this._cookieStorage !== cookieStorage) { this._cookieStorage = cookieStorage; this.updateStorage(); } }, enumerable: false, configurable: true }); Object.defineProperty(BrowserConfig.prototype, "deviceId", { get: function () { return this._deviceId; }, set: function (deviceId) { if (this._deviceId !== deviceId) { this._deviceId = deviceId; this.updateStorage(); } }, enumerable: false, configurable: true }); Object.defineProperty(BrowserConfig.prototype, "userId", { get: function () { return this._userId; }, set: function (userId) { if (this._userId !== userId) { this._userId = userId; this.updateStorage(); } }, enumerable: false, configurable: true }); Object.defineProperty(BrowserConfig.prototype, "sessionId", { get: function () { return this._sessionId; }, set: function (sessionId) { if (this._sessionId !== sessionId) { this._sessionId = sessionId; this.updateStorage(); } }, enumerable: false, configurable: true }); Object.defineProperty(BrowserConfig.prototype, "optOut", { get: function () { return this._optOut; }, set: function (optOut) { if (this._optOut !== optOut) { this._optOut = optOut; this.updateStorage(); } }, enumerable: false, configurable: true }); Object.defineProperty(BrowserConfig.prototype, "lastEventTime", { get: function () { return this._lastEventTime; }, set: function (lastEventTime) { if (this._lastEventTime !== lastEventTime) { this._lastEventTime = lastEventTime; this.updateStorage(); } }, enumerable: false, configurable: true }); Object.defineProperty(BrowserConfig.prototype, "lastEventId", { get: function () { return this._lastEventId; }, set: function (lastEventId) { if (this._lastEventId !== lastEventId) { this._lastEventId = lastEventId; this.updateStorage(); } }, enumerable: false, configurable: true }); Object.defineProperty(BrowserConfig.prototype, "pageCounter", { get: function () { return this._pageCounter; }, set: function (pageCounter) { if (this._pageCounter !== pageCounter) { this._pageCounter = pageCounter; this.updateStorage(); } }, enumerable: false, configurable: true }); Object.defineProperty(BrowserConfig.prototype, "debugLogsEnabled", { set: function (debugLogsEnabled) { if (this._debugLogsEnabled !== debugLogsEnabled) { this._debugLogsEnabled = debugLogsEnabled; this.updateStorage(); } }, enumerable: false, configurable: true }); BrowserConfig.prototype.updateStorage = function () { var cache = { deviceId: this._deviceId, userId: this._userId, sessionId: this._sessionId, optOut: this._optOut, lastEventTime: this._lastEventTime, lastEventId: this._lastEventId, pageCounter: this._pageCounter, debugLogsEnabled: this._debugLogsEnabled, cookieDomain: undefined, }; if (this.cookieStorage instanceof CookieStorage) { cache.cookieDomain = this.cookieStorage.options.domain; } void this.cookieStorage.set(getCookieName(this.apiKey), cache); }; return BrowserConfig; }(Config)); export { BrowserConfig }; export var useBrowserConfig = function (apiKey, options, amplitudeInstance, diagnosticsClient, earlyConfig) { if (options === void 0) { options = {}; } return __awaiter(void 0, void 0, void 0, function () { var identityStorage, cookieOptions, _a, _b, cookieConfig, cookieStorage, legacyCookies, previousCookies, queryParams, ampTimestamp, isWithinTimeLimit, deviceId, lastEventId, lastEventTime, optOut, sessionId, userId, trackingOptions, pageCounter, debugLogsEnabled, browserConfig; var _c; var _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6; return __generator(this, function (_7) { switch (_7.label) { case 0: identityStorage = options.identityStorage || DEFAULT_IDENTITY_STORAGE; _c = {}; if (!(identityStorage !== DEFAULT_IDENTITY_STORAGE)) return [3 /*break*/, 1]; _a = ''; return [3 /*break*/, 5]; case 1: if (!((_e = (_d = options.cookieOptions) === null || _d === void 0 ? void 0 : _d.domain) !== null && _e !== void 0)) return [3 /*break*/, 2]; _b = _e; return [3 /*break*/, 4]; case 2: return [4 /*yield*/, getTopLevelDomain()]; case 3: _b = (_7.sent()); _7.label = 4; case 4: _a = _b; _7.label = 5; case 5: cookieOptions = __assign.apply(void 0, [(_c.domain = _a, _c.expiration = 365, _c.sameSite = 'Lax', _c.secure = false, _c.upgrade = true, _c), options.cookieOptions]); cookieConfig = { // if more than one cookie with the same key exists, // look for the cookie that has the domain attribute set to cookieOptions.domain duplicateResolverFn: function (value) { var decodedValue = decodeCookieValue(value); if (!decodedValue) { return false; } var parsed = JSON.parse(decodedValue); return isDomainEqual(parsed.cookieDomain, cookieOptions.domain); }, diagnosticsClient: diagnosticsClient, }; cookieStorage = createCookieStorage(options.identityStorage, cookieOptions, cookieConfig); return [4 /*yield*/, parseLegacyCookies(apiKey, cookieStorage, (_g = (_f = options.cookieOptions) === null || _f === void 0 ? void 0 : _f.upgrade) !== null && _g !== void 0 ? _g : true)]; case 6: legacyCookies = _7.sent(); return [4 /*yield*/, cookieStorage.get(getCookieName(apiKey))]; case 7: previousCookies = _7.sent(); queryParams = getQueryParams(); ampTimestamp = queryParams.ampTimestamp ? Number(queryParams.ampTimestamp) : undefined; isWithinTimeLimit = ampTimestamp ? Date.now() < ampTimestamp : true; deviceId = (_m = (_l = (_k = (_h = options.deviceId) !== null && _h !== void 0 ? _h : (isWithinTimeLimit ? (_j = queryParams.ampDeviceId) !== null && _j !== void 0 ? _j : queryParams.deviceId : undefined)) !== null && _k !== void 0 ? _k : previousCookies === null || previousCookies === void 0 ? void 0 : previousCookies.deviceId) !== null && _l !== void 0 ? _l : legacyCookies.deviceId) !== null && _m !== void 0 ? _m : UUID(); lastEventId = (_o = previousCookies === null || previousCookies === void 0 ? void 0 : previousCookies.lastEventId) !== null && _o !== void 0 ? _o : legacyCookies.lastEventId; lastEventTime = (_p = previousCookies === null || previousCookies === void 0 ? void 0 : previousCookies.lastEventTime) !== null && _p !== void 0 ? _p : legacyCookies.lastEventTime; optOut = (_r = (_q = options.optOut) !== null && _q !== void 0 ? _q : previousCookies === null || previousCookies === void 0 ? void 0 : previousCookies.optOut) !== null && _r !== void 0 ? _r : legacyCookies.optOut; sessionId = (_s = previousCookies === null || previousCookies === void 0 ? void 0 : previousCookies.sessionId) !== null && _s !== void 0 ? _s : legacyCookies.sessionId; userId = (_u = (_t = options.userId) !== null && _t !== void 0 ? _t : previousCookies === null || previousCookies === void 0 ? void 0 : previousCookies.userId) !== null && _u !== void 0 ? _u : legacyCookies.userId; amplitudeInstance.previousSessionDeviceId = (_v = previousCookies === null || previousCookies === void 0 ? void 0 : previousCookies.deviceId) !== null && _v !== void 0 ? _v : legacyCookies.deviceId; amplitudeInstance.previousSessionUserId = (_w = previousCookies === null || previousCookies === void 0 ? void 0 : previousCookies.userId) !== null && _w !== void 0 ? _w : legacyCookies.userId; trackingOptions = { ipAddress: (_y = (_x = options.trackingOptions) === null || _x === void 0 ? void 0 : _x.ipAddress) !== null && _y !== void 0 ? _y : true, language: (_0 = (_z = options.trackingOptions) === null || _z === void 0 ? void 0 : _z.language) !== null && _0 !== void 0 ? _0 : true, platform: (_2 = (_1 = options.trackingOptions) === null || _1 === void 0 ? void 0 : _1.platform) !== null && _2 !== void 0 ? _2 : true, }; pageCounter = previousCookies === null || previousCookies === void 0 ? void 0 : previousCookies.pageCounter; debugLogsEnabled = previousCookies === null || previousCookies === void 0 ? void 0 : previousCookies.debugLogsEnabled; // Override default tracking options if autocapture is set if (options.autocapture !== undefined) { options.defaultTracking = options.autocapture; } browserConfig = new BrowserConfig(apiKey, options.appVersion, cookieStorage, cookieOptions, options.defaultTracking, options.autocapture, deviceId, options.flushIntervalMillis, options.flushMaxRetries, options.flushQueueSize, identityStorage, options.ingestionMetadata, options.instanceName, lastEventId, lastEventTime, // Use earlyConfig.loggerProvider to ensure consistent logger across DiagnosticsClient/RemoteConfigClient/BrowserConfig (_3 = earlyConfig === null || earlyConfig === void 0 ? void 0 : earlyConfig.loggerProvider) !== null && _3 !== void 0 ? _3 : options.loggerProvider, options.logLevel, options.minIdLength, options.offline, optOut, options.partnerId, options.plan, options.serverUrl, // Use earlyConfig.serverZone to ensure consistent serverZone (_4 = earlyConfig === null || earlyConfig === void 0 ? void 0 : earlyConfig.serverZone) !== null && _4 !== void 0 ? _4 : options.serverZone, sessionId, options.sessionTimeout, options.storageProvider, trackingOptions, options.transport, options.useBatch, options.fetchRemoteConfig, userId, pageCounter, debugLogsEnabled, options.networkTrackingOptions, options.identify, // Use earlyConfig values (already has remote config applied), otherwise fall back to options (_5 = earlyConfig === null || earlyConfig === void 0 ? void 0 : earlyConfig.enableDiagnostics) !== null && _5 !== void 0 ? _5 : options.enableDiagnostics, (_6 = earlyConfig === null || earlyConfig === void 0 ? void 0 : earlyConfig.diagnosticsSampleRate) !== null && _6 !== void 0 ? _6 : amplitudeInstance._diagnosticsSampleRate, diagnosticsClient, options.remoteConfig); return [4 /*yield*/, browserConfig.storageProvider.isEnabled()]; case 8: if (!(_7.sent())) { browserConfig.loggerProvider.warn("Storage provider ".concat(browserConfig.storageProvider.constructor.name, " is not enabled. Falling back to MemoryStorage.")); browserConfig.storageProvider = new MemoryStorage(); } return [2 /*return*/, browserConfig]; } }); }); }; export var createCookieStorage = function (identityStorage, cookieOptions, cookieConfig) { if (identityStorage === void 0) { identityStorage = DEFAULT_IDENTITY_STORAGE; } if (cookieOptions === void 0) { cookieOptions = {}; } switch (identityStorage) { case 'localStorage': return new LocalStorage(); case 'sessionStorage': return new SessionStorage(); case 'none': return new MemoryStorage(); case 'cookie': default: return new CookieStorage(__assign(__assign({}, cookieOptions), { expirationDays: cookieOptions.expiration }), cookieConfig); } }; /** * Determines whether to fetch remote config based on options. * Extracted to allow early determination before useBrowserConfig is called. */ export var shouldFetchRemoteConfig = function (options) { var _a, _b; if (options === void 0) { options = {}; } if (((_a = options.remoteConfig) === null || _a === void 0 ? void 0 : _a.fetchRemoteConfig) === true) { // set to true if remoteConfig explicitly set to true return true; } else if (((_b = options.remoteConfig) === null || _b === void 0 ? void 0 : _b.fetchRemoteConfig) === false || options.fetchRemoteConfig === false) { // set to false if either are set to false explicitly return false; } else { // default to true if both undefined return true; } }; export var createTransport = function (transport) { var type = typeof transport === 'object' ? transport.type : transport; var headers = typeof transport === 'object' ? transport.headers : undefined; if (type === 'xhr') { return new XHRTransport(headers); } if (type === 'beacon') { // SendBeacon does not support custom headers return new SendBeaconTransport(); } return new FetchTransport(headers); }; export var getTopLevelDomain = function (url) { return __awaiter(void 0, void 0, void 0, function () { var host, parts, levels, cookieKeyUniqueId, storageKey, i, i, domain, options, storage, value; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new CookieStorage().isEnabled()]; case 1: if (!(_a.sent()) || (!url && (typeof location === 'undefined' || !location.hostname))) { return [2 /*return*/, '']; } host = url !== null && url !== void 0 ? url : location.hostname; parts = host.split('.'); levels = []; cookieKeyUniqueId = UUID(); storageKey = "AMP_TLDTEST_".concat(cookieKeyUniqueId.substring(0, 8)); for (i = parts.length - 2; i >= 0; --i) { levels.push(parts.slice(i).join('.')); } i = 0; _a.label = 2; case 2: if (!(i < levels.length)) return [3 /*break*/, 7]; domain = levels[i]; options = { domain: '.' + domain, expirationDays: 0.003, // expire in ~5 minutes }; storage = new CookieStorage(options); return [4 /*yield*/, storage.set(storageKey, 1)]; case 3: _a.sent(); return [4 /*yield*/, storage.get(storageKey)]; case 4: value = _a.sent(); if (!value) return [3 /*break*/, 6]; return [4 /*yield*/, storage.remove(storageKey)]; case 5: _a.sent(); return [2 /*return*/, '.' + domain]; case 6: i++; return [3 /*break*/, 2]; case 7: return [2 /*return*/, '']; } }); }); }; //# sourceMappingURL=config.js.map