@amplitude/analytics-browser
Version:
Official Amplitude SDK for Web
419 lines • 23 kB
JavaScript
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