source: frontend/node_modules/workbox-google-analytics/src/initialize.ts

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 7.3 KB
Line 
1/*
2 Copyright 2018 Google LLC
3
4 Use of this source code is governed by an MIT-style
5 license that can be found in the LICENSE file or at
6 https://opensource.org/licenses/MIT.
7*/
8
9import {BackgroundSyncPlugin} from 'workbox-background-sync/BackgroundSyncPlugin.js';
10import {Queue} from 'workbox-background-sync/Queue.js';
11import {cacheNames} from 'workbox-core/_private/cacheNames.js';
12import {getFriendlyURL} from 'workbox-core/_private/getFriendlyURL.js';
13import {logger} from 'workbox-core/_private/logger.js';
14import {RouteMatchCallbackOptions} from 'workbox-core/types.js';
15import {Route} from 'workbox-routing/Route.js';
16import {Router} from 'workbox-routing/Router.js';
17import {NetworkFirst} from 'workbox-strategies/NetworkFirst.js';
18import {NetworkOnly} from 'workbox-strategies/NetworkOnly.js';
19import {
20 QUEUE_NAME,
21 MAX_RETENTION_TIME,
22 GOOGLE_ANALYTICS_HOST,
23 GTM_HOST,
24 ANALYTICS_JS_PATH,
25 GTAG_JS_PATH,
26 GTM_JS_PATH,
27 COLLECT_PATHS_REGEX,
28} from './utils/constants.js';
29import './_version.js';
30
31export interface GoogleAnalyticsInitializeOptions {
32 cacheName?: string;
33 parameterOverrides?: {[paramName: string]: string};
34 hitFilter?: (params: URLSearchParams) => void;
35}
36
37/**
38 * Creates the requestWillDequeue callback to be used with the background
39 * sync plugin. The callback takes the failed request and adds the
40 * `qt` param based on the current time, as well as applies any other
41 * user-defined hit modifications.
42 *
43 * @param {Object} config See {@link workbox-google-analytics.initialize}.
44 * @return {Function} The requestWillDequeue callback function.
45 *
46 * @private
47 */
48const createOnSyncCallback = (config: GoogleAnalyticsInitializeOptions) => {
49 return async ({queue}: {queue: Queue}) => {
50 let entry;
51 while ((entry = await queue.shiftRequest())) {
52 const {request, timestamp} = entry;
53 const url = new URL(request.url);
54
55 try {
56 // Measurement protocol requests can set their payload parameters in
57 // either the URL query string (for GET requests) or the POST body.
58 const params =
59 request.method === 'POST'
60 ? new URLSearchParams(await request.clone().text())
61 : url.searchParams;
62
63 // Calculate the qt param, accounting for the fact that an existing
64 // qt param may be present and should be updated rather than replaced.
65 const originalHitTime = timestamp! - (Number(params.get('qt')) || 0);
66 const queueTime = Date.now() - originalHitTime;
67
68 // Set the qt param prior to applying hitFilter or parameterOverrides.
69 params.set('qt', String(queueTime));
70
71 // Apply `parameterOverrides`, if set.
72 if (config.parameterOverrides) {
73 for (const param of Object.keys(config.parameterOverrides)) {
74 const value = config.parameterOverrides[param];
75 params.set(param, value);
76 }
77 }
78
79 // Apply `hitFilter`, if set.
80 if (typeof config.hitFilter === 'function') {
81 config.hitFilter.call(null, params);
82 }
83
84 // Retry the fetch. Ignore URL search params from the URL as they're
85 // now in the post body.
86 await fetch(
87 new Request(url.origin + url.pathname, {
88 body: params.toString(),
89 method: 'POST',
90 mode: 'cors',
91 credentials: 'omit',
92 headers: {'Content-Type': 'text/plain'},
93 }),
94 );
95
96 if (process.env.NODE_ENV !== 'production') {
97 logger.log(
98 `Request for '${getFriendlyURL(url.href)}' ` + `has been replayed`,
99 );
100 }
101 } catch (err) {
102 await queue.unshiftRequest(entry);
103
104 if (process.env.NODE_ENV !== 'production') {
105 logger.log(
106 `Request for '${getFriendlyURL(url.href)}' ` +
107 `failed to replay, putting it back in the queue.`,
108 );
109 }
110 throw err;
111 }
112 }
113 if (process.env.NODE_ENV !== 'production') {
114 logger.log(
115 `All Google Analytics request successfully replayed; ` +
116 `the queue is now empty!`,
117 );
118 }
119 };
120};
121
122/**
123 * Creates GET and POST routes to catch failed Measurement Protocol hits.
124 *
125 * @param {BackgroundSyncPlugin} bgSyncPlugin
126 * @return {Array<Route>} The created routes.
127 *
128 * @private
129 */
130const createCollectRoutes = (bgSyncPlugin: BackgroundSyncPlugin) => {
131 const match = ({url}: RouteMatchCallbackOptions) =>
132 url.hostname === GOOGLE_ANALYTICS_HOST &&
133 COLLECT_PATHS_REGEX.test(url.pathname);
134
135 const handler = new NetworkOnly({
136 plugins: [bgSyncPlugin],
137 });
138
139 return [new Route(match, handler, 'GET'), new Route(match, handler, 'POST')];
140};
141
142/**
143 * Creates a route with a network first strategy for the analytics.js script.
144 *
145 * @param {string} cacheName
146 * @return {Route} The created route.
147 *
148 * @private
149 */
150const createAnalyticsJsRoute = (cacheName: string) => {
151 const match = ({url}: RouteMatchCallbackOptions) =>
152 url.hostname === GOOGLE_ANALYTICS_HOST &&
153 url.pathname === ANALYTICS_JS_PATH;
154
155 const handler = new NetworkFirst({cacheName});
156
157 return new Route(match, handler, 'GET');
158};
159
160/**
161 * Creates a route with a network first strategy for the gtag.js script.
162 *
163 * @param {string} cacheName
164 * @return {Route} The created route.
165 *
166 * @private
167 */
168const createGtagJsRoute = (cacheName: string) => {
169 const match = ({url}: RouteMatchCallbackOptions) =>
170 url.hostname === GTM_HOST && url.pathname === GTAG_JS_PATH;
171
172 const handler = new NetworkFirst({cacheName});
173
174 return new Route(match, handler, 'GET');
175};
176
177/**
178 * Creates a route with a network first strategy for the gtm.js script.
179 *
180 * @param {string} cacheName
181 * @return {Route} The created route.
182 *
183 * @private
184 */
185const createGtmJsRoute = (cacheName: string) => {
186 const match = ({url}: RouteMatchCallbackOptions) =>
187 url.hostname === GTM_HOST && url.pathname === GTM_JS_PATH;
188
189 const handler = new NetworkFirst({cacheName});
190
191 return new Route(match, handler, 'GET');
192};
193
194/**
195 * @param {Object=} [options]
196 * @param {Object} [options.cacheName] The cache name to store and retrieve
197 * analytics.js. Defaults to the cache names provided by `workbox-core`.
198 * @param {Object} [options.parameterOverrides]
199 * [Measurement Protocol parameters](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters),
200 * expressed as key/value pairs, to be added to replayed Google Analytics
201 * requests. This can be used to, e.g., set a custom dimension indicating
202 * that the request was replayed.
203 * @param {Function} [options.hitFilter] A function that allows you to modify
204 * the hit parameters prior to replaying
205 * the hit. The function is invoked with the original hit's URLSearchParams
206 * object as its only argument.
207 *
208 * @memberof workbox-google-analytics
209 */
210const initialize = (options: GoogleAnalyticsInitializeOptions = {}): void => {
211 const cacheName = cacheNames.getGoogleAnalyticsName(options.cacheName);
212
213 const bgSyncPlugin = new BackgroundSyncPlugin(QUEUE_NAME, {
214 maxRetentionTime: MAX_RETENTION_TIME,
215 onSync: createOnSyncCallback(options),
216 });
217
218 const routes = [
219 createGtmJsRoute(cacheName),
220 createAnalyticsJsRoute(cacheName),
221 createGtagJsRoute(cacheName),
222 ...createCollectRoutes(bgSyncPlugin),
223 ];
224
225 const router = new Router();
226 for (const route of routes) {
227 router.registerRoute(route);
228 }
229
230 router.addFetchListener();
231};
232
233export {initialize};
Note: See TracBrowser for help on using the repository browser.