source: frontend/node_modules/workbox-broadcast-update/src/BroadcastCacheUpdate.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.4 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 {assert} from 'workbox-core/_private/assert.js';
10import {timeout} from 'workbox-core/_private/timeout.js';
11import {resultingClientExists} from 'workbox-core/_private/resultingClientExists.js';
12import {CacheDidUpdateCallbackParam} from 'workbox-core/types.js';
13import {logger} from 'workbox-core/_private/logger.js';
14import {responsesAreSame} from './responsesAreSame.js';
15import {
16 CACHE_UPDATED_MESSAGE_META,
17 CACHE_UPDATED_MESSAGE_TYPE,
18 DEFAULT_HEADERS_TO_CHECK,
19 NOTIFY_ALL_CLIENTS,
20} from './utils/constants.js';
21
22import './_version.js';
23
24// UA-sniff Safari: https://stackoverflow.com/questions/7944460/detect-safari-browser
25// TODO(philipwalton): remove once this Safari bug fix has been released.
26// https://bugs.webkit.org/show_bug.cgi?id=201169
27const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
28
29// Give TypeScript the correct global.
30declare let self: ServiceWorkerGlobalScope;
31
32export interface BroadcastCacheUpdateOptions {
33 headersToCheck?: string[];
34 generatePayload?: (
35 options: CacheDidUpdateCallbackParam,
36 ) => Record<string, any>;
37 notifyAllClients?: boolean;
38}
39
40/**
41 * Generates the default payload used in update messages. By default the
42 * payload includes the `cacheName` and `updatedURL` fields.
43 *
44 * @return Object
45 * @private
46 */
47function defaultPayloadGenerator(
48 data: CacheDidUpdateCallbackParam,
49): Record<string, any> {
50 return {
51 cacheName: data.cacheName,
52 updatedURL: data.request.url,
53 };
54}
55
56/**
57 * Uses the `postMessage()` API to inform any open windows/tabs when a cached
58 * response has been updated.
59 *
60 * For efficiency's sake, the underlying response bodies are not compared;
61 * only specific response headers are checked.
62 *
63 * @memberof workbox-broadcast-update
64 */
65class BroadcastCacheUpdate {
66 private readonly _headersToCheck: string[];
67 private readonly _generatePayload: (
68 options: CacheDidUpdateCallbackParam,
69 ) => Record<string, any>;
70 private readonly _notifyAllClients: boolean;
71
72 /**
73 * Construct a BroadcastCacheUpdate instance with a specific `channelName` to
74 * broadcast messages on
75 *
76 * @param {Object} [options]
77 * @param {Array<string>} [options.headersToCheck=['content-length', 'etag', 'last-modified']]
78 * A list of headers that will be used to determine whether the responses
79 * differ.
80 * @param {string} [options.generatePayload] A function whose return value
81 * will be used as the `payload` field in any cache update messages sent
82 * to the window clients.
83 * @param {boolean} [options.notifyAllClients=true] If true (the default) then
84 * all open clients will receive a message. If false, then only the client
85 * that make the original request will be notified of the update.
86 */
87 constructor({
88 generatePayload,
89 headersToCheck,
90 notifyAllClients,
91 }: BroadcastCacheUpdateOptions = {}) {
92 this._headersToCheck = headersToCheck || DEFAULT_HEADERS_TO_CHECK;
93 this._generatePayload = generatePayload || defaultPayloadGenerator;
94 this._notifyAllClients = notifyAllClients ?? NOTIFY_ALL_CLIENTS;
95 }
96
97 /**
98 * Compares two [Responses](https://developer.mozilla.org/en-US/docs/Web/API/Response)
99 * and sends a message (via `postMessage()`) to all window clients if the
100 * responses differ. Neither of the Responses can be
101 * [opaque](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
102 *
103 * The message that's posted has the following format (where `payload` can
104 * be customized via the `generatePayload` option the instance is created
105 * with):
106 *
107 * ```
108 * {
109 * type: 'CACHE_UPDATED',
110 * meta: 'workbox-broadcast-update',
111 * payload: {
112 * cacheName: 'the-cache-name',
113 * updatedURL: 'https://example.com/'
114 * }
115 * }
116 * ```
117 *
118 * @param {Object} options
119 * @param {Response} [options.oldResponse] Cached response to compare.
120 * @param {Response} options.newResponse Possibly updated response to compare.
121 * @param {Request} options.request The request.
122 * @param {string} options.cacheName Name of the cache the responses belong
123 * to. This is included in the broadcast message.
124 * @param {Event} options.event event The event that triggered
125 * this possible cache update.
126 * @return {Promise} Resolves once the update is sent.
127 */
128 async notifyIfUpdated(options: CacheDidUpdateCallbackParam): Promise<void> {
129 if (process.env.NODE_ENV !== 'production') {
130 assert!.isType(options.cacheName, 'string', {
131 moduleName: 'workbox-broadcast-update',
132 className: 'BroadcastCacheUpdate',
133 funcName: 'notifyIfUpdated',
134 paramName: 'cacheName',
135 });
136 assert!.isInstance(options.newResponse, Response, {
137 moduleName: 'workbox-broadcast-update',
138 className: 'BroadcastCacheUpdate',
139 funcName: 'notifyIfUpdated',
140 paramName: 'newResponse',
141 });
142 assert!.isInstance(options.request, Request, {
143 moduleName: 'workbox-broadcast-update',
144 className: 'BroadcastCacheUpdate',
145 funcName: 'notifyIfUpdated',
146 paramName: 'request',
147 });
148 }
149
150 // Without two responses there is nothing to compare.
151 if (!options.oldResponse) {
152 return;
153 }
154
155 if (
156 !responsesAreSame(
157 options.oldResponse,
158 options.newResponse,
159 this._headersToCheck,
160 )
161 ) {
162 if (process.env.NODE_ENV !== 'production') {
163 logger.log(
164 `Newer response found (and cached) for:`,
165 options.request.url,
166 );
167 }
168
169 const messageData = {
170 type: CACHE_UPDATED_MESSAGE_TYPE,
171 meta: CACHE_UPDATED_MESSAGE_META,
172 payload: this._generatePayload(options),
173 };
174
175 // For navigation requests, wait until the new window client exists
176 // before sending the message
177 if (options.request.mode === 'navigate') {
178 let resultingClientId: string | undefined;
179 if (options.event instanceof FetchEvent) {
180 resultingClientId = options.event.resultingClientId;
181 }
182
183 const resultingWin = await resultingClientExists(resultingClientId);
184
185 // Safari does not currently implement postMessage buffering and
186 // there's no good way to feature detect that, so to increase the
187 // chances of the message being delivered in Safari, we add a timeout.
188 // We also do this if `resultingClientExists()` didn't return a client,
189 // which means it timed out, so it's worth waiting a bit longer.
190 if (!resultingWin || isSafari) {
191 // 3500 is chosen because (according to CrUX data) 80% of mobile
192 // websites hit the DOMContentLoaded event in less than 3.5 seconds.
193 // And presumably sites implementing service worker are on the
194 // higher end of the performance spectrum.
195 await timeout(3500);
196 }
197 }
198
199 if (this._notifyAllClients) {
200 const windows = await self.clients.matchAll({type: 'window'});
201 for (const win of windows) {
202 win.postMessage(messageData);
203 }
204 } else {
205 // See https://github.com/GoogleChrome/workbox/issues/2895
206 if (options.event instanceof FetchEvent) {
207 const client = await self.clients.get(options.event.clientId);
208 client?.postMessage(messageData);
209 }
210 }
211 }
212 }
213}
214
215export {BroadcastCacheUpdate};
Note: See TracBrowser for help on using the repository browser.