UNPKG

67.4 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var app = require('@firebase/app');
6var tslib = require('tslib');
7var logger$1 = require('@firebase/logger');
8var util = require('@firebase/util');
9var component = require('@firebase/component');
10require('@firebase/installations');
11
12/**
13 * @license
14 * Copyright 2019 Google LLC
15 *
16 * Licensed under the Apache License, Version 2.0 (the "License");
17 * you may not use this file except in compliance with the License.
18 * You may obtain a copy of the License at
19 *
20 * http://www.apache.org/licenses/LICENSE-2.0
21 *
22 * Unless required by applicable law or agreed to in writing, software
23 * distributed under the License is distributed on an "AS IS" BASIS,
24 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25 * See the License for the specific language governing permissions and
26 * limitations under the License.
27 */
28/**
29 * Type constant for Firebase Analytics.
30 */
31var ANALYTICS_TYPE = 'analytics';
32// Key to attach FID to in gtag params.
33var GA_FID_KEY = 'firebase_id';
34var ORIGIN_KEY = 'origin';
35var FETCH_TIMEOUT_MILLIS = 60 * 1000;
36var DYNAMIC_CONFIG_URL = 'https://firebase.googleapis.com/v1alpha/projects/-/apps/{app-id}/webConfig';
37var GTAG_URL = 'https://www.googletagmanager.com/gtag/js';
38
39/**
40 * @license
41 * Copyright 2019 Google LLC
42 *
43 * Licensed under the Apache License, Version 2.0 (the "License");
44 * you may not use this file except in compliance with the License.
45 * You may obtain a copy of the License at
46 *
47 * http://www.apache.org/licenses/LICENSE-2.0
48 *
49 * Unless required by applicable law or agreed to in writing, software
50 * distributed under the License is distributed on an "AS IS" BASIS,
51 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
52 * See the License for the specific language governing permissions and
53 * limitations under the License.
54 */
55var logger = new logger$1.Logger('@firebase/analytics');
56
57/**
58 * @license
59 * Copyright 2019 Google LLC
60 *
61 * Licensed under the Apache License, Version 2.0 (the "License");
62 * you may not use this file except in compliance with the License.
63 * You may obtain a copy of the License at
64 *
65 * http://www.apache.org/licenses/LICENSE-2.0
66 *
67 * Unless required by applicable law or agreed to in writing, software
68 * distributed under the License is distributed on an "AS IS" BASIS,
69 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
70 * See the License for the specific language governing permissions and
71 * limitations under the License.
72 */
73var _a;
74var ERRORS = (_a = {},
75 _a["already-exists" /* AnalyticsError.ALREADY_EXISTS */] = 'A Firebase Analytics instance with the appId {$id} ' +
76 ' already exists. ' +
77 'Only one Firebase Analytics instance can be created for each appId.',
78 _a["already-initialized" /* AnalyticsError.ALREADY_INITIALIZED */] = 'initializeAnalytics() cannot be called again with different options than those ' +
79 'it was initially called with. It can be called again with the same options to ' +
80 'return the existing instance, or getAnalytics() can be used ' +
81 'to get a reference to the already-intialized instance.',
82 _a["already-initialized-settings" /* AnalyticsError.ALREADY_INITIALIZED_SETTINGS */] = 'Firebase Analytics has already been initialized.' +
83 'settings() must be called before initializing any Analytics instance' +
84 'or it will have no effect.',
85 _a["interop-component-reg-failed" /* AnalyticsError.INTEROP_COMPONENT_REG_FAILED */] = 'Firebase Analytics Interop Component failed to instantiate: {$reason}',
86 _a["invalid-analytics-context" /* AnalyticsError.INVALID_ANALYTICS_CONTEXT */] = 'Firebase Analytics is not supported in this environment. ' +
87 'Wrap initialization of analytics in analytics.isSupported() ' +
88 'to prevent initialization in unsupported environments. Details: {$errorInfo}',
89 _a["indexeddb-unavailable" /* AnalyticsError.INDEXEDDB_UNAVAILABLE */] = 'IndexedDB unavailable or restricted in this environment. ' +
90 'Wrap initialization of analytics in analytics.isSupported() ' +
91 'to prevent initialization in unsupported environments. Details: {$errorInfo}',
92 _a["fetch-throttle" /* AnalyticsError.FETCH_THROTTLE */] = 'The config fetch request timed out while in an exponential backoff state.' +
93 ' Unix timestamp in milliseconds when fetch request throttling ends: {$throttleEndTimeMillis}.',
94 _a["config-fetch-failed" /* AnalyticsError.CONFIG_FETCH_FAILED */] = 'Dynamic config fetch failed: [{$httpStatus}] {$responseMessage}',
95 _a["no-api-key" /* AnalyticsError.NO_API_KEY */] = 'The "apiKey" field is empty in the local Firebase config. Firebase Analytics requires this field to' +
96 'contain a valid API key.',
97 _a["no-app-id" /* AnalyticsError.NO_APP_ID */] = 'The "appId" field is empty in the local Firebase config. Firebase Analytics requires this field to' +
98 'contain a valid app ID.',
99 _a["no-client-id" /* AnalyticsError.NO_CLIENT_ID */] = 'The "client_id" field is empty.',
100 _a["invalid-gtag-resource" /* AnalyticsError.INVALID_GTAG_RESOURCE */] = 'Trusted Types detected an invalid gtag resource: {$gtagURL}.',
101 _a);
102var ERROR_FACTORY = new util.ErrorFactory('analytics', 'Analytics', ERRORS);
103
104/**
105 * @license
106 * Copyright 2019 Google LLC
107 *
108 * Licensed under the Apache License, Version 2.0 (the "License");
109 * you may not use this file except in compliance with the License.
110 * You may obtain a copy of the License at
111 *
112 * http://www.apache.org/licenses/LICENSE-2.0
113 *
114 * Unless required by applicable law or agreed to in writing, software
115 * distributed under the License is distributed on an "AS IS" BASIS,
116 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
117 * See the License for the specific language governing permissions and
118 * limitations under the License.
119 */
120/**
121 * Verifies and creates a TrustedScriptURL.
122 */
123function createGtagTrustedTypesScriptURL(url) {
124 if (!url.startsWith(GTAG_URL)) {
125 var err = ERROR_FACTORY.create("invalid-gtag-resource" /* AnalyticsError.INVALID_GTAG_RESOURCE */, {
126 gtagURL: url
127 });
128 logger.warn(err.message);
129 return '';
130 }
131 return url;
132}
133/**
134 * Makeshift polyfill for Promise.allSettled(). Resolves when all promises
135 * have either resolved or rejected.
136 *
137 * @param promises Array of promises to wait for.
138 */
139function promiseAllSettled(promises) {
140 return Promise.all(promises.map(function (promise) { return promise.catch(function (e) { return e; }); }));
141}
142/**
143 * Creates a TrustedTypePolicy object that implements the rules passed as policyOptions.
144 *
145 * @param policyName A string containing the name of the policy
146 * @param policyOptions Object containing implementations of instance methods for TrustedTypesPolicy, see {@link https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicy#instance_methods
147 * | the TrustedTypePolicy reference documentation}.
148 */
149function createTrustedTypesPolicy(policyName, policyOptions) {
150 // Create a TrustedTypes policy that we can use for updating src
151 // properties
152 var trustedTypesPolicy;
153 if (window.trustedTypes) {
154 trustedTypesPolicy = window.trustedTypes.createPolicy(policyName, policyOptions);
155 }
156 return trustedTypesPolicy;
157}
158/**
159 * Inserts gtag script tag into the page to asynchronously download gtag.
160 * @param dataLayerName Name of datalayer (most often the default, "_dataLayer").
161 */
162function insertScriptTag(dataLayerName, measurementId) {
163 var trustedTypesPolicy = createTrustedTypesPolicy('firebase-js-sdk-policy', {
164 createScriptURL: createGtagTrustedTypesScriptURL
165 });
166 var script = document.createElement('script');
167 // We are not providing an analyticsId in the URL because it would trigger a `page_view`
168 // without fid. We will initialize ga-id using gtag (config) command together with fid.
169 var gtagScriptURL = "".concat(GTAG_URL, "?l=").concat(dataLayerName, "&id=").concat(measurementId);
170 script.src = trustedTypesPolicy
171 ? trustedTypesPolicy === null || trustedTypesPolicy === void 0 ? void 0 : trustedTypesPolicy.createScriptURL(gtagScriptURL)
172 : gtagScriptURL;
173 script.async = true;
174 document.head.appendChild(script);
175}
176/**
177 * Get reference to, or create, global datalayer.
178 * @param dataLayerName Name of datalayer (most often the default, "_dataLayer").
179 */
180function getOrCreateDataLayer(dataLayerName) {
181 // Check for existing dataLayer and create if needed.
182 var dataLayer = [];
183 if (Array.isArray(window[dataLayerName])) {
184 dataLayer = window[dataLayerName];
185 }
186 else {
187 window[dataLayerName] = dataLayer;
188 }
189 return dataLayer;
190}
191/**
192 * Wrapped gtag logic when gtag is called with 'config' command.
193 *
194 * @param gtagCore Basic gtag function that just appends to dataLayer.
195 * @param initializationPromisesMap Map of appIds to their initialization promises.
196 * @param dynamicConfigPromisesList Array of dynamic config fetch promises.
197 * @param measurementIdToAppId Map of GA measurementIDs to corresponding Firebase appId.
198 * @param measurementId GA Measurement ID to set config for.
199 * @param gtagParams Gtag config params to set.
200 */
201function gtagOnConfig(gtagCore, initializationPromisesMap, dynamicConfigPromisesList, measurementIdToAppId, measurementId, gtagParams) {
202 return tslib.__awaiter(this, void 0, void 0, function () {
203 var correspondingAppId, dynamicConfigResults, foundConfig, e_1;
204 return tslib.__generator(this, function (_a) {
205 switch (_a.label) {
206 case 0:
207 correspondingAppId = measurementIdToAppId[measurementId];
208 _a.label = 1;
209 case 1:
210 _a.trys.push([1, 7, , 8]);
211 if (!correspondingAppId) return [3 /*break*/, 3];
212 return [4 /*yield*/, initializationPromisesMap[correspondingAppId]];
213 case 2:
214 _a.sent();
215 return [3 /*break*/, 6];
216 case 3: return [4 /*yield*/, promiseAllSettled(dynamicConfigPromisesList)];
217 case 4:
218 dynamicConfigResults = _a.sent();
219 foundConfig = dynamicConfigResults.find(function (config) { return config.measurementId === measurementId; });
220 if (!foundConfig) return [3 /*break*/, 6];
221 return [4 /*yield*/, initializationPromisesMap[foundConfig.appId]];
222 case 5:
223 _a.sent();
224 _a.label = 6;
225 case 6: return [3 /*break*/, 8];
226 case 7:
227 e_1 = _a.sent();
228 logger.error(e_1);
229 return [3 /*break*/, 8];
230 case 8:
231 gtagCore("config" /* GtagCommand.CONFIG */, measurementId, gtagParams);
232 return [2 /*return*/];
233 }
234 });
235 });
236}
237/**
238 * Wrapped gtag logic when gtag is called with 'event' command.
239 *
240 * @param gtagCore Basic gtag function that just appends to dataLayer.
241 * @param initializationPromisesMap Map of appIds to their initialization promises.
242 * @param dynamicConfigPromisesList Array of dynamic config fetch promises.
243 * @param measurementId GA Measurement ID to log event to.
244 * @param gtagParams Params to log with this event.
245 */
246function gtagOnEvent(gtagCore, initializationPromisesMap, dynamicConfigPromisesList, measurementId, gtagParams) {
247 return tslib.__awaiter(this, void 0, void 0, function () {
248 var initializationPromisesToWaitFor, gaSendToList, dynamicConfigResults, _loop_1, _i, gaSendToList_1, sendToId, state_1, e_2;
249 return tslib.__generator(this, function (_a) {
250 switch (_a.label) {
251 case 0:
252 _a.trys.push([0, 4, , 5]);
253 initializationPromisesToWaitFor = [];
254 if (!(gtagParams && gtagParams['send_to'])) return [3 /*break*/, 2];
255 gaSendToList = gtagParams['send_to'];
256 // Make it an array if is isn't, so it can be dealt with the same way.
257 if (!Array.isArray(gaSendToList)) {
258 gaSendToList = [gaSendToList];
259 }
260 return [4 /*yield*/, promiseAllSettled(dynamicConfigPromisesList)];
261 case 1:
262 dynamicConfigResults = _a.sent();
263 _loop_1 = function (sendToId) {
264 // Any fetched dynamic measurement ID that matches this 'send_to' ID
265 var foundConfig = dynamicConfigResults.find(function (config) { return config.measurementId === sendToId; });
266 var initializationPromise = foundConfig && initializationPromisesMap[foundConfig.appId];
267 if (initializationPromise) {
268 initializationPromisesToWaitFor.push(initializationPromise);
269 }
270 else {
271 // Found an item in 'send_to' that is not associated
272 // directly with an FID, possibly a group. Empty this array,
273 // exit the loop early, and let it get populated below.
274 initializationPromisesToWaitFor = [];
275 return "break";
276 }
277 };
278 for (_i = 0, gaSendToList_1 = gaSendToList; _i < gaSendToList_1.length; _i++) {
279 sendToId = gaSendToList_1[_i];
280 state_1 = _loop_1(sendToId);
281 if (state_1 === "break")
282 break;
283 }
284 _a.label = 2;
285 case 2:
286 // This will be unpopulated if there was no 'send_to' field , or
287 // if not all entries in the 'send_to' field could be mapped to
288 // a FID. In these cases, wait on all pending initialization promises.
289 if (initializationPromisesToWaitFor.length === 0) {
290 initializationPromisesToWaitFor = Object.values(initializationPromisesMap);
291 }
292 // Run core gtag function with args after all relevant initialization
293 // promises have been resolved.
294 return [4 /*yield*/, Promise.all(initializationPromisesToWaitFor)];
295 case 3:
296 // Run core gtag function with args after all relevant initialization
297 // promises have been resolved.
298 _a.sent();
299 // Workaround for http://b/141370449 - third argument cannot be undefined.
300 gtagCore("event" /* GtagCommand.EVENT */, measurementId, gtagParams || {});
301 return [3 /*break*/, 5];
302 case 4:
303 e_2 = _a.sent();
304 logger.error(e_2);
305 return [3 /*break*/, 5];
306 case 5: return [2 /*return*/];
307 }
308 });
309 });
310}
311/**
312 * Wraps a standard gtag function with extra code to wait for completion of
313 * relevant initialization promises before sending requests.
314 *
315 * @param gtagCore Basic gtag function that just appends to dataLayer.
316 * @param initializationPromisesMap Map of appIds to their initialization promises.
317 * @param dynamicConfigPromisesList Array of dynamic config fetch promises.
318 * @param measurementIdToAppId Map of GA measurementIDs to corresponding Firebase appId.
319 */
320function wrapGtag(gtagCore,
321/**
322 * Allows wrapped gtag calls to wait on whichever intialization promises are required,
323 * depending on the contents of the gtag params' `send_to` field, if any.
324 */
325initializationPromisesMap,
326/**
327 * Wrapped gtag calls sometimes require all dynamic config fetches to have returned
328 * before determining what initialization promises (which include FIDs) to wait for.
329 */
330dynamicConfigPromisesList,
331/**
332 * Wrapped gtag config calls can narrow down which initialization promise (with FID)
333 * to wait for if the measurementId is already fetched, by getting the corresponding appId,
334 * which is the key for the initialization promises map.
335 */
336measurementIdToAppId) {
337 /**
338 * Wrapper around gtag that ensures FID is sent with gtag calls.
339 * @param command Gtag command type.
340 * @param idOrNameOrParams Measurement ID if command is EVENT/CONFIG, params if command is SET.
341 * @param gtagParams Params if event is EVENT/CONFIG.
342 */
343 function gtagWrapper(command) {
344 var args = [];
345 for (var _i = 1; _i < arguments.length; _i++) {
346 args[_i - 1] = arguments[_i];
347 }
348 return tslib.__awaiter(this, void 0, void 0, function () {
349 var measurementId, gtagParams, measurementId, gtagParams, gtagParams, measurementId, fieldName, callback, customParams, e_3;
350 return tslib.__generator(this, function (_a) {
351 switch (_a.label) {
352 case 0:
353 _a.trys.push([0, 6, , 7]);
354 if (!(command === "event" /* GtagCommand.EVENT */)) return [3 /*break*/, 2];
355 measurementId = args[0], gtagParams = args[1];
356 // If EVENT, second arg must be measurementId.
357 return [4 /*yield*/, gtagOnEvent(gtagCore, initializationPromisesMap, dynamicConfigPromisesList, measurementId, gtagParams)];
358 case 1:
359 // If EVENT, second arg must be measurementId.
360 _a.sent();
361 return [3 /*break*/, 5];
362 case 2:
363 if (!(command === "config" /* GtagCommand.CONFIG */)) return [3 /*break*/, 4];
364 measurementId = args[0], gtagParams = args[1];
365 // If CONFIG, second arg must be measurementId.
366 return [4 /*yield*/, gtagOnConfig(gtagCore, initializationPromisesMap, dynamicConfigPromisesList, measurementIdToAppId, measurementId, gtagParams)];
367 case 3:
368 // If CONFIG, second arg must be measurementId.
369 _a.sent();
370 return [3 /*break*/, 5];
371 case 4:
372 if (command === "consent" /* GtagCommand.CONSENT */) {
373 gtagParams = args[0];
374 gtagCore("consent" /* GtagCommand.CONSENT */, 'update', gtagParams);
375 }
376 else if (command === "get" /* GtagCommand.GET */) {
377 measurementId = args[0], fieldName = args[1], callback = args[2];
378 gtagCore("get" /* GtagCommand.GET */, measurementId, fieldName, callback);
379 }
380 else if (command === "set" /* GtagCommand.SET */) {
381 customParams = args[0];
382 // If SET, second arg must be params.
383 gtagCore("set" /* GtagCommand.SET */, customParams);
384 }
385 else {
386 gtagCore.apply(void 0, tslib.__spreadArray([command], args, false));
387 }
388 _a.label = 5;
389 case 5: return [3 /*break*/, 7];
390 case 6:
391 e_3 = _a.sent();
392 logger.error(e_3);
393 return [3 /*break*/, 7];
394 case 7: return [2 /*return*/];
395 }
396 });
397 });
398 }
399 return gtagWrapper;
400}
401/**
402 * Creates global gtag function or wraps existing one if found.
403 * This wrapped function attaches Firebase instance ID (FID) to gtag 'config' and
404 * 'event' calls that belong to the GAID associated with this Firebase instance.
405 *
406 * @param initializationPromisesMap Map of appIds to their initialization promises.
407 * @param dynamicConfigPromisesList Array of dynamic config fetch promises.
408 * @param measurementIdToAppId Map of GA measurementIDs to corresponding Firebase appId.
409 * @param dataLayerName Name of global GA datalayer array.
410 * @param gtagFunctionName Name of global gtag function ("gtag" if not user-specified).
411 */
412function wrapOrCreateGtag(initializationPromisesMap, dynamicConfigPromisesList, measurementIdToAppId, dataLayerName, gtagFunctionName) {
413 // Create a basic core gtag function
414 var gtagCore = function () {
415 // Must push IArguments object, not an array.
416 window[dataLayerName].push(arguments);
417 };
418 // Replace it with existing one if found
419 if (window[gtagFunctionName] &&
420 typeof window[gtagFunctionName] === 'function') {
421 // @ts-ignore
422 gtagCore = window[gtagFunctionName];
423 }
424 window[gtagFunctionName] = wrapGtag(gtagCore, initializationPromisesMap, dynamicConfigPromisesList, measurementIdToAppId);
425 return {
426 gtagCore: gtagCore,
427 wrappedGtag: window[gtagFunctionName]
428 };
429}
430/**
431 * Returns the script tag in the DOM matching both the gtag url pattern
432 * and the provided data layer name.
433 */
434function findGtagScriptOnPage(dataLayerName) {
435 var scriptTags = window.document.getElementsByTagName('script');
436 for (var _i = 0, _a = Object.values(scriptTags); _i < _a.length; _i++) {
437 var tag = _a[_i];
438 if (tag.src &&
439 tag.src.includes(GTAG_URL) &&
440 tag.src.includes(dataLayerName)) {
441 return tag;
442 }
443 }
444 return null;
445}
446
447/**
448 * @license
449 * Copyright 2020 Google LLC
450 *
451 * Licensed under the Apache License, Version 2.0 (the "License");
452 * you may not use this file except in compliance with the License.
453 * You may obtain a copy of the License at
454 *
455 * http://www.apache.org/licenses/LICENSE-2.0
456 *
457 * Unless required by applicable law or agreed to in writing, software
458 * distributed under the License is distributed on an "AS IS" BASIS,
459 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
460 * See the License for the specific language governing permissions and
461 * limitations under the License.
462 */
463/**
464 * Backoff factor for 503 errors, which we want to be conservative about
465 * to avoid overloading servers. Each retry interval will be
466 * BASE_INTERVAL_MILLIS * LONG_RETRY_FACTOR ^ retryCount, so the second one
467 * will be ~30 seconds (with fuzzing).
468 */
469var LONG_RETRY_FACTOR = 30;
470/**
471 * Base wait interval to multiplied by backoffFactor^backoffCount.
472 */
473var BASE_INTERVAL_MILLIS = 1000;
474/**
475 * Stubbable retry data storage class.
476 */
477var RetryData = /** @class */ (function () {
478 function RetryData(throttleMetadata, intervalMillis) {
479 if (throttleMetadata === void 0) { throttleMetadata = {}; }
480 if (intervalMillis === void 0) { intervalMillis = BASE_INTERVAL_MILLIS; }
481 this.throttleMetadata = throttleMetadata;
482 this.intervalMillis = intervalMillis;
483 }
484 RetryData.prototype.getThrottleMetadata = function (appId) {
485 return this.throttleMetadata[appId];
486 };
487 RetryData.prototype.setThrottleMetadata = function (appId, metadata) {
488 this.throttleMetadata[appId] = metadata;
489 };
490 RetryData.prototype.deleteThrottleMetadata = function (appId) {
491 delete this.throttleMetadata[appId];
492 };
493 return RetryData;
494}());
495var defaultRetryData = new RetryData();
496/**
497 * Set GET request headers.
498 * @param apiKey App API key.
499 */
500function getHeaders(apiKey) {
501 return new Headers({
502 Accept: 'application/json',
503 'x-goog-api-key': apiKey
504 });
505}
506/**
507 * Fetches dynamic config from backend.
508 * @param app Firebase app to fetch config for.
509 */
510function fetchDynamicConfig(appFields) {
511 var _a;
512 return tslib.__awaiter(this, void 0, void 0, function () {
513 var appId, apiKey, request, appUrl, response, errorMessage, jsonResponse;
514 return tslib.__generator(this, function (_b) {
515 switch (_b.label) {
516 case 0:
517 appId = appFields.appId, apiKey = appFields.apiKey;
518 request = {
519 method: 'GET',
520 headers: getHeaders(apiKey)
521 };
522 appUrl = DYNAMIC_CONFIG_URL.replace('{app-id}', appId);
523 return [4 /*yield*/, fetch(appUrl, request)];
524 case 1:
525 response = _b.sent();
526 if (!(response.status !== 200 && response.status !== 304)) return [3 /*break*/, 6];
527 errorMessage = '';
528 _b.label = 2;
529 case 2:
530 _b.trys.push([2, 4, , 5]);
531 return [4 /*yield*/, response.json()];
532 case 3:
533 jsonResponse = (_b.sent());
534 if ((_a = jsonResponse.error) === null || _a === void 0 ? void 0 : _a.message) {
535 errorMessage = jsonResponse.error.message;
536 }
537 return [3 /*break*/, 5];
538 case 4:
539 _b.sent();
540 return [3 /*break*/, 5];
541 case 5: throw ERROR_FACTORY.create("config-fetch-failed" /* AnalyticsError.CONFIG_FETCH_FAILED */, {
542 httpStatus: response.status,
543 responseMessage: errorMessage
544 });
545 case 6: return [2 /*return*/, response.json()];
546 }
547 });
548 });
549}
550/**
551 * Fetches dynamic config from backend, retrying if failed.
552 * @param app Firebase app to fetch config for.
553 */
554function fetchDynamicConfigWithRetry(app,
555// retryData and timeoutMillis are parameterized to allow passing a different value for testing.
556retryData, timeoutMillis) {
557 if (retryData === void 0) { retryData = defaultRetryData; }
558 return tslib.__awaiter(this, void 0, void 0, function () {
559 var _a, appId, apiKey, measurementId, throttleMetadata, signal;
560 var _this = this;
561 return tslib.__generator(this, function (_b) {
562 _a = app.options, appId = _a.appId, apiKey = _a.apiKey, measurementId = _a.measurementId;
563 if (!appId) {
564 throw ERROR_FACTORY.create("no-app-id" /* AnalyticsError.NO_APP_ID */);
565 }
566 if (!apiKey) {
567 if (measurementId) {
568 return [2 /*return*/, {
569 measurementId: measurementId,
570 appId: appId
571 }];
572 }
573 throw ERROR_FACTORY.create("no-api-key" /* AnalyticsError.NO_API_KEY */);
574 }
575 throttleMetadata = retryData.getThrottleMetadata(appId) || {
576 backoffCount: 0,
577 throttleEndTimeMillis: Date.now()
578 };
579 signal = new AnalyticsAbortSignal();
580 setTimeout(function () { return tslib.__awaiter(_this, void 0, void 0, function () {
581 return tslib.__generator(this, function (_a) {
582 // Note a very low delay, eg < 10ms, can elapse before listeners are initialized.
583 signal.abort();
584 return [2 /*return*/];
585 });
586 }); }, timeoutMillis !== undefined ? timeoutMillis : FETCH_TIMEOUT_MILLIS);
587 return [2 /*return*/, attemptFetchDynamicConfigWithRetry({ appId: appId, apiKey: apiKey, measurementId: measurementId }, throttleMetadata, signal, retryData)];
588 });
589 });
590}
591/**
592 * Runs one retry attempt.
593 * @param appFields Necessary app config fields.
594 * @param throttleMetadata Ongoing metadata to determine throttling times.
595 * @param signal Abort signal.
596 */
597function attemptFetchDynamicConfigWithRetry(appFields, _a, signal, retryData // for testing
598) {
599 var _b;
600 var throttleEndTimeMillis = _a.throttleEndTimeMillis, backoffCount = _a.backoffCount;
601 if (retryData === void 0) { retryData = defaultRetryData; }
602 return tslib.__awaiter(this, void 0, void 0, function () {
603 var appId, measurementId, e_1, response, e_2, error, backoffMillis, throttleMetadata;
604 return tslib.__generator(this, function (_c) {
605 switch (_c.label) {
606 case 0:
607 appId = appFields.appId, measurementId = appFields.measurementId;
608 _c.label = 1;
609 case 1:
610 _c.trys.push([1, 3, , 4]);
611 return [4 /*yield*/, setAbortableTimeout(signal, throttleEndTimeMillis)];
612 case 2:
613 _c.sent();
614 return [3 /*break*/, 4];
615 case 3:
616 e_1 = _c.sent();
617 if (measurementId) {
618 logger.warn("Timed out fetching this Firebase app's measurement ID from the server." +
619 " Falling back to the measurement ID ".concat(measurementId) +
620 " provided in the \"measurementId\" field in the local Firebase config. [".concat(e_1 === null || e_1 === void 0 ? void 0 : e_1.message, "]"));
621 return [2 /*return*/, { appId: appId, measurementId: measurementId }];
622 }
623 throw e_1;
624 case 4:
625 _c.trys.push([4, 6, , 7]);
626 return [4 /*yield*/, fetchDynamicConfig(appFields)];
627 case 5:
628 response = _c.sent();
629 // Note the SDK only clears throttle state if response is success or non-retriable.
630 retryData.deleteThrottleMetadata(appId);
631 return [2 /*return*/, response];
632 case 6:
633 e_2 = _c.sent();
634 error = e_2;
635 if (!isRetriableError(error)) {
636 retryData.deleteThrottleMetadata(appId);
637 if (measurementId) {
638 logger.warn("Failed to fetch this Firebase app's measurement ID from the server." +
639 " Falling back to the measurement ID ".concat(measurementId) +
640 " provided in the \"measurementId\" field in the local Firebase config. [".concat(error === null || error === void 0 ? void 0 : error.message, "]"));
641 return [2 /*return*/, { appId: appId, measurementId: measurementId }];
642 }
643 else {
644 throw e_2;
645 }
646 }
647 backoffMillis = Number((_b = error === null || error === void 0 ? void 0 : error.customData) === null || _b === void 0 ? void 0 : _b.httpStatus) === 503
648 ? util.calculateBackoffMillis(backoffCount, retryData.intervalMillis, LONG_RETRY_FACTOR)
649 : util.calculateBackoffMillis(backoffCount, retryData.intervalMillis);
650 throttleMetadata = {
651 throttleEndTimeMillis: Date.now() + backoffMillis,
652 backoffCount: backoffCount + 1
653 };
654 // Persists state.
655 retryData.setThrottleMetadata(appId, throttleMetadata);
656 logger.debug("Calling attemptFetch again in ".concat(backoffMillis, " millis"));
657 return [2 /*return*/, attemptFetchDynamicConfigWithRetry(appFields, throttleMetadata, signal, retryData)];
658 case 7: return [2 /*return*/];
659 }
660 });
661 });
662}
663/**
664 * Supports waiting on a backoff by:
665 *
666 * <ul>
667 * <li>Promisifying setTimeout, so we can set a timeout in our Promise chain</li>
668 * <li>Listening on a signal bus for abort events, just like the Fetch API</li>
669 * <li>Failing in the same way the Fetch API fails, so timing out a live request and a throttled
670 * request appear the same.</li>
671 * </ul>
672 *
673 * <p>Visible for testing.
674 */
675function setAbortableTimeout(signal, throttleEndTimeMillis) {
676 return new Promise(function (resolve, reject) {
677 // Derives backoff from given end time, normalizing negative numbers to zero.
678 var backoffMillis = Math.max(throttleEndTimeMillis - Date.now(), 0);
679 var timeout = setTimeout(resolve, backoffMillis);
680 // Adds listener, rather than sets onabort, because signal is a shared object.
681 signal.addEventListener(function () {
682 clearTimeout(timeout);
683 // If the request completes before this timeout, the rejection has no effect.
684 reject(ERROR_FACTORY.create("fetch-throttle" /* AnalyticsError.FETCH_THROTTLE */, {
685 throttleEndTimeMillis: throttleEndTimeMillis
686 }));
687 });
688 });
689}
690/**
691 * Returns true if the {@link Error} indicates a fetch request may succeed later.
692 */
693function isRetriableError(e) {
694 if (!(e instanceof util.FirebaseError) || !e.customData) {
695 return false;
696 }
697 // Uses string index defined by ErrorData, which FirebaseError implements.
698 var httpStatus = Number(e.customData['httpStatus']);
699 return (httpStatus === 429 ||
700 httpStatus === 500 ||
701 httpStatus === 503 ||
702 httpStatus === 504);
703}
704/**
705 * Shims a minimal AbortSignal (copied from Remote Config).
706 *
707 * <p>AbortController's AbortSignal conveniently decouples fetch timeout logic from other aspects
708 * of networking, such as retries. Firebase doesn't use AbortController enough to justify a
709 * polyfill recommendation, like we do with the Fetch API, but this minimal shim can easily be
710 * swapped out if/when we do.
711 */
712var AnalyticsAbortSignal = /** @class */ (function () {
713 function AnalyticsAbortSignal() {
714 this.listeners = [];
715 }
716 AnalyticsAbortSignal.prototype.addEventListener = function (listener) {
717 this.listeners.push(listener);
718 };
719 AnalyticsAbortSignal.prototype.abort = function () {
720 this.listeners.forEach(function (listener) { return listener(); });
721 };
722 return AnalyticsAbortSignal;
723}());
724
725/**
726 * @license
727 * Copyright 2019 Google LLC
728 *
729 * Licensed under the Apache License, Version 2.0 (the "License");
730 * you may not use this file except in compliance with the License.
731 * You may obtain a copy of the License at
732 *
733 * http://www.apache.org/licenses/LICENSE-2.0
734 *
735 * Unless required by applicable law or agreed to in writing, software
736 * distributed under the License is distributed on an "AS IS" BASIS,
737 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
738 * See the License for the specific language governing permissions and
739 * limitations under the License.
740 */
741/**
742 * Event parameters to set on 'gtag' during initialization.
743 */
744var defaultEventParametersForInit;
745/**
746 * Logs an analytics event through the Firebase SDK.
747 *
748 * @param gtagFunction Wrapped gtag function that waits for fid to be set before sending an event
749 * @param eventName Google Analytics event name, choose from standard list or use a custom string.
750 * @param eventParams Analytics event parameters.
751 */
752function logEvent$1(gtagFunction, initializationPromise, eventName, eventParams, options) {
753 return tslib.__awaiter(this, void 0, void 0, function () {
754 var measurementId, params;
755 return tslib.__generator(this, function (_a) {
756 switch (_a.label) {
757 case 0:
758 if (!(options && options.global)) return [3 /*break*/, 1];
759 gtagFunction("event" /* GtagCommand.EVENT */, eventName, eventParams);
760 return [2 /*return*/];
761 case 1: return [4 /*yield*/, initializationPromise];
762 case 2:
763 measurementId = _a.sent();
764 params = tslib.__assign(tslib.__assign({}, eventParams), { 'send_to': measurementId });
765 gtagFunction("event" /* GtagCommand.EVENT */, eventName, params);
766 _a.label = 3;
767 case 3: return [2 /*return*/];
768 }
769 });
770 });
771}
772/**
773 * Set screen_name parameter for this Google Analytics ID.
774 *
775 * @deprecated Use {@link logEvent} with `eventName` as 'screen_view' and add relevant `eventParams`.
776 * See {@link https://firebase.google.com/docs/analytics/screenviews | Track Screenviews}.
777 *
778 * @param gtagFunction Wrapped gtag function that waits for fid to be set before sending an event
779 * @param screenName Screen name string to set.
780 */
781function setCurrentScreen$1(gtagFunction, initializationPromise, screenName, options) {
782 return tslib.__awaiter(this, void 0, void 0, function () {
783 var measurementId;
784 return tslib.__generator(this, function (_a) {
785 switch (_a.label) {
786 case 0:
787 if (!(options && options.global)) return [3 /*break*/, 1];
788 gtagFunction("set" /* GtagCommand.SET */, { 'screen_name': screenName });
789 return [2 /*return*/, Promise.resolve()];
790 case 1: return [4 /*yield*/, initializationPromise];
791 case 2:
792 measurementId = _a.sent();
793 gtagFunction("config" /* GtagCommand.CONFIG */, measurementId, {
794 update: true,
795 'screen_name': screenName
796 });
797 _a.label = 3;
798 case 3: return [2 /*return*/];
799 }
800 });
801 });
802}
803/**
804 * Set user_id parameter for this Google Analytics ID.
805 *
806 * @param gtagFunction Wrapped gtag function that waits for fid to be set before sending an event
807 * @param id User ID string to set
808 */
809function setUserId$1(gtagFunction, initializationPromise, id, options) {
810 return tslib.__awaiter(this, void 0, void 0, function () {
811 var measurementId;
812 return tslib.__generator(this, function (_a) {
813 switch (_a.label) {
814 case 0:
815 if (!(options && options.global)) return [3 /*break*/, 1];
816 gtagFunction("set" /* GtagCommand.SET */, { 'user_id': id });
817 return [2 /*return*/, Promise.resolve()];
818 case 1: return [4 /*yield*/, initializationPromise];
819 case 2:
820 measurementId = _a.sent();
821 gtagFunction("config" /* GtagCommand.CONFIG */, measurementId, {
822 update: true,
823 'user_id': id
824 });
825 _a.label = 3;
826 case 3: return [2 /*return*/];
827 }
828 });
829 });
830}
831/**
832 * Set all other user properties other than user_id and screen_name.
833 *
834 * @param gtagFunction Wrapped gtag function that waits for fid to be set before sending an event
835 * @param properties Map of user properties to set
836 */
837function setUserProperties$1(gtagFunction, initializationPromise, properties, options) {
838 return tslib.__awaiter(this, void 0, void 0, function () {
839 var flatProperties, _i, _a, key, measurementId;
840 return tslib.__generator(this, function (_b) {
841 switch (_b.label) {
842 case 0:
843 if (!(options && options.global)) return [3 /*break*/, 1];
844 flatProperties = {};
845 for (_i = 0, _a = Object.keys(properties); _i < _a.length; _i++) {
846 key = _a[_i];
847 // use dot notation for merge behavior in gtag.js
848 flatProperties["user_properties.".concat(key)] = properties[key];
849 }
850 gtagFunction("set" /* GtagCommand.SET */, flatProperties);
851 return [2 /*return*/, Promise.resolve()];
852 case 1: return [4 /*yield*/, initializationPromise];
853 case 2:
854 measurementId = _b.sent();
855 gtagFunction("config" /* GtagCommand.CONFIG */, measurementId, {
856 update: true,
857 'user_properties': properties
858 });
859 _b.label = 3;
860 case 3: return [2 /*return*/];
861 }
862 });
863 });
864}
865/**
866 * Retrieves a unique Google Analytics identifier for the web client.
867 * See {@link https://developers.google.com/analytics/devguides/collection/ga4/reference/config#client_id | client_id}.
868 *
869 * @param gtagFunction Wrapped gtag function that waits for fid to be set before sending an event
870 */
871function internalGetGoogleAnalyticsClientId(gtagFunction, initializationPromise) {
872 return tslib.__awaiter(this, void 0, void 0, function () {
873 var measurementId;
874 return tslib.__generator(this, function (_a) {
875 switch (_a.label) {
876 case 0: return [4 /*yield*/, initializationPromise];
877 case 1:
878 measurementId = _a.sent();
879 return [2 /*return*/, new Promise(function (resolve, reject) {
880 gtagFunction("get" /* GtagCommand.GET */, measurementId, 'client_id', function (clientId) {
881 if (!clientId) {
882 reject(ERROR_FACTORY.create("no-client-id" /* AnalyticsError.NO_CLIENT_ID */));
883 }
884 resolve(clientId);
885 });
886 })];
887 }
888 });
889 });
890}
891/**
892 * Set whether collection is enabled for this ID.
893 *
894 * @param enabled If true, collection is enabled for this ID.
895 */
896function setAnalyticsCollectionEnabled$1(initializationPromise, enabled) {
897 return tslib.__awaiter(this, void 0, void 0, function () {
898 var measurementId;
899 return tslib.__generator(this, function (_a) {
900 switch (_a.label) {
901 case 0: return [4 /*yield*/, initializationPromise];
902 case 1:
903 measurementId = _a.sent();
904 window["ga-disable-".concat(measurementId)] = !enabled;
905 return [2 /*return*/];
906 }
907 });
908 });
909}
910/**
911 * Consent parameters to default to during 'gtag' initialization.
912 */
913var defaultConsentSettingsForInit;
914/**
915 * Sets the variable {@link defaultConsentSettingsForInit} for use in the initialization of
916 * analytics.
917 *
918 * @param consentSettings Maps the applicable end user consent state for gtag.js.
919 */
920function _setConsentDefaultForInit(consentSettings) {
921 defaultConsentSettingsForInit = consentSettings;
922}
923/**
924 * Sets the variable `defaultEventParametersForInit` for use in the initialization of
925 * analytics.
926 *
927 * @param customParams Any custom params the user may pass to gtag.js.
928 */
929function _setDefaultEventParametersForInit(customParams) {
930 defaultEventParametersForInit = customParams;
931}
932
933/**
934 * @license
935 * Copyright 2020 Google LLC
936 *
937 * Licensed under the Apache License, Version 2.0 (the "License");
938 * you may not use this file except in compliance with the License.
939 * You may obtain a copy of the License at
940 *
941 * http://www.apache.org/licenses/LICENSE-2.0
942 *
943 * Unless required by applicable law or agreed to in writing, software
944 * distributed under the License is distributed on an "AS IS" BASIS,
945 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
946 * See the License for the specific language governing permissions and
947 * limitations under the License.
948 */
949function validateIndexedDB() {
950 return tslib.__awaiter(this, void 0, void 0, function () {
951 var e_1;
952 return tslib.__generator(this, function (_a) {
953 switch (_a.label) {
954 case 0:
955 if (!!util.isIndexedDBAvailable()) return [3 /*break*/, 1];
956 logger.warn(ERROR_FACTORY.create("indexeddb-unavailable" /* AnalyticsError.INDEXEDDB_UNAVAILABLE */, {
957 errorInfo: 'IndexedDB is not available in this environment.'
958 }).message);
959 return [2 /*return*/, false];
960 case 1:
961 _a.trys.push([1, 3, , 4]);
962 return [4 /*yield*/, util.validateIndexedDBOpenable()];
963 case 2:
964 _a.sent();
965 return [3 /*break*/, 4];
966 case 3:
967 e_1 = _a.sent();
968 logger.warn(ERROR_FACTORY.create("indexeddb-unavailable" /* AnalyticsError.INDEXEDDB_UNAVAILABLE */, {
969 errorInfo: e_1 === null || e_1 === void 0 ? void 0 : e_1.toString()
970 }).message);
971 return [2 /*return*/, false];
972 case 4: return [2 /*return*/, true];
973 }
974 });
975 });
976}
977/**
978 * Initialize the analytics instance in gtag.js by calling config command with fid.
979 *
980 * NOTE: We combine analytics initialization and setting fid together because we want fid to be
981 * part of the `page_view` event that's sent during the initialization
982 * @param app Firebase app
983 * @param gtagCore The gtag function that's not wrapped.
984 * @param dynamicConfigPromisesList Array of all dynamic config promises.
985 * @param measurementIdToAppId Maps measurementID to appID.
986 * @param installations _FirebaseInstallationsInternal instance.
987 *
988 * @returns Measurement ID.
989 */
990function _initializeAnalytics(app, dynamicConfigPromisesList, measurementIdToAppId, installations, gtagCore, dataLayerName, options) {
991 var _a;
992 return tslib.__awaiter(this, void 0, void 0, function () {
993 var dynamicConfigPromise, fidPromise, _b, dynamicConfig, fid, configProperties;
994 return tslib.__generator(this, function (_c) {
995 switch (_c.label) {
996 case 0:
997 dynamicConfigPromise = fetchDynamicConfigWithRetry(app);
998 // Once fetched, map measurementIds to appId, for ease of lookup in wrapped gtag function.
999 dynamicConfigPromise
1000 .then(function (config) {
1001 measurementIdToAppId[config.measurementId] = config.appId;
1002 if (app.options.measurementId &&
1003 config.measurementId !== app.options.measurementId) {
1004 logger.warn("The measurement ID in the local Firebase config (".concat(app.options.measurementId, ")") +
1005 " does not match the measurement ID fetched from the server (".concat(config.measurementId, ").") +
1006 " To ensure analytics events are always sent to the correct Analytics property," +
1007 " update the" +
1008 " measurement ID field in the local config or remove it from the local config.");
1009 }
1010 })
1011 .catch(function (e) { return logger.error(e); });
1012 // Add to list to track state of all dynamic config promises.
1013 dynamicConfigPromisesList.push(dynamicConfigPromise);
1014 fidPromise = validateIndexedDB().then(function (envIsValid) {
1015 if (envIsValid) {
1016 return installations.getId();
1017 }
1018 else {
1019 return undefined;
1020 }
1021 });
1022 return [4 /*yield*/, Promise.all([
1023 dynamicConfigPromise,
1024 fidPromise
1025 ])];
1026 case 1:
1027 _b = _c.sent(), dynamicConfig = _b[0], fid = _b[1];
1028 // Detect if user has already put the gtag <script> tag on this page with the passed in
1029 // data layer name.
1030 if (!findGtagScriptOnPage(dataLayerName)) {
1031 insertScriptTag(dataLayerName, dynamicConfig.measurementId);
1032 }
1033 // Detects if there are consent settings that need to be configured.
1034 if (defaultConsentSettingsForInit) {
1035 gtagCore("consent" /* GtagCommand.CONSENT */, 'default', defaultConsentSettingsForInit);
1036 _setConsentDefaultForInit(undefined);
1037 }
1038 // This command initializes gtag.js and only needs to be called once for the entire web app,
1039 // but since it is idempotent, we can call it multiple times.
1040 // We keep it together with other initialization logic for better code structure.
1041 // eslint-disable-next-line @typescript-eslint/no-explicit-any
1042 gtagCore('js', new Date());
1043 configProperties = (_a = options === null || options === void 0 ? void 0 : options.config) !== null && _a !== void 0 ? _a : {};
1044 // guard against developers accidentally setting properties with prefix `firebase_`
1045 configProperties[ORIGIN_KEY] = 'firebase';
1046 configProperties.update = true;
1047 if (fid != null) {
1048 configProperties[GA_FID_KEY] = fid;
1049 }
1050 // It should be the first config command called on this GA-ID
1051 // Initialize this GA-ID and set FID on it using the gtag config API.
1052 // Note: This will trigger a page_view event unless 'send_page_view' is set to false in
1053 // `configProperties`.
1054 gtagCore("config" /* GtagCommand.CONFIG */, dynamicConfig.measurementId, configProperties);
1055 // Detects if there is data that will be set on every event logged from the SDK.
1056 if (defaultEventParametersForInit) {
1057 gtagCore("set" /* GtagCommand.SET */, defaultEventParametersForInit);
1058 _setDefaultEventParametersForInit(undefined);
1059 }
1060 return [2 /*return*/, dynamicConfig.measurementId];
1061 }
1062 });
1063 });
1064}
1065
1066/**
1067 * @license
1068 * Copyright 2019 Google LLC
1069 *
1070 * Licensed under the Apache License, Version 2.0 (the "License");
1071 * you may not use this file except in compliance with the License.
1072 * You may obtain a copy of the License at
1073 *
1074 * http://www.apache.org/licenses/LICENSE-2.0
1075 *
1076 * Unless required by applicable law or agreed to in writing, software
1077 * distributed under the License is distributed on an "AS IS" BASIS,
1078 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1079 * See the License for the specific language governing permissions and
1080 * limitations under the License.
1081 */
1082/**
1083 * Analytics Service class.
1084 */
1085var AnalyticsService = /** @class */ (function () {
1086 function AnalyticsService(app) {
1087 this.app = app;
1088 }
1089 AnalyticsService.prototype._delete = function () {
1090 delete initializationPromisesMap[this.app.options.appId];
1091 return Promise.resolve();
1092 };
1093 return AnalyticsService;
1094}());
1095/**
1096 * Maps appId to full initialization promise. Wrapped gtag calls must wait on
1097 * all or some of these, depending on the call's `send_to` param and the status
1098 * of the dynamic config fetches (see below).
1099 */
1100var initializationPromisesMap = {};
1101/**
1102 * List of dynamic config fetch promises. In certain cases, wrapped gtag calls
1103 * wait on all these to be complete in order to determine if it can selectively
1104 * wait for only certain initialization (FID) promises or if it must wait for all.
1105 */
1106var dynamicConfigPromisesList = [];
1107/**
1108 * Maps fetched measurementIds to appId. Populated when the app's dynamic config
1109 * fetch completes. If already populated, gtag config calls can use this to
1110 * selectively wait for only this app's initialization promise (FID) instead of all
1111 * initialization promises.
1112 */
1113var measurementIdToAppId = {};
1114/**
1115 * Name for window global data layer array used by GA: defaults to 'dataLayer'.
1116 */
1117var dataLayerName = 'dataLayer';
1118/**
1119 * Name for window global gtag function used by GA: defaults to 'gtag'.
1120 */
1121var gtagName = 'gtag';
1122/**
1123 * Reproduction of standard gtag function or reference to existing
1124 * gtag function on window object.
1125 */
1126var gtagCoreFunction;
1127/**
1128 * Wrapper around gtag function that ensures FID is sent with all
1129 * relevant event and config calls.
1130 */
1131var wrappedGtagFunction;
1132/**
1133 * Flag to ensure page initialization steps (creation or wrapping of
1134 * dataLayer and gtag script) are only run once per page load.
1135 */
1136var globalInitDone = false;
1137/**
1138 * Configures Firebase Analytics to use custom `gtag` or `dataLayer` names.
1139 * Intended to be used if `gtag.js` script has been installed on
1140 * this page independently of Firebase Analytics, and is using non-default
1141 * names for either the `gtag` function or for `dataLayer`.
1142 * Must be called before calling `getAnalytics()` or it won't
1143 * have any effect.
1144 *
1145 * @public
1146 *
1147 * @param options - Custom gtag and dataLayer names.
1148 */
1149function settings(options) {
1150 if (globalInitDone) {
1151 throw ERROR_FACTORY.create("already-initialized" /* AnalyticsError.ALREADY_INITIALIZED */);
1152 }
1153 if (options.dataLayerName) {
1154 dataLayerName = options.dataLayerName;
1155 }
1156 if (options.gtagName) {
1157 gtagName = options.gtagName;
1158 }
1159}
1160/**
1161 * Returns true if no environment mismatch is found.
1162 * If environment mismatches are found, throws an INVALID_ANALYTICS_CONTEXT
1163 * error that also lists details for each mismatch found.
1164 */
1165function warnOnBrowserContextMismatch() {
1166 var mismatchedEnvMessages = [];
1167 if (util.isBrowserExtension()) {
1168 mismatchedEnvMessages.push('This is a browser extension environment.');
1169 }
1170 if (!util.areCookiesEnabled()) {
1171 mismatchedEnvMessages.push('Cookies are not available.');
1172 }
1173 if (mismatchedEnvMessages.length > 0) {
1174 var details = mismatchedEnvMessages
1175 .map(function (message, index) { return "(".concat(index + 1, ") ").concat(message); })
1176 .join(' ');
1177 var err = ERROR_FACTORY.create("invalid-analytics-context" /* AnalyticsError.INVALID_ANALYTICS_CONTEXT */, {
1178 errorInfo: details
1179 });
1180 logger.warn(err.message);
1181 }
1182}
1183/**
1184 * Analytics instance factory.
1185 * @internal
1186 */
1187function factory(app, installations, options) {
1188 warnOnBrowserContextMismatch();
1189 var appId = app.options.appId;
1190 if (!appId) {
1191 throw ERROR_FACTORY.create("no-app-id" /* AnalyticsError.NO_APP_ID */);
1192 }
1193 if (!app.options.apiKey) {
1194 if (app.options.measurementId) {
1195 logger.warn("The \"apiKey\" field is empty in the local Firebase config. This is needed to fetch the latest" +
1196 " measurement ID for this Firebase app. Falling back to the measurement ID ".concat(app.options.measurementId) +
1197 " provided in the \"measurementId\" field in the local Firebase config.");
1198 }
1199 else {
1200 throw ERROR_FACTORY.create("no-api-key" /* AnalyticsError.NO_API_KEY */);
1201 }
1202 }
1203 if (initializationPromisesMap[appId] != null) {
1204 throw ERROR_FACTORY.create("already-exists" /* AnalyticsError.ALREADY_EXISTS */, {
1205 id: appId
1206 });
1207 }
1208 if (!globalInitDone) {
1209 // Steps here should only be done once per page: creation or wrapping
1210 // of dataLayer and global gtag function.
1211 getOrCreateDataLayer(dataLayerName);
1212 var _a = wrapOrCreateGtag(initializationPromisesMap, dynamicConfigPromisesList, measurementIdToAppId, dataLayerName, gtagName), wrappedGtag = _a.wrappedGtag, gtagCore = _a.gtagCore;
1213 wrappedGtagFunction = wrappedGtag;
1214 gtagCoreFunction = gtagCore;
1215 globalInitDone = true;
1216 }
1217 // Async but non-blocking.
1218 // This map reflects the completion state of all promises for each appId.
1219 initializationPromisesMap[appId] = _initializeAnalytics(app, dynamicConfigPromisesList, measurementIdToAppId, installations, gtagCoreFunction, dataLayerName, options);
1220 var analyticsInstance = new AnalyticsService(app);
1221 return analyticsInstance;
1222}
1223
1224/* eslint-disable @typescript-eslint/no-explicit-any */
1225/**
1226 * Returns an {@link Analytics} instance for the given app.
1227 *
1228 * @public
1229 *
1230 * @param app - The {@link @firebase/app#FirebaseApp} to use.
1231 */
1232function getAnalytics(app$1) {
1233 if (app$1 === void 0) { app$1 = app.getApp(); }
1234 app$1 = util.getModularInstance(app$1);
1235 // Dependencies
1236 var analyticsProvider = app._getProvider(app$1, ANALYTICS_TYPE);
1237 if (analyticsProvider.isInitialized()) {
1238 return analyticsProvider.getImmediate();
1239 }
1240 return initializeAnalytics(app$1);
1241}
1242/**
1243 * Returns an {@link Analytics} instance for the given app.
1244 *
1245 * @public
1246 *
1247 * @param app - The {@link @firebase/app#FirebaseApp} to use.
1248 */
1249function initializeAnalytics(app$1, options) {
1250 if (options === void 0) { options = {}; }
1251 // Dependencies
1252 var analyticsProvider = app._getProvider(app$1, ANALYTICS_TYPE);
1253 if (analyticsProvider.isInitialized()) {
1254 var existingInstance = analyticsProvider.getImmediate();
1255 if (util.deepEqual(options, analyticsProvider.getOptions())) {
1256 return existingInstance;
1257 }
1258 else {
1259 throw ERROR_FACTORY.create("already-initialized" /* AnalyticsError.ALREADY_INITIALIZED */);
1260 }
1261 }
1262 var analyticsInstance = analyticsProvider.initialize({ options: options });
1263 return analyticsInstance;
1264}
1265/**
1266 * This is a public static method provided to users that wraps four different checks:
1267 *
1268 * 1. Check if it's not a browser extension environment.
1269 * 2. Check if cookies are enabled in current browser.
1270 * 3. Check if IndexedDB is supported by the browser environment.
1271 * 4. Check if the current browser context is valid for using `IndexedDB.open()`.
1272 *
1273 * @public
1274 *
1275 */
1276function isSupported() {
1277 return tslib.__awaiter(this, void 0, void 0, function () {
1278 var isDBOpenable;
1279 return tslib.__generator(this, function (_a) {
1280 switch (_a.label) {
1281 case 0:
1282 if (util.isBrowserExtension()) {
1283 return [2 /*return*/, false];
1284 }
1285 if (!util.areCookiesEnabled()) {
1286 return [2 /*return*/, false];
1287 }
1288 if (!util.isIndexedDBAvailable()) {
1289 return [2 /*return*/, false];
1290 }
1291 _a.label = 1;
1292 case 1:
1293 _a.trys.push([1, 3, , 4]);
1294 return [4 /*yield*/, util.validateIndexedDBOpenable()];
1295 case 2:
1296 isDBOpenable = _a.sent();
1297 return [2 /*return*/, isDBOpenable];
1298 case 3:
1299 _a.sent();
1300 return [2 /*return*/, false];
1301 case 4: return [2 /*return*/];
1302 }
1303 });
1304 });
1305}
1306/**
1307 * Use gtag `config` command to set `screen_name`.
1308 *
1309 * @public
1310 *
1311 * @deprecated Use {@link logEvent} with `eventName` as 'screen_view' and add relevant `eventParams`.
1312 * See {@link https://firebase.google.com/docs/analytics/screenviews | Track Screenviews}.
1313 *
1314 * @param analyticsInstance - The {@link Analytics} instance.
1315 * @param screenName - Screen name to set.
1316 */
1317function setCurrentScreen(analyticsInstance, screenName, options) {
1318 analyticsInstance = util.getModularInstance(analyticsInstance);
1319 setCurrentScreen$1(wrappedGtagFunction, initializationPromisesMap[analyticsInstance.app.options.appId], screenName, options).catch(function (e) { return logger.error(e); });
1320}
1321/**
1322 * Retrieves a unique Google Analytics identifier for the web client.
1323 * See {@link https://developers.google.com/analytics/devguides/collection/ga4/reference/config#client_id | client_id}.
1324 *
1325 * @public
1326 *
1327 * @param app - The {@link @firebase/app#FirebaseApp} to use.
1328 */
1329function getGoogleAnalyticsClientId(analyticsInstance) {
1330 return tslib.__awaiter(this, void 0, void 0, function () {
1331 return tslib.__generator(this, function (_a) {
1332 analyticsInstance = util.getModularInstance(analyticsInstance);
1333 return [2 /*return*/, internalGetGoogleAnalyticsClientId(wrappedGtagFunction, initializationPromisesMap[analyticsInstance.app.options.appId])];
1334 });
1335 });
1336}
1337/**
1338 * Use gtag `config` command to set `user_id`.
1339 *
1340 * @public
1341 *
1342 * @param analyticsInstance - The {@link Analytics} instance.
1343 * @param id - User ID to set.
1344 */
1345function setUserId(analyticsInstance, id, options) {
1346 analyticsInstance = util.getModularInstance(analyticsInstance);
1347 setUserId$1(wrappedGtagFunction, initializationPromisesMap[analyticsInstance.app.options.appId], id, options).catch(function (e) { return logger.error(e); });
1348}
1349/**
1350 * Use gtag `config` command to set all params specified.
1351 *
1352 * @public
1353 */
1354function setUserProperties(analyticsInstance, properties, options) {
1355 analyticsInstance = util.getModularInstance(analyticsInstance);
1356 setUserProperties$1(wrappedGtagFunction, initializationPromisesMap[analyticsInstance.app.options.appId], properties, options).catch(function (e) { return logger.error(e); });
1357}
1358/**
1359 * Sets whether Google Analytics collection is enabled for this app on this device.
1360 * Sets global `window['ga-disable-analyticsId'] = true;`
1361 *
1362 * @public
1363 *
1364 * @param analyticsInstance - The {@link Analytics} instance.
1365 * @param enabled - If true, enables collection, if false, disables it.
1366 */
1367function setAnalyticsCollectionEnabled(analyticsInstance, enabled) {
1368 analyticsInstance = util.getModularInstance(analyticsInstance);
1369 setAnalyticsCollectionEnabled$1(initializationPromisesMap[analyticsInstance.app.options.appId], enabled).catch(function (e) { return logger.error(e); });
1370}
1371/**
1372 * Adds data that will be set on every event logged from the SDK, including automatic ones.
1373 * With gtag's "set" command, the values passed persist on the current page and are passed with
1374 * all subsequent events.
1375 * @public
1376 * @param customParams - Any custom params the user may pass to gtag.js.
1377 */
1378function setDefaultEventParameters(customParams) {
1379 // Check if reference to existing gtag function on window object exists
1380 if (wrappedGtagFunction) {
1381 wrappedGtagFunction("set" /* GtagCommand.SET */, customParams);
1382 }
1383 else {
1384 _setDefaultEventParametersForInit(customParams);
1385 }
1386}
1387/**
1388 * Sends a Google Analytics event with given `eventParams`. This method
1389 * automatically associates this logged event with this Firebase web
1390 * app instance on this device.
1391 * List of official event parameters can be found in the gtag.js
1392 * reference documentation:
1393 * {@link https://developers.google.com/gtagjs/reference/ga4-events
1394 * | the GA4 reference documentation}.
1395 *
1396 * @public
1397 */
1398function logEvent(analyticsInstance, eventName, eventParams, options) {
1399 analyticsInstance = util.getModularInstance(analyticsInstance);
1400 logEvent$1(wrappedGtagFunction, initializationPromisesMap[analyticsInstance.app.options.appId], eventName, eventParams, options).catch(function (e) { return logger.error(e); });
1401}
1402/**
1403 * Sets the applicable end user consent state for this web app across all gtag references once
1404 * Firebase Analytics is initialized.
1405 *
1406 * Use the {@link ConsentSettings} to specify individual consent type values. By default consent
1407 * types are set to "granted".
1408 * @public
1409 * @param consentSettings - Maps the applicable end user consent state for gtag.js.
1410 */
1411function setConsent(consentSettings) {
1412 // Check if reference to existing gtag function on window object exists
1413 if (wrappedGtagFunction) {
1414 wrappedGtagFunction("consent" /* GtagCommand.CONSENT */, 'update', consentSettings);
1415 }
1416 else {
1417 _setConsentDefaultForInit(consentSettings);
1418 }
1419}
1420
1421var name = "@firebase/analytics";
1422var version = "0.10.0";
1423
1424/**
1425 * Firebase Analytics
1426 *
1427 * @packageDocumentation
1428 */
1429function registerAnalytics() {
1430 app._registerComponent(new component.Component(ANALYTICS_TYPE, function (container, _a) {
1431 var analyticsOptions = _a.options;
1432 // getImmediate for FirebaseApp will always succeed
1433 var app = container.getProvider('app').getImmediate();
1434 var installations = container
1435 .getProvider('installations-internal')
1436 .getImmediate();
1437 return factory(app, installations, analyticsOptions);
1438 }, "PUBLIC" /* ComponentType.PUBLIC */));
1439 app._registerComponent(new component.Component('analytics-internal', internalFactory, "PRIVATE" /* ComponentType.PRIVATE */));
1440 app.registerVersion(name, version);
1441 // BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation
1442 app.registerVersion(name, version, 'cjs5');
1443 function internalFactory(container) {
1444 try {
1445 var analytics_1 = container.getProvider(ANALYTICS_TYPE).getImmediate();
1446 return {
1447 logEvent: function (eventName, eventParams, options) { return logEvent(analytics_1, eventName, eventParams, options); }
1448 };
1449 }
1450 catch (e) {
1451 throw ERROR_FACTORY.create("interop-component-reg-failed" /* AnalyticsError.INTEROP_COMPONENT_REG_FAILED */, {
1452 reason: e
1453 });
1454 }
1455 }
1456}
1457registerAnalytics();
1458
1459exports.getAnalytics = getAnalytics;
1460exports.getGoogleAnalyticsClientId = getGoogleAnalyticsClientId;
1461exports.initializeAnalytics = initializeAnalytics;
1462exports.isSupported = isSupported;
1463exports.logEvent = logEvent;
1464exports.setAnalyticsCollectionEnabled = setAnalyticsCollectionEnabled;
1465exports.setConsent = setConsent;
1466exports.setCurrentScreen = setCurrentScreen;
1467exports.setDefaultEventParameters = setDefaultEventParameters;
1468exports.setUserId = setUserId;
1469exports.setUserProperties = setUserProperties;
1470exports.settings = settings;
1471//# sourceMappingURL=index.cjs.js.map