spoko-design-system 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3391 @@
1
+ define(['exports'], (function (exports) { 'use strict';
2
+
3
+ // @ts-ignore
4
+ try {
5
+ self['workbox:core:7.0.0'] && _();
6
+ } catch (e) {}
7
+
8
+ /*
9
+ Copyright 2019 Google LLC
10
+
11
+ Use of this source code is governed by an MIT-style
12
+ license that can be found in the LICENSE file or at
13
+ https://opensource.org/licenses/MIT.
14
+ */
15
+ /**
16
+ * Claim any currently available clients once the service worker
17
+ * becomes active. This is normally used in conjunction with `skipWaiting()`.
18
+ *
19
+ * @memberof workbox-core
20
+ */
21
+ function clientsClaim() {
22
+ self.addEventListener('activate', () => self.clients.claim());
23
+ }
24
+
25
+ /*
26
+ Copyright 2019 Google LLC
27
+ Use of this source code is governed by an MIT-style
28
+ license that can be found in the LICENSE file or at
29
+ https://opensource.org/licenses/MIT.
30
+ */
31
+ const logger = (() => {
32
+ // Don't overwrite this value if it's already set.
33
+ // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923
34
+ if (!('__WB_DISABLE_DEV_LOGS' in globalThis)) {
35
+ self.__WB_DISABLE_DEV_LOGS = false;
36
+ }
37
+ let inGroup = false;
38
+ const methodToColorMap = {
39
+ debug: `#7f8c8d`,
40
+ log: `#2ecc71`,
41
+ warn: `#f39c12`,
42
+ error: `#c0392b`,
43
+ groupCollapsed: `#3498db`,
44
+ groupEnd: null // No colored prefix on groupEnd
45
+ };
46
+ const print = function (method, args) {
47
+ if (self.__WB_DISABLE_DEV_LOGS) {
48
+ return;
49
+ }
50
+ if (method === 'groupCollapsed') {
51
+ // Safari doesn't print all console.groupCollapsed() arguments:
52
+ // https://bugs.webkit.org/show_bug.cgi?id=182754
53
+ if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
54
+ console[method](...args);
55
+ return;
56
+ }
57
+ }
58
+ const styles = [`background: ${methodToColorMap[method]}`, `border-radius: 0.5em`, `color: white`, `font-weight: bold`, `padding: 2px 0.5em`];
59
+ // When in a group, the workbox prefix is not displayed.
60
+ const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];
61
+ console[method](...logPrefix, ...args);
62
+ if (method === 'groupCollapsed') {
63
+ inGroup = true;
64
+ }
65
+ if (method === 'groupEnd') {
66
+ inGroup = false;
67
+ }
68
+ };
69
+ // eslint-disable-next-line @typescript-eslint/ban-types
70
+ const api = {};
71
+ const loggerMethods = Object.keys(methodToColorMap);
72
+ for (const key of loggerMethods) {
73
+ const method = key;
74
+ api[method] = (...args) => {
75
+ print(method, args);
76
+ };
77
+ }
78
+ return api;
79
+ })();
80
+
81
+ /*
82
+ Copyright 2018 Google LLC
83
+
84
+ Use of this source code is governed by an MIT-style
85
+ license that can be found in the LICENSE file or at
86
+ https://opensource.org/licenses/MIT.
87
+ */
88
+ const messages = {
89
+ 'invalid-value': ({
90
+ paramName,
91
+ validValueDescription,
92
+ value
93
+ }) => {
94
+ if (!paramName || !validValueDescription) {
95
+ throw new Error(`Unexpected input to 'invalid-value' error.`);
96
+ }
97
+ return `The '${paramName}' parameter was given a value with an ` + `unexpected value. ${validValueDescription} Received a value of ` + `${JSON.stringify(value)}.`;
98
+ },
99
+ 'not-an-array': ({
100
+ moduleName,
101
+ className,
102
+ funcName,
103
+ paramName
104
+ }) => {
105
+ if (!moduleName || !className || !funcName || !paramName) {
106
+ throw new Error(`Unexpected input to 'not-an-array' error.`);
107
+ }
108
+ return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className}.${funcName}()' must be an array.`;
109
+ },
110
+ 'incorrect-type': ({
111
+ expectedType,
112
+ paramName,
113
+ moduleName,
114
+ className,
115
+ funcName
116
+ }) => {
117
+ if (!expectedType || !paramName || !moduleName || !funcName) {
118
+ throw new Error(`Unexpected input to 'incorrect-type' error.`);
119
+ }
120
+ const classNameStr = className ? `${className}.` : '';
121
+ return `The parameter '${paramName}' passed into ` + `'${moduleName}.${classNameStr}` + `${funcName}()' must be of type ${expectedType}.`;
122
+ },
123
+ 'incorrect-class': ({
124
+ expectedClassName,
125
+ paramName,
126
+ moduleName,
127
+ className,
128
+ funcName,
129
+ isReturnValueProblem
130
+ }) => {
131
+ if (!expectedClassName || !moduleName || !funcName) {
132
+ throw new Error(`Unexpected input to 'incorrect-class' error.`);
133
+ }
134
+ const classNameStr = className ? `${className}.` : '';
135
+ if (isReturnValueProblem) {
136
+ return `The return value from ` + `'${moduleName}.${classNameStr}${funcName}()' ` + `must be an instance of class ${expectedClassName}.`;
137
+ }
138
+ return `The parameter '${paramName}' passed into ` + `'${moduleName}.${classNameStr}${funcName}()' ` + `must be an instance of class ${expectedClassName}.`;
139
+ },
140
+ 'missing-a-method': ({
141
+ expectedMethod,
142
+ paramName,
143
+ moduleName,
144
+ className,
145
+ funcName
146
+ }) => {
147
+ if (!expectedMethod || !paramName || !moduleName || !className || !funcName) {
148
+ throw new Error(`Unexpected input to 'missing-a-method' error.`);
149
+ }
150
+ return `${moduleName}.${className}.${funcName}() expected the ` + `'${paramName}' parameter to expose a '${expectedMethod}' method.`;
151
+ },
152
+ 'add-to-cache-list-unexpected-type': ({
153
+ entry
154
+ }) => {
155
+ return `An unexpected entry was passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` + `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` + `strings with one or more characters, objects with a url property or ` + `Request objects.`;
156
+ },
157
+ 'add-to-cache-list-conflicting-entries': ({
158
+ firstEntry,
159
+ secondEntry
160
+ }) => {
161
+ if (!firstEntry || !secondEntry) {
162
+ throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`);
163
+ }
164
+ return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${firstEntry} but different revision details. Workbox is ` + `unable to cache and version the asset correctly. Please remove one ` + `of the entries.`;
165
+ },
166
+ 'plugin-error-request-will-fetch': ({
167
+ thrownErrorMessage
168
+ }) => {
169
+ if (!thrownErrorMessage) {
170
+ throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`);
171
+ }
172
+ return `An error was thrown by a plugins 'requestWillFetch()' method. ` + `The thrown error message was: '${thrownErrorMessage}'.`;
173
+ },
174
+ 'invalid-cache-name': ({
175
+ cacheNameId,
176
+ value
177
+ }) => {
178
+ if (!cacheNameId) {
179
+ throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`);
180
+ }
181
+ return `You must provide a name containing at least one character for ` + `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` + `'${JSON.stringify(value)}'`;
182
+ },
183
+ 'unregister-route-but-not-found-with-method': ({
184
+ method
185
+ }) => {
186
+ if (!method) {
187
+ throw new Error(`Unexpected input to ` + `'unregister-route-but-not-found-with-method' error.`);
188
+ }
189
+ return `The route you're trying to unregister was not previously ` + `registered for the method type '${method}'.`;
190
+ },
191
+ 'unregister-route-route-not-registered': () => {
192
+ return `The route you're trying to unregister was not previously ` + `registered.`;
193
+ },
194
+ 'queue-replay-failed': ({
195
+ name
196
+ }) => {
197
+ return `Replaying the background sync queue '${name}' failed.`;
198
+ },
199
+ 'duplicate-queue-name': ({
200
+ name
201
+ }) => {
202
+ return `The Queue name '${name}' is already being used. ` + `All instances of backgroundSync.Queue must be given unique names.`;
203
+ },
204
+ 'expired-test-without-max-age': ({
205
+ methodName,
206
+ paramName
207
+ }) => {
208
+ return `The '${methodName}()' method can only be used when the ` + `'${paramName}' is used in the constructor.`;
209
+ },
210
+ 'unsupported-route-type': ({
211
+ moduleName,
212
+ className,
213
+ funcName,
214
+ paramName
215
+ }) => {
216
+ return `The supplied '${paramName}' parameter was an unsupported type. ` + `Please check the docs for ${moduleName}.${className}.${funcName} for ` + `valid input types.`;
217
+ },
218
+ 'not-array-of-class': ({
219
+ value,
220
+ expectedClass,
221
+ moduleName,
222
+ className,
223
+ funcName,
224
+ paramName
225
+ }) => {
226
+ return `The supplied '${paramName}' parameter must be an array of ` + `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` + `Please check the call to ${moduleName}.${className}.${funcName}() ` + `to fix the issue.`;
227
+ },
228
+ 'max-entries-or-age-required': ({
229
+ moduleName,
230
+ className,
231
+ funcName
232
+ }) => {
233
+ return `You must define either config.maxEntries or config.maxAgeSeconds` + `in ${moduleName}.${className}.${funcName}`;
234
+ },
235
+ 'statuses-or-headers-required': ({
236
+ moduleName,
237
+ className,
238
+ funcName
239
+ }) => {
240
+ return `You must define either config.statuses or config.headers` + `in ${moduleName}.${className}.${funcName}`;
241
+ },
242
+ 'invalid-string': ({
243
+ moduleName,
244
+ funcName,
245
+ paramName
246
+ }) => {
247
+ if (!paramName || !moduleName || !funcName) {
248
+ throw new Error(`Unexpected input to 'invalid-string' error.`);
249
+ }
250
+ return `When using strings, the '${paramName}' parameter must start with ` + `'http' (for cross-origin matches) or '/' (for same-origin matches). ` + `Please see the docs for ${moduleName}.${funcName}() for ` + `more info.`;
251
+ },
252
+ 'channel-name-required': () => {
253
+ return `You must provide a channelName to construct a ` + `BroadcastCacheUpdate instance.`;
254
+ },
255
+ 'invalid-responses-are-same-args': () => {
256
+ return `The arguments passed into responsesAreSame() appear to be ` + `invalid. Please ensure valid Responses are used.`;
257
+ },
258
+ 'expire-custom-caches-only': () => {
259
+ return `You must provide a 'cacheName' property when using the ` + `expiration plugin with a runtime caching strategy.`;
260
+ },
261
+ 'unit-must-be-bytes': ({
262
+ normalizedRangeHeader
263
+ }) => {
264
+ if (!normalizedRangeHeader) {
265
+ throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`);
266
+ }
267
+ return `The 'unit' portion of the Range header must be set to 'bytes'. ` + `The Range header provided was "${normalizedRangeHeader}"`;
268
+ },
269
+ 'single-range-only': ({
270
+ normalizedRangeHeader
271
+ }) => {
272
+ if (!normalizedRangeHeader) {
273
+ throw new Error(`Unexpected input to 'single-range-only' error.`);
274
+ }
275
+ return `Multiple ranges are not supported. Please use a single start ` + `value, and optional end value. The Range header provided was ` + `"${normalizedRangeHeader}"`;
276
+ },
277
+ 'invalid-range-values': ({
278
+ normalizedRangeHeader
279
+ }) => {
280
+ if (!normalizedRangeHeader) {
281
+ throw new Error(`Unexpected input to 'invalid-range-values' error.`);
282
+ }
283
+ return `The Range header is missing both start and end values. At least ` + `one of those values is needed. The Range header provided was ` + `"${normalizedRangeHeader}"`;
284
+ },
285
+ 'no-range-header': () => {
286
+ return `No Range header was found in the Request provided.`;
287
+ },
288
+ 'range-not-satisfiable': ({
289
+ size,
290
+ start,
291
+ end
292
+ }) => {
293
+ return `The start (${start}) and end (${end}) values in the Range are ` + `not satisfiable by the cached response, which is ${size} bytes.`;
294
+ },
295
+ 'attempt-to-cache-non-get-request': ({
296
+ url,
297
+ method
298
+ }) => {
299
+ return `Unable to cache '${url}' because it is a '${method}' request and ` + `only 'GET' requests can be cached.`;
300
+ },
301
+ 'cache-put-with-no-response': ({
302
+ url
303
+ }) => {
304
+ return `There was an attempt to cache '${url}' but the response was not ` + `defined.`;
305
+ },
306
+ 'no-response': ({
307
+ url,
308
+ error
309
+ }) => {
310
+ let message = `The strategy could not generate a response for '${url}'.`;
311
+ if (error) {
312
+ message += ` The underlying error is ${error}.`;
313
+ }
314
+ return message;
315
+ },
316
+ 'bad-precaching-response': ({
317
+ url,
318
+ status
319
+ }) => {
320
+ return `The precaching request for '${url}' failed` + (status ? ` with an HTTP status of ${status}.` : `.`);
321
+ },
322
+ 'non-precached-url': ({
323
+ url
324
+ }) => {
325
+ return `createHandlerBoundToURL('${url}') was called, but that URL is ` + `not precached. Please pass in a URL that is precached instead.`;
326
+ },
327
+ 'add-to-cache-list-conflicting-integrities': ({
328
+ url
329
+ }) => {
330
+ return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${url} with different integrity values. Please remove one of them.`;
331
+ },
332
+ 'missing-precache-entry': ({
333
+ cacheName,
334
+ url
335
+ }) => {
336
+ return `Unable to find a precached response in ${cacheName} for ${url}.`;
337
+ },
338
+ 'cross-origin-copy-response': ({
339
+ origin
340
+ }) => {
341
+ return `workbox-core.copyResponse() can only be used with same-origin ` + `responses. It was passed a response with origin ${origin}.`;
342
+ },
343
+ 'opaque-streams-source': ({
344
+ type
345
+ }) => {
346
+ const message = `One of the workbox-streams sources resulted in an ` + `'${type}' response.`;
347
+ if (type === 'opaqueredirect') {
348
+ return `${message} Please do not use a navigation request that results ` + `in a redirect as a source.`;
349
+ }
350
+ return `${message} Please ensure your sources are CORS-enabled.`;
351
+ }
352
+ };
353
+
354
+ /*
355
+ Copyright 2018 Google LLC
356
+
357
+ Use of this source code is governed by an MIT-style
358
+ license that can be found in the LICENSE file or at
359
+ https://opensource.org/licenses/MIT.
360
+ */
361
+ const generatorFunction = (code, details = {}) => {
362
+ const message = messages[code];
363
+ if (!message) {
364
+ throw new Error(`Unable to find message for code '${code}'.`);
365
+ }
366
+ return message(details);
367
+ };
368
+ const messageGenerator = generatorFunction;
369
+
370
+ /*
371
+ Copyright 2018 Google LLC
372
+
373
+ Use of this source code is governed by an MIT-style
374
+ license that can be found in the LICENSE file or at
375
+ https://opensource.org/licenses/MIT.
376
+ */
377
+ /**
378
+ * Workbox errors should be thrown with this class.
379
+ * This allows use to ensure the type easily in tests,
380
+ * helps developers identify errors from workbox
381
+ * easily and allows use to optimise error
382
+ * messages correctly.
383
+ *
384
+ * @private
385
+ */
386
+ class WorkboxError extends Error {
387
+ /**
388
+ *
389
+ * @param {string} errorCode The error code that
390
+ * identifies this particular error.
391
+ * @param {Object=} details Any relevant arguments
392
+ * that will help developers identify issues should
393
+ * be added as a key on the context object.
394
+ */
395
+ constructor(errorCode, details) {
396
+ const message = messageGenerator(errorCode, details);
397
+ super(message);
398
+ this.name = errorCode;
399
+ this.details = details;
400
+ }
401
+ }
402
+
403
+ /*
404
+ Copyright 2018 Google LLC
405
+
406
+ Use of this source code is governed by an MIT-style
407
+ license that can be found in the LICENSE file or at
408
+ https://opensource.org/licenses/MIT.
409
+ */
410
+ /*
411
+ * This method throws if the supplied value is not an array.
412
+ * The destructed values are required to produce a meaningful error for users.
413
+ * The destructed and restructured object is so it's clear what is
414
+ * needed.
415
+ */
416
+ const isArray = (value, details) => {
417
+ if (!Array.isArray(value)) {
418
+ throw new WorkboxError('not-an-array', details);
419
+ }
420
+ };
421
+ const hasMethod = (object, expectedMethod, details) => {
422
+ const type = typeof object[expectedMethod];
423
+ if (type !== 'function') {
424
+ details['expectedMethod'] = expectedMethod;
425
+ throw new WorkboxError('missing-a-method', details);
426
+ }
427
+ };
428
+ const isType = (object, expectedType, details) => {
429
+ if (typeof object !== expectedType) {
430
+ details['expectedType'] = expectedType;
431
+ throw new WorkboxError('incorrect-type', details);
432
+ }
433
+ };
434
+ const isInstance = (object,
435
+ // Need the general type to do the check later.
436
+ // eslint-disable-next-line @typescript-eslint/ban-types
437
+ expectedClass, details) => {
438
+ if (!(object instanceof expectedClass)) {
439
+ details['expectedClassName'] = expectedClass.name;
440
+ throw new WorkboxError('incorrect-class', details);
441
+ }
442
+ };
443
+ const isOneOf = (value, validValues, details) => {
444
+ if (!validValues.includes(value)) {
445
+ details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`;
446
+ throw new WorkboxError('invalid-value', details);
447
+ }
448
+ };
449
+ const isArrayOfClass = (value,
450
+ // Need general type to do check later.
451
+ expectedClass,
452
+ // eslint-disable-line
453
+ details) => {
454
+ const error = new WorkboxError('not-array-of-class', details);
455
+ if (!Array.isArray(value)) {
456
+ throw error;
457
+ }
458
+ for (const item of value) {
459
+ if (!(item instanceof expectedClass)) {
460
+ throw error;
461
+ }
462
+ }
463
+ };
464
+ const finalAssertExports = {
465
+ hasMethod,
466
+ isArray,
467
+ isInstance,
468
+ isOneOf,
469
+ isType,
470
+ isArrayOfClass
471
+ };
472
+
473
+ // @ts-ignore
474
+ try {
475
+ self['workbox:routing:7.0.0'] && _();
476
+ } catch (e) {}
477
+
478
+ /*
479
+ Copyright 2018 Google LLC
480
+
481
+ Use of this source code is governed by an MIT-style
482
+ license that can be found in the LICENSE file or at
483
+ https://opensource.org/licenses/MIT.
484
+ */
485
+ /**
486
+ * The default HTTP method, 'GET', used when there's no specific method
487
+ * configured for a route.
488
+ *
489
+ * @type {string}
490
+ *
491
+ * @private
492
+ */
493
+ const defaultMethod = 'GET';
494
+ /**
495
+ * The list of valid HTTP methods associated with requests that could be routed.
496
+ *
497
+ * @type {Array<string>}
498
+ *
499
+ * @private
500
+ */
501
+ const validMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT'];
502
+
503
+ /*
504
+ Copyright 2018 Google LLC
505
+
506
+ Use of this source code is governed by an MIT-style
507
+ license that can be found in the LICENSE file or at
508
+ https://opensource.org/licenses/MIT.
509
+ */
510
+ /**
511
+ * @param {function()|Object} handler Either a function, or an object with a
512
+ * 'handle' method.
513
+ * @return {Object} An object with a handle method.
514
+ *
515
+ * @private
516
+ */
517
+ const normalizeHandler = handler => {
518
+ if (handler && typeof handler === 'object') {
519
+ {
520
+ finalAssertExports.hasMethod(handler, 'handle', {
521
+ moduleName: 'workbox-routing',
522
+ className: 'Route',
523
+ funcName: 'constructor',
524
+ paramName: 'handler'
525
+ });
526
+ }
527
+ return handler;
528
+ } else {
529
+ {
530
+ finalAssertExports.isType(handler, 'function', {
531
+ moduleName: 'workbox-routing',
532
+ className: 'Route',
533
+ funcName: 'constructor',
534
+ paramName: 'handler'
535
+ });
536
+ }
537
+ return {
538
+ handle: handler
539
+ };
540
+ }
541
+ };
542
+
543
+ /*
544
+ Copyright 2018 Google LLC
545
+
546
+ Use of this source code is governed by an MIT-style
547
+ license that can be found in the LICENSE file or at
548
+ https://opensource.org/licenses/MIT.
549
+ */
550
+ /**
551
+ * A `Route` consists of a pair of callback functions, "match" and "handler".
552
+ * The "match" callback determine if a route should be used to "handle" a
553
+ * request by returning a non-falsy value if it can. The "handler" callback
554
+ * is called when there is a match and should return a Promise that resolves
555
+ * to a `Response`.
556
+ *
557
+ * @memberof workbox-routing
558
+ */
559
+ class Route {
560
+ /**
561
+ * Constructor for Route class.
562
+ *
563
+ * @param {workbox-routing~matchCallback} match
564
+ * A callback function that determines whether the route matches a given
565
+ * `fetch` event by returning a non-falsy value.
566
+ * @param {workbox-routing~handlerCallback} handler A callback
567
+ * function that returns a Promise resolving to a Response.
568
+ * @param {string} [method='GET'] The HTTP method to match the Route
569
+ * against.
570
+ */
571
+ constructor(match, handler, method = defaultMethod) {
572
+ {
573
+ finalAssertExports.isType(match, 'function', {
574
+ moduleName: 'workbox-routing',
575
+ className: 'Route',
576
+ funcName: 'constructor',
577
+ paramName: 'match'
578
+ });
579
+ if (method) {
580
+ finalAssertExports.isOneOf(method, validMethods, {
581
+ paramName: 'method'
582
+ });
583
+ }
584
+ }
585
+ // These values are referenced directly by Router so cannot be
586
+ // altered by minificaton.
587
+ this.handler = normalizeHandler(handler);
588
+ this.match = match;
589
+ this.method = method;
590
+ }
591
+ /**
592
+ *
593
+ * @param {workbox-routing-handlerCallback} handler A callback
594
+ * function that returns a Promise resolving to a Response
595
+ */
596
+ setCatchHandler(handler) {
597
+ this.catchHandler = normalizeHandler(handler);
598
+ }
599
+ }
600
+
601
+ /*
602
+ Copyright 2018 Google LLC
603
+
604
+ Use of this source code is governed by an MIT-style
605
+ license that can be found in the LICENSE file or at
606
+ https://opensource.org/licenses/MIT.
607
+ */
608
+ /**
609
+ * RegExpRoute makes it easy to create a regular expression based
610
+ * {@link workbox-routing.Route}.
611
+ *
612
+ * For same-origin requests the RegExp only needs to match part of the URL. For
613
+ * requests against third-party servers, you must define a RegExp that matches
614
+ * the start of the URL.
615
+ *
616
+ * @memberof workbox-routing
617
+ * @extends workbox-routing.Route
618
+ */
619
+ class RegExpRoute extends Route {
620
+ /**
621
+ * If the regular expression contains
622
+ * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references},
623
+ * the captured values will be passed to the
624
+ * {@link workbox-routing~handlerCallback} `params`
625
+ * argument.
626
+ *
627
+ * @param {RegExp} regExp The regular expression to match against URLs.
628
+ * @param {workbox-routing~handlerCallback} handler A callback
629
+ * function that returns a Promise resulting in a Response.
630
+ * @param {string} [method='GET'] The HTTP method to match the Route
631
+ * against.
632
+ */
633
+ constructor(regExp, handler, method) {
634
+ {
635
+ finalAssertExports.isInstance(regExp, RegExp, {
636
+ moduleName: 'workbox-routing',
637
+ className: 'RegExpRoute',
638
+ funcName: 'constructor',
639
+ paramName: 'pattern'
640
+ });
641
+ }
642
+ const match = ({
643
+ url
644
+ }) => {
645
+ const result = regExp.exec(url.href);
646
+ // Return immediately if there's no match.
647
+ if (!result) {
648
+ return;
649
+ }
650
+ // Require that the match start at the first character in the URL string
651
+ // if it's a cross-origin request.
652
+ // See https://github.com/GoogleChrome/workbox/issues/281 for the context
653
+ // behind this behavior.
654
+ if (url.origin !== location.origin && result.index !== 0) {
655
+ {
656
+ logger.debug(`The regular expression '${regExp.toString()}' only partially matched ` + `against the cross-origin URL '${url.toString()}'. RegExpRoute's will only ` + `handle cross-origin requests if they match the entire URL.`);
657
+ }
658
+ return;
659
+ }
660
+ // If the route matches, but there aren't any capture groups defined, then
661
+ // this will return [], which is truthy and therefore sufficient to
662
+ // indicate a match.
663
+ // If there are capture groups, then it will return their values.
664
+ return result.slice(1);
665
+ };
666
+ super(match, handler, method);
667
+ }
668
+ }
669
+
670
+ /*
671
+ Copyright 2018 Google LLC
672
+
673
+ Use of this source code is governed by an MIT-style
674
+ license that can be found in the LICENSE file or at
675
+ https://opensource.org/licenses/MIT.
676
+ */
677
+ const getFriendlyURL = url => {
678
+ const urlObj = new URL(String(url), location.href);
679
+ // See https://github.com/GoogleChrome/workbox/issues/2323
680
+ // We want to include everything, except for the origin if it's same-origin.
681
+ return urlObj.href.replace(new RegExp(`^${location.origin}`), '');
682
+ };
683
+
684
+ /*
685
+ Copyright 2018 Google LLC
686
+
687
+ Use of this source code is governed by an MIT-style
688
+ license that can be found in the LICENSE file or at
689
+ https://opensource.org/licenses/MIT.
690
+ */
691
+ /**
692
+ * The Router can be used to process a `FetchEvent` using one or more
693
+ * {@link workbox-routing.Route}, responding with a `Response` if
694
+ * a matching route exists.
695
+ *
696
+ * If no route matches a given a request, the Router will use a "default"
697
+ * handler if one is defined.
698
+ *
699
+ * Should the matching Route throw an error, the Router will use a "catch"
700
+ * handler if one is defined to gracefully deal with issues and respond with a
701
+ * Request.
702
+ *
703
+ * If a request matches multiple routes, the **earliest** registered route will
704
+ * be used to respond to the request.
705
+ *
706
+ * @memberof workbox-routing
707
+ */
708
+ class Router {
709
+ /**
710
+ * Initializes a new Router.
711
+ */
712
+ constructor() {
713
+ this._routes = new Map();
714
+ this._defaultHandlerMap = new Map();
715
+ }
716
+ /**
717
+ * @return {Map<string, Array<workbox-routing.Route>>} routes A `Map` of HTTP
718
+ * method name ('GET', etc.) to an array of all the corresponding `Route`
719
+ * instances that are registered.
720
+ */
721
+ get routes() {
722
+ return this._routes;
723
+ }
724
+ /**
725
+ * Adds a fetch event listener to respond to events when a route matches
726
+ * the event's request.
727
+ */
728
+ addFetchListener() {
729
+ // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
730
+ self.addEventListener('fetch', event => {
731
+ const {
732
+ request
733
+ } = event;
734
+ const responsePromise = this.handleRequest({
735
+ request,
736
+ event
737
+ });
738
+ if (responsePromise) {
739
+ event.respondWith(responsePromise);
740
+ }
741
+ });
742
+ }
743
+ /**
744
+ * Adds a message event listener for URLs to cache from the window.
745
+ * This is useful to cache resources loaded on the page prior to when the
746
+ * service worker started controlling it.
747
+ *
748
+ * The format of the message data sent from the window should be as follows.
749
+ * Where the `urlsToCache` array may consist of URL strings or an array of
750
+ * URL string + `requestInit` object (the same as you'd pass to `fetch()`).
751
+ *
752
+ * ```
753
+ * {
754
+ * type: 'CACHE_URLS',
755
+ * payload: {
756
+ * urlsToCache: [
757
+ * './script1.js',
758
+ * './script2.js',
759
+ * ['./script3.js', {mode: 'no-cors'}],
760
+ * ],
761
+ * },
762
+ * }
763
+ * ```
764
+ */
765
+ addCacheListener() {
766
+ // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
767
+ self.addEventListener('message', event => {
768
+ // event.data is type 'any'
769
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
770
+ if (event.data && event.data.type === 'CACHE_URLS') {
771
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
772
+ const {
773
+ payload
774
+ } = event.data;
775
+ {
776
+ logger.debug(`Caching URLs from the window`, payload.urlsToCache);
777
+ }
778
+ const requestPromises = Promise.all(payload.urlsToCache.map(entry => {
779
+ if (typeof entry === 'string') {
780
+ entry = [entry];
781
+ }
782
+ const request = new Request(...entry);
783
+ return this.handleRequest({
784
+ request,
785
+ event
786
+ });
787
+ // TODO(philipwalton): TypeScript errors without this typecast for
788
+ // some reason (probably a bug). The real type here should work but
789
+ // doesn't: `Array<Promise<Response> | undefined>`.
790
+ })); // TypeScript
791
+ event.waitUntil(requestPromises);
792
+ // If a MessageChannel was used, reply to the message on success.
793
+ if (event.ports && event.ports[0]) {
794
+ void requestPromises.then(() => event.ports[0].postMessage(true));
795
+ }
796
+ }
797
+ });
798
+ }
799
+ /**
800
+ * Apply the routing rules to a FetchEvent object to get a Response from an
801
+ * appropriate Route's handler.
802
+ *
803
+ * @param {Object} options
804
+ * @param {Request} options.request The request to handle.
805
+ * @param {ExtendableEvent} options.event The event that triggered the
806
+ * request.
807
+ * @return {Promise<Response>|undefined} A promise is returned if a
808
+ * registered route can handle the request. If there is no matching
809
+ * route and there's no `defaultHandler`, `undefined` is returned.
810
+ */
811
+ handleRequest({
812
+ request,
813
+ event
814
+ }) {
815
+ {
816
+ finalAssertExports.isInstance(request, Request, {
817
+ moduleName: 'workbox-routing',
818
+ className: 'Router',
819
+ funcName: 'handleRequest',
820
+ paramName: 'options.request'
821
+ });
822
+ }
823
+ const url = new URL(request.url, location.href);
824
+ if (!url.protocol.startsWith('http')) {
825
+ {
826
+ logger.debug(`Workbox Router only supports URLs that start with 'http'.`);
827
+ }
828
+ return;
829
+ }
830
+ const sameOrigin = url.origin === location.origin;
831
+ const {
832
+ params,
833
+ route
834
+ } = this.findMatchingRoute({
835
+ event,
836
+ request,
837
+ sameOrigin,
838
+ url
839
+ });
840
+ let handler = route && route.handler;
841
+ const debugMessages = [];
842
+ {
843
+ if (handler) {
844
+ debugMessages.push([`Found a route to handle this request:`, route]);
845
+ if (params) {
846
+ debugMessages.push([`Passing the following params to the route's handler:`, params]);
847
+ }
848
+ }
849
+ }
850
+ // If we don't have a handler because there was no matching route, then
851
+ // fall back to defaultHandler if that's defined.
852
+ const method = request.method;
853
+ if (!handler && this._defaultHandlerMap.has(method)) {
854
+ {
855
+ debugMessages.push(`Failed to find a matching route. Falling ` + `back to the default handler for ${method}.`);
856
+ }
857
+ handler = this._defaultHandlerMap.get(method);
858
+ }
859
+ if (!handler) {
860
+ {
861
+ // No handler so Workbox will do nothing. If logs is set of debug
862
+ // i.e. verbose, we should print out this information.
863
+ logger.debug(`No route found for: ${getFriendlyURL(url)}`);
864
+ }
865
+ return;
866
+ }
867
+ {
868
+ // We have a handler, meaning Workbox is going to handle the route.
869
+ // print the routing details to the console.
870
+ logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`);
871
+ debugMessages.forEach(msg => {
872
+ if (Array.isArray(msg)) {
873
+ logger.log(...msg);
874
+ } else {
875
+ logger.log(msg);
876
+ }
877
+ });
878
+ logger.groupEnd();
879
+ }
880
+ // Wrap in try and catch in case the handle method throws a synchronous
881
+ // error. It should still callback to the catch handler.
882
+ let responsePromise;
883
+ try {
884
+ responsePromise = handler.handle({
885
+ url,
886
+ request,
887
+ event,
888
+ params
889
+ });
890
+ } catch (err) {
891
+ responsePromise = Promise.reject(err);
892
+ }
893
+ // Get route's catch handler, if it exists
894
+ const catchHandler = route && route.catchHandler;
895
+ if (responsePromise instanceof Promise && (this._catchHandler || catchHandler)) {
896
+ responsePromise = responsePromise.catch(async err => {
897
+ // If there's a route catch handler, process that first
898
+ if (catchHandler) {
899
+ {
900
+ // Still include URL here as it will be async from the console group
901
+ // and may not make sense without the URL
902
+ logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`);
903
+ logger.error(`Error thrown by:`, route);
904
+ logger.error(err);
905
+ logger.groupEnd();
906
+ }
907
+ try {
908
+ return await catchHandler.handle({
909
+ url,
910
+ request,
911
+ event,
912
+ params
913
+ });
914
+ } catch (catchErr) {
915
+ if (catchErr instanceof Error) {
916
+ err = catchErr;
917
+ }
918
+ }
919
+ }
920
+ if (this._catchHandler) {
921
+ {
922
+ // Still include URL here as it will be async from the console group
923
+ // and may not make sense without the URL
924
+ logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to global Catch Handler.`);
925
+ logger.error(`Error thrown by:`, route);
926
+ logger.error(err);
927
+ logger.groupEnd();
928
+ }
929
+ return this._catchHandler.handle({
930
+ url,
931
+ request,
932
+ event
933
+ });
934
+ }
935
+ throw err;
936
+ });
937
+ }
938
+ return responsePromise;
939
+ }
940
+ /**
941
+ * Checks a request and URL (and optionally an event) against the list of
942
+ * registered routes, and if there's a match, returns the corresponding
943
+ * route along with any params generated by the match.
944
+ *
945
+ * @param {Object} options
946
+ * @param {URL} options.url
947
+ * @param {boolean} options.sameOrigin The result of comparing `url.origin`
948
+ * against the current origin.
949
+ * @param {Request} options.request The request to match.
950
+ * @param {Event} options.event The corresponding event.
951
+ * @return {Object} An object with `route` and `params` properties.
952
+ * They are populated if a matching route was found or `undefined`
953
+ * otherwise.
954
+ */
955
+ findMatchingRoute({
956
+ url,
957
+ sameOrigin,
958
+ request,
959
+ event
960
+ }) {
961
+ const routes = this._routes.get(request.method) || [];
962
+ for (const route of routes) {
963
+ let params;
964
+ // route.match returns type any, not possible to change right now.
965
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
966
+ const matchResult = route.match({
967
+ url,
968
+ sameOrigin,
969
+ request,
970
+ event
971
+ });
972
+ if (matchResult) {
973
+ {
974
+ // Warn developers that using an async matchCallback is almost always
975
+ // not the right thing to do.
976
+ if (matchResult instanceof Promise) {
977
+ logger.warn(`While routing ${getFriendlyURL(url)}, an async ` + `matchCallback function was used. Please convert the ` + `following route to use a synchronous matchCallback function:`, route);
978
+ }
979
+ }
980
+ // See https://github.com/GoogleChrome/workbox/issues/2079
981
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
982
+ params = matchResult;
983
+ if (Array.isArray(params) && params.length === 0) {
984
+ // Instead of passing an empty array in as params, use undefined.
985
+ params = undefined;
986
+ } else if (matchResult.constructor === Object &&
987
+ // eslint-disable-line
988
+ Object.keys(matchResult).length === 0) {
989
+ // Instead of passing an empty object in as params, use undefined.
990
+ params = undefined;
991
+ } else if (typeof matchResult === 'boolean') {
992
+ // For the boolean value true (rather than just something truth-y),
993
+ // don't set params.
994
+ // See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353
995
+ params = undefined;
996
+ }
997
+ // Return early if have a match.
998
+ return {
999
+ route,
1000
+ params
1001
+ };
1002
+ }
1003
+ }
1004
+ // If no match was found above, return and empty object.
1005
+ return {};
1006
+ }
1007
+ /**
1008
+ * Define a default `handler` that's called when no routes explicitly
1009
+ * match the incoming request.
1010
+ *
1011
+ * Each HTTP method ('GET', 'POST', etc.) gets its own default handler.
1012
+ *
1013
+ * Without a default handler, unmatched requests will go against the
1014
+ * network as if there were no service worker present.
1015
+ *
1016
+ * @param {workbox-routing~handlerCallback} handler A callback
1017
+ * function that returns a Promise resulting in a Response.
1018
+ * @param {string} [method='GET'] The HTTP method to associate with this
1019
+ * default handler. Each method has its own default.
1020
+ */
1021
+ setDefaultHandler(handler, method = defaultMethod) {
1022
+ this._defaultHandlerMap.set(method, normalizeHandler(handler));
1023
+ }
1024
+ /**
1025
+ * If a Route throws an error while handling a request, this `handler`
1026
+ * will be called and given a chance to provide a response.
1027
+ *
1028
+ * @param {workbox-routing~handlerCallback} handler A callback
1029
+ * function that returns a Promise resulting in a Response.
1030
+ */
1031
+ setCatchHandler(handler) {
1032
+ this._catchHandler = normalizeHandler(handler);
1033
+ }
1034
+ /**
1035
+ * Registers a route with the router.
1036
+ *
1037
+ * @param {workbox-routing.Route} route The route to register.
1038
+ */
1039
+ registerRoute(route) {
1040
+ {
1041
+ finalAssertExports.isType(route, 'object', {
1042
+ moduleName: 'workbox-routing',
1043
+ className: 'Router',
1044
+ funcName: 'registerRoute',
1045
+ paramName: 'route'
1046
+ });
1047
+ finalAssertExports.hasMethod(route, 'match', {
1048
+ moduleName: 'workbox-routing',
1049
+ className: 'Router',
1050
+ funcName: 'registerRoute',
1051
+ paramName: 'route'
1052
+ });
1053
+ finalAssertExports.isType(route.handler, 'object', {
1054
+ moduleName: 'workbox-routing',
1055
+ className: 'Router',
1056
+ funcName: 'registerRoute',
1057
+ paramName: 'route'
1058
+ });
1059
+ finalAssertExports.hasMethod(route.handler, 'handle', {
1060
+ moduleName: 'workbox-routing',
1061
+ className: 'Router',
1062
+ funcName: 'registerRoute',
1063
+ paramName: 'route.handler'
1064
+ });
1065
+ finalAssertExports.isType(route.method, 'string', {
1066
+ moduleName: 'workbox-routing',
1067
+ className: 'Router',
1068
+ funcName: 'registerRoute',
1069
+ paramName: 'route.method'
1070
+ });
1071
+ }
1072
+ if (!this._routes.has(route.method)) {
1073
+ this._routes.set(route.method, []);
1074
+ }
1075
+ // Give precedence to all of the earlier routes by adding this additional
1076
+ // route to the end of the array.
1077
+ this._routes.get(route.method).push(route);
1078
+ }
1079
+ /**
1080
+ * Unregisters a route with the router.
1081
+ *
1082
+ * @param {workbox-routing.Route} route The route to unregister.
1083
+ */
1084
+ unregisterRoute(route) {
1085
+ if (!this._routes.has(route.method)) {
1086
+ throw new WorkboxError('unregister-route-but-not-found-with-method', {
1087
+ method: route.method
1088
+ });
1089
+ }
1090
+ const routeIndex = this._routes.get(route.method).indexOf(route);
1091
+ if (routeIndex > -1) {
1092
+ this._routes.get(route.method).splice(routeIndex, 1);
1093
+ } else {
1094
+ throw new WorkboxError('unregister-route-route-not-registered');
1095
+ }
1096
+ }
1097
+ }
1098
+
1099
+ /*
1100
+ Copyright 2019 Google LLC
1101
+
1102
+ Use of this source code is governed by an MIT-style
1103
+ license that can be found in the LICENSE file or at
1104
+ https://opensource.org/licenses/MIT.
1105
+ */
1106
+ let defaultRouter;
1107
+ /**
1108
+ * Creates a new, singleton Router instance if one does not exist. If one
1109
+ * does already exist, that instance is returned.
1110
+ *
1111
+ * @private
1112
+ * @return {Router}
1113
+ */
1114
+ const getOrCreateDefaultRouter = () => {
1115
+ if (!defaultRouter) {
1116
+ defaultRouter = new Router();
1117
+ // The helpers that use the default Router assume these listeners exist.
1118
+ defaultRouter.addFetchListener();
1119
+ defaultRouter.addCacheListener();
1120
+ }
1121
+ return defaultRouter;
1122
+ };
1123
+
1124
+ /*
1125
+ Copyright 2019 Google LLC
1126
+
1127
+ Use of this source code is governed by an MIT-style
1128
+ license that can be found in the LICENSE file or at
1129
+ https://opensource.org/licenses/MIT.
1130
+ */
1131
+ /**
1132
+ * Easily register a RegExp, string, or function with a caching
1133
+ * strategy to a singleton Router instance.
1134
+ *
1135
+ * This method will generate a Route for you if needed and
1136
+ * call {@link workbox-routing.Router#registerRoute}.
1137
+ *
1138
+ * @param {RegExp|string|workbox-routing.Route~matchCallback|workbox-routing.Route} capture
1139
+ * If the capture param is a `Route`, all other arguments will be ignored.
1140
+ * @param {workbox-routing~handlerCallback} [handler] A callback
1141
+ * function that returns a Promise resulting in a Response. This parameter
1142
+ * is required if `capture` is not a `Route` object.
1143
+ * @param {string} [method='GET'] The HTTP method to match the Route
1144
+ * against.
1145
+ * @return {workbox-routing.Route} The generated `Route`.
1146
+ *
1147
+ * @memberof workbox-routing
1148
+ */
1149
+ function registerRoute(capture, handler, method) {
1150
+ let route;
1151
+ if (typeof capture === 'string') {
1152
+ const captureUrl = new URL(capture, location.href);
1153
+ {
1154
+ if (!(capture.startsWith('/') || capture.startsWith('http'))) {
1155
+ throw new WorkboxError('invalid-string', {
1156
+ moduleName: 'workbox-routing',
1157
+ funcName: 'registerRoute',
1158
+ paramName: 'capture'
1159
+ });
1160
+ }
1161
+ // We want to check if Express-style wildcards are in the pathname only.
1162
+ // TODO: Remove this log message in v4.
1163
+ const valueToCheck = capture.startsWith('http') ? captureUrl.pathname : capture;
1164
+ // See https://github.com/pillarjs/path-to-regexp#parameters
1165
+ const wildcards = '[*:?+]';
1166
+ if (new RegExp(`${wildcards}`).exec(valueToCheck)) {
1167
+ logger.debug(`The '$capture' parameter contains an Express-style wildcard ` + `character (${wildcards}). Strings are now always interpreted as ` + `exact matches; use a RegExp for partial or wildcard matches.`);
1168
+ }
1169
+ }
1170
+ const matchCallback = ({
1171
+ url
1172
+ }) => {
1173
+ {
1174
+ if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) {
1175
+ logger.debug(`${capture} only partially matches the cross-origin URL ` + `${url.toString()}. This route will only handle cross-origin requests ` + `if they match the entire URL.`);
1176
+ }
1177
+ }
1178
+ return url.href === captureUrl.href;
1179
+ };
1180
+ // If `capture` is a string then `handler` and `method` must be present.
1181
+ route = new Route(matchCallback, handler, method);
1182
+ } else if (capture instanceof RegExp) {
1183
+ // If `capture` is a `RegExp` then `handler` and `method` must be present.
1184
+ route = new RegExpRoute(capture, handler, method);
1185
+ } else if (typeof capture === 'function') {
1186
+ // If `capture` is a function then `handler` and `method` must be present.
1187
+ route = new Route(capture, handler, method);
1188
+ } else if (capture instanceof Route) {
1189
+ route = capture;
1190
+ } else {
1191
+ throw new WorkboxError('unsupported-route-type', {
1192
+ moduleName: 'workbox-routing',
1193
+ funcName: 'registerRoute',
1194
+ paramName: 'capture'
1195
+ });
1196
+ }
1197
+ const defaultRouter = getOrCreateDefaultRouter();
1198
+ defaultRouter.registerRoute(route);
1199
+ return route;
1200
+ }
1201
+
1202
+ /*
1203
+ Copyright 2018 Google LLC
1204
+
1205
+ Use of this source code is governed by an MIT-style
1206
+ license that can be found in the LICENSE file or at
1207
+ https://opensource.org/licenses/MIT.
1208
+ */
1209
+ const _cacheNameDetails = {
1210
+ googleAnalytics: 'googleAnalytics',
1211
+ precache: 'precache-v2',
1212
+ prefix: 'workbox',
1213
+ runtime: 'runtime',
1214
+ suffix: typeof registration !== 'undefined' ? registration.scope : ''
1215
+ };
1216
+ const _createCacheName = cacheName => {
1217
+ return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix].filter(value => value && value.length > 0).join('-');
1218
+ };
1219
+ const eachCacheNameDetail = fn => {
1220
+ for (const key of Object.keys(_cacheNameDetails)) {
1221
+ fn(key);
1222
+ }
1223
+ };
1224
+ const cacheNames = {
1225
+ updateDetails: details => {
1226
+ eachCacheNameDetail(key => {
1227
+ if (typeof details[key] === 'string') {
1228
+ _cacheNameDetails[key] = details[key];
1229
+ }
1230
+ });
1231
+ },
1232
+ getGoogleAnalyticsName: userCacheName => {
1233
+ return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics);
1234
+ },
1235
+ getPrecacheName: userCacheName => {
1236
+ return userCacheName || _createCacheName(_cacheNameDetails.precache);
1237
+ },
1238
+ getPrefix: () => {
1239
+ return _cacheNameDetails.prefix;
1240
+ },
1241
+ getRuntimeName: userCacheName => {
1242
+ return userCacheName || _createCacheName(_cacheNameDetails.runtime);
1243
+ },
1244
+ getSuffix: () => {
1245
+ return _cacheNameDetails.suffix;
1246
+ }
1247
+ };
1248
+
1249
+ /*
1250
+ Copyright 2020 Google LLC
1251
+ Use of this source code is governed by an MIT-style
1252
+ license that can be found in the LICENSE file or at
1253
+ https://opensource.org/licenses/MIT.
1254
+ */
1255
+ /**
1256
+ * A utility method that makes it easier to use `event.waitUntil` with
1257
+ * async functions and return the result.
1258
+ *
1259
+ * @param {ExtendableEvent} event
1260
+ * @param {Function} asyncFn
1261
+ * @return {Function}
1262
+ * @private
1263
+ */
1264
+ function waitUntil(event, asyncFn) {
1265
+ const returnPromise = asyncFn();
1266
+ event.waitUntil(returnPromise);
1267
+ return returnPromise;
1268
+ }
1269
+
1270
+ // @ts-ignore
1271
+ try {
1272
+ self['workbox:precaching:7.0.0'] && _();
1273
+ } catch (e) {}
1274
+
1275
+ /*
1276
+ Copyright 2018 Google LLC
1277
+
1278
+ Use of this source code is governed by an MIT-style
1279
+ license that can be found in the LICENSE file or at
1280
+ https://opensource.org/licenses/MIT.
1281
+ */
1282
+ // Name of the search parameter used to store revision info.
1283
+ const REVISION_SEARCH_PARAM = '__WB_REVISION__';
1284
+ /**
1285
+ * Converts a manifest entry into a versioned URL suitable for precaching.
1286
+ *
1287
+ * @param {Object|string} entry
1288
+ * @return {string} A URL with versioning info.
1289
+ *
1290
+ * @private
1291
+ * @memberof workbox-precaching
1292
+ */
1293
+ function createCacheKey(entry) {
1294
+ if (!entry) {
1295
+ throw new WorkboxError('add-to-cache-list-unexpected-type', {
1296
+ entry
1297
+ });
1298
+ }
1299
+ // If a precache manifest entry is a string, it's assumed to be a versioned
1300
+ // URL, like '/app.abcd1234.js'. Return as-is.
1301
+ if (typeof entry === 'string') {
1302
+ const urlObject = new URL(entry, location.href);
1303
+ return {
1304
+ cacheKey: urlObject.href,
1305
+ url: urlObject.href
1306
+ };
1307
+ }
1308
+ const {
1309
+ revision,
1310
+ url
1311
+ } = entry;
1312
+ if (!url) {
1313
+ throw new WorkboxError('add-to-cache-list-unexpected-type', {
1314
+ entry
1315
+ });
1316
+ }
1317
+ // If there's just a URL and no revision, then it's also assumed to be a
1318
+ // versioned URL.
1319
+ if (!revision) {
1320
+ const urlObject = new URL(url, location.href);
1321
+ return {
1322
+ cacheKey: urlObject.href,
1323
+ url: urlObject.href
1324
+ };
1325
+ }
1326
+ // Otherwise, construct a properly versioned URL using the custom Workbox
1327
+ // search parameter along with the revision info.
1328
+ const cacheKeyURL = new URL(url, location.href);
1329
+ const originalURL = new URL(url, location.href);
1330
+ cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);
1331
+ return {
1332
+ cacheKey: cacheKeyURL.href,
1333
+ url: originalURL.href
1334
+ };
1335
+ }
1336
+
1337
+ /*
1338
+ Copyright 2020 Google LLC
1339
+
1340
+ Use of this source code is governed by an MIT-style
1341
+ license that can be found in the LICENSE file or at
1342
+ https://opensource.org/licenses/MIT.
1343
+ */
1344
+ /**
1345
+ * A plugin, designed to be used with PrecacheController, to determine the
1346
+ * of assets that were updated (or not updated) during the install event.
1347
+ *
1348
+ * @private
1349
+ */
1350
+ class PrecacheInstallReportPlugin {
1351
+ constructor() {
1352
+ this.updatedURLs = [];
1353
+ this.notUpdatedURLs = [];
1354
+ this.handlerWillStart = async ({
1355
+ request,
1356
+ state
1357
+ }) => {
1358
+ // TODO: `state` should never be undefined...
1359
+ if (state) {
1360
+ state.originalRequest = request;
1361
+ }
1362
+ };
1363
+ this.cachedResponseWillBeUsed = async ({
1364
+ event,
1365
+ state,
1366
+ cachedResponse
1367
+ }) => {
1368
+ if (event.type === 'install') {
1369
+ if (state && state.originalRequest && state.originalRequest instanceof Request) {
1370
+ // TODO: `state` should never be undefined...
1371
+ const url = state.originalRequest.url;
1372
+ if (cachedResponse) {
1373
+ this.notUpdatedURLs.push(url);
1374
+ } else {
1375
+ this.updatedURLs.push(url);
1376
+ }
1377
+ }
1378
+ }
1379
+ return cachedResponse;
1380
+ };
1381
+ }
1382
+ }
1383
+
1384
+ /*
1385
+ Copyright 2020 Google LLC
1386
+
1387
+ Use of this source code is governed by an MIT-style
1388
+ license that can be found in the LICENSE file or at
1389
+ https://opensource.org/licenses/MIT.
1390
+ */
1391
+ /**
1392
+ * A plugin, designed to be used with PrecacheController, to translate URLs into
1393
+ * the corresponding cache key, based on the current revision info.
1394
+ *
1395
+ * @private
1396
+ */
1397
+ class PrecacheCacheKeyPlugin {
1398
+ constructor({
1399
+ precacheController
1400
+ }) {
1401
+ this.cacheKeyWillBeUsed = async ({
1402
+ request,
1403
+ params
1404
+ }) => {
1405
+ // Params is type any, can't change right now.
1406
+ /* eslint-disable */
1407
+ const cacheKey = (params === null || params === void 0 ? void 0 : params.cacheKey) || this._precacheController.getCacheKeyForURL(request.url);
1408
+ /* eslint-enable */
1409
+ return cacheKey ? new Request(cacheKey, {
1410
+ headers: request.headers
1411
+ }) : request;
1412
+ };
1413
+ this._precacheController = precacheController;
1414
+ }
1415
+ }
1416
+
1417
+ /*
1418
+ Copyright 2018 Google LLC
1419
+
1420
+ Use of this source code is governed by an MIT-style
1421
+ license that can be found in the LICENSE file or at
1422
+ https://opensource.org/licenses/MIT.
1423
+ */
1424
+ /**
1425
+ * @param {string} groupTitle
1426
+ * @param {Array<string>} deletedURLs
1427
+ *
1428
+ * @private
1429
+ */
1430
+ const logGroup = (groupTitle, deletedURLs) => {
1431
+ logger.groupCollapsed(groupTitle);
1432
+ for (const url of deletedURLs) {
1433
+ logger.log(url);
1434
+ }
1435
+ logger.groupEnd();
1436
+ };
1437
+ /**
1438
+ * @param {Array<string>} deletedURLs
1439
+ *
1440
+ * @private
1441
+ * @memberof workbox-precaching
1442
+ */
1443
+ function printCleanupDetails(deletedURLs) {
1444
+ const deletionCount = deletedURLs.length;
1445
+ if (deletionCount > 0) {
1446
+ logger.groupCollapsed(`During precaching cleanup, ` + `${deletionCount} cached ` + `request${deletionCount === 1 ? ' was' : 's were'} deleted.`);
1447
+ logGroup('Deleted Cache Requests', deletedURLs);
1448
+ logger.groupEnd();
1449
+ }
1450
+ }
1451
+
1452
+ /*
1453
+ Copyright 2018 Google LLC
1454
+
1455
+ Use of this source code is governed by an MIT-style
1456
+ license that can be found in the LICENSE file or at
1457
+ https://opensource.org/licenses/MIT.
1458
+ */
1459
+ /**
1460
+ * @param {string} groupTitle
1461
+ * @param {Array<string>} urls
1462
+ *
1463
+ * @private
1464
+ */
1465
+ function _nestedGroup(groupTitle, urls) {
1466
+ if (urls.length === 0) {
1467
+ return;
1468
+ }
1469
+ logger.groupCollapsed(groupTitle);
1470
+ for (const url of urls) {
1471
+ logger.log(url);
1472
+ }
1473
+ logger.groupEnd();
1474
+ }
1475
+ /**
1476
+ * @param {Array<string>} urlsToPrecache
1477
+ * @param {Array<string>} urlsAlreadyPrecached
1478
+ *
1479
+ * @private
1480
+ * @memberof workbox-precaching
1481
+ */
1482
+ function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) {
1483
+ const precachedCount = urlsToPrecache.length;
1484
+ const alreadyPrecachedCount = urlsAlreadyPrecached.length;
1485
+ if (precachedCount || alreadyPrecachedCount) {
1486
+ let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`;
1487
+ if (alreadyPrecachedCount > 0) {
1488
+ message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`;
1489
+ }
1490
+ logger.groupCollapsed(message);
1491
+ _nestedGroup(`View newly precached URLs.`, urlsToPrecache);
1492
+ _nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached);
1493
+ logger.groupEnd();
1494
+ }
1495
+ }
1496
+
1497
+ /*
1498
+ Copyright 2019 Google LLC
1499
+
1500
+ Use of this source code is governed by an MIT-style
1501
+ license that can be found in the LICENSE file or at
1502
+ https://opensource.org/licenses/MIT.
1503
+ */
1504
+ let supportStatus;
1505
+ /**
1506
+ * A utility function that determines whether the current browser supports
1507
+ * constructing a new `Response` from a `response.body` stream.
1508
+ *
1509
+ * @return {boolean} `true`, if the current browser can successfully
1510
+ * construct a `Response` from a `response.body` stream, `false` otherwise.
1511
+ *
1512
+ * @private
1513
+ */
1514
+ function canConstructResponseFromBodyStream() {
1515
+ if (supportStatus === undefined) {
1516
+ const testResponse = new Response('');
1517
+ if ('body' in testResponse) {
1518
+ try {
1519
+ new Response(testResponse.body);
1520
+ supportStatus = true;
1521
+ } catch (error) {
1522
+ supportStatus = false;
1523
+ }
1524
+ }
1525
+ supportStatus = false;
1526
+ }
1527
+ return supportStatus;
1528
+ }
1529
+
1530
+ /*
1531
+ Copyright 2019 Google LLC
1532
+
1533
+ Use of this source code is governed by an MIT-style
1534
+ license that can be found in the LICENSE file or at
1535
+ https://opensource.org/licenses/MIT.
1536
+ */
1537
+ /**
1538
+ * Allows developers to copy a response and modify its `headers`, `status`,
1539
+ * or `statusText` values (the values settable via a
1540
+ * [`ResponseInit`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Syntax}
1541
+ * object in the constructor).
1542
+ * To modify these values, pass a function as the second argument. That
1543
+ * function will be invoked with a single object with the response properties
1544
+ * `{headers, status, statusText}`. The return value of this function will
1545
+ * be used as the `ResponseInit` for the new `Response`. To change the values
1546
+ * either modify the passed parameter(s) and return it, or return a totally
1547
+ * new object.
1548
+ *
1549
+ * This method is intentionally limited to same-origin responses, regardless of
1550
+ * whether CORS was used or not.
1551
+ *
1552
+ * @param {Response} response
1553
+ * @param {Function} modifier
1554
+ * @memberof workbox-core
1555
+ */
1556
+ async function copyResponse(response, modifier) {
1557
+ let origin = null;
1558
+ // If response.url isn't set, assume it's cross-origin and keep origin null.
1559
+ if (response.url) {
1560
+ const responseURL = new URL(response.url);
1561
+ origin = responseURL.origin;
1562
+ }
1563
+ if (origin !== self.location.origin) {
1564
+ throw new WorkboxError('cross-origin-copy-response', {
1565
+ origin
1566
+ });
1567
+ }
1568
+ const clonedResponse = response.clone();
1569
+ // Create a fresh `ResponseInit` object by cloning the headers.
1570
+ const responseInit = {
1571
+ headers: new Headers(clonedResponse.headers),
1572
+ status: clonedResponse.status,
1573
+ statusText: clonedResponse.statusText
1574
+ };
1575
+ // Apply any user modifications.
1576
+ const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;
1577
+ // Create the new response from the body stream and `ResponseInit`
1578
+ // modifications. Note: not all browsers support the Response.body stream,
1579
+ // so fall back to reading the entire body into memory as a blob.
1580
+ const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob();
1581
+ return new Response(body, modifiedResponseInit);
1582
+ }
1583
+
1584
+ /*
1585
+ Copyright 2020 Google LLC
1586
+ Use of this source code is governed by an MIT-style
1587
+ license that can be found in the LICENSE file or at
1588
+ https://opensource.org/licenses/MIT.
1589
+ */
1590
+ function stripParams(fullURL, ignoreParams) {
1591
+ const strippedURL = new URL(fullURL);
1592
+ for (const param of ignoreParams) {
1593
+ strippedURL.searchParams.delete(param);
1594
+ }
1595
+ return strippedURL.href;
1596
+ }
1597
+ /**
1598
+ * Matches an item in the cache, ignoring specific URL params. This is similar
1599
+ * to the `ignoreSearch` option, but it allows you to ignore just specific
1600
+ * params (while continuing to match on the others).
1601
+ *
1602
+ * @private
1603
+ * @param {Cache} cache
1604
+ * @param {Request} request
1605
+ * @param {Object} matchOptions
1606
+ * @param {Array<string>} ignoreParams
1607
+ * @return {Promise<Response|undefined>}
1608
+ */
1609
+ async function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) {
1610
+ const strippedRequestURL = stripParams(request.url, ignoreParams);
1611
+ // If the request doesn't include any ignored params, match as normal.
1612
+ if (request.url === strippedRequestURL) {
1613
+ return cache.match(request, matchOptions);
1614
+ }
1615
+ // Otherwise, match by comparing keys
1616
+ const keysOptions = Object.assign(Object.assign({}, matchOptions), {
1617
+ ignoreSearch: true
1618
+ });
1619
+ const cacheKeys = await cache.keys(request, keysOptions);
1620
+ for (const cacheKey of cacheKeys) {
1621
+ const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams);
1622
+ if (strippedRequestURL === strippedCacheKeyURL) {
1623
+ return cache.match(cacheKey, matchOptions);
1624
+ }
1625
+ }
1626
+ return;
1627
+ }
1628
+
1629
+ /*
1630
+ Copyright 2018 Google LLC
1631
+
1632
+ Use of this source code is governed by an MIT-style
1633
+ license that can be found in the LICENSE file or at
1634
+ https://opensource.org/licenses/MIT.
1635
+ */
1636
+ /**
1637
+ * The Deferred class composes Promises in a way that allows for them to be
1638
+ * resolved or rejected from outside the constructor. In most cases promises
1639
+ * should be used directly, but Deferreds can be necessary when the logic to
1640
+ * resolve a promise must be separate.
1641
+ *
1642
+ * @private
1643
+ */
1644
+ class Deferred {
1645
+ /**
1646
+ * Creates a promise and exposes its resolve and reject functions as methods.
1647
+ */
1648
+ constructor() {
1649
+ this.promise = new Promise((resolve, reject) => {
1650
+ this.resolve = resolve;
1651
+ this.reject = reject;
1652
+ });
1653
+ }
1654
+ }
1655
+
1656
+ /*
1657
+ Copyright 2018 Google LLC
1658
+
1659
+ Use of this source code is governed by an MIT-style
1660
+ license that can be found in the LICENSE file or at
1661
+ https://opensource.org/licenses/MIT.
1662
+ */
1663
+ // Callbacks to be executed whenever there's a quota error.
1664
+ // Can't change Function type right now.
1665
+ // eslint-disable-next-line @typescript-eslint/ban-types
1666
+ const quotaErrorCallbacks = new Set();
1667
+
1668
+ /*
1669
+ Copyright 2018 Google LLC
1670
+
1671
+ Use of this source code is governed by an MIT-style
1672
+ license that can be found in the LICENSE file or at
1673
+ https://opensource.org/licenses/MIT.
1674
+ */
1675
+ /**
1676
+ * Runs all of the callback functions, one at a time sequentially, in the order
1677
+ * in which they were registered.
1678
+ *
1679
+ * @memberof workbox-core
1680
+ * @private
1681
+ */
1682
+ async function executeQuotaErrorCallbacks() {
1683
+ {
1684
+ logger.log(`About to run ${quotaErrorCallbacks.size} ` + `callbacks to clean up caches.`);
1685
+ }
1686
+ for (const callback of quotaErrorCallbacks) {
1687
+ await callback();
1688
+ {
1689
+ logger.log(callback, 'is complete.');
1690
+ }
1691
+ }
1692
+ {
1693
+ logger.log('Finished running callbacks.');
1694
+ }
1695
+ }
1696
+
1697
+ /*
1698
+ Copyright 2019 Google LLC
1699
+ Use of this source code is governed by an MIT-style
1700
+ license that can be found in the LICENSE file or at
1701
+ https://opensource.org/licenses/MIT.
1702
+ */
1703
+ /**
1704
+ * Returns a promise that resolves and the passed number of milliseconds.
1705
+ * This utility is an async/await-friendly version of `setTimeout`.
1706
+ *
1707
+ * @param {number} ms
1708
+ * @return {Promise}
1709
+ * @private
1710
+ */
1711
+ function timeout(ms) {
1712
+ return new Promise(resolve => setTimeout(resolve, ms));
1713
+ }
1714
+
1715
+ // @ts-ignore
1716
+ try {
1717
+ self['workbox:strategies:7.0.0'] && _();
1718
+ } catch (e) {}
1719
+
1720
+ /*
1721
+ Copyright 2020 Google LLC
1722
+
1723
+ Use of this source code is governed by an MIT-style
1724
+ license that can be found in the LICENSE file or at
1725
+ https://opensource.org/licenses/MIT.
1726
+ */
1727
+ function toRequest(input) {
1728
+ return typeof input === 'string' ? new Request(input) : input;
1729
+ }
1730
+ /**
1731
+ * A class created every time a Strategy instance instance calls
1732
+ * {@link workbox-strategies.Strategy~handle} or
1733
+ * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and
1734
+ * cache actions around plugin callbacks and keeps track of when the strategy
1735
+ * is "done" (i.e. all added `event.waitUntil()` promises have resolved).
1736
+ *
1737
+ * @memberof workbox-strategies
1738
+ */
1739
+ class StrategyHandler {
1740
+ /**
1741
+ * Creates a new instance associated with the passed strategy and event
1742
+ * that's handling the request.
1743
+ *
1744
+ * The constructor also initializes the state that will be passed to each of
1745
+ * the plugins handling this request.
1746
+ *
1747
+ * @param {workbox-strategies.Strategy} strategy
1748
+ * @param {Object} options
1749
+ * @param {Request|string} options.request A request to run this strategy for.
1750
+ * @param {ExtendableEvent} options.event The event associated with the
1751
+ * request.
1752
+ * @param {URL} [options.url]
1753
+ * @param {*} [options.params] The return value from the
1754
+ * {@link workbox-routing~matchCallback} (if applicable).
1755
+ */
1756
+ constructor(strategy, options) {
1757
+ this._cacheKeys = {};
1758
+ /**
1759
+ * The request the strategy is performing (passed to the strategy's
1760
+ * `handle()` or `handleAll()` method).
1761
+ * @name request
1762
+ * @instance
1763
+ * @type {Request}
1764
+ * @memberof workbox-strategies.StrategyHandler
1765
+ */
1766
+ /**
1767
+ * The event associated with this request.
1768
+ * @name event
1769
+ * @instance
1770
+ * @type {ExtendableEvent}
1771
+ * @memberof workbox-strategies.StrategyHandler
1772
+ */
1773
+ /**
1774
+ * A `URL` instance of `request.url` (if passed to the strategy's
1775
+ * `handle()` or `handleAll()` method).
1776
+ * Note: the `url` param will be present if the strategy was invoked
1777
+ * from a workbox `Route` object.
1778
+ * @name url
1779
+ * @instance
1780
+ * @type {URL|undefined}
1781
+ * @memberof workbox-strategies.StrategyHandler
1782
+ */
1783
+ /**
1784
+ * A `param` value (if passed to the strategy's
1785
+ * `handle()` or `handleAll()` method).
1786
+ * Note: the `param` param will be present if the strategy was invoked
1787
+ * from a workbox `Route` object and the
1788
+ * {@link workbox-routing~matchCallback} returned
1789
+ * a truthy value (it will be that value).
1790
+ * @name params
1791
+ * @instance
1792
+ * @type {*|undefined}
1793
+ * @memberof workbox-strategies.StrategyHandler
1794
+ */
1795
+ {
1796
+ finalAssertExports.isInstance(options.event, ExtendableEvent, {
1797
+ moduleName: 'workbox-strategies',
1798
+ className: 'StrategyHandler',
1799
+ funcName: 'constructor',
1800
+ paramName: 'options.event'
1801
+ });
1802
+ }
1803
+ Object.assign(this, options);
1804
+ this.event = options.event;
1805
+ this._strategy = strategy;
1806
+ this._handlerDeferred = new Deferred();
1807
+ this._extendLifetimePromises = [];
1808
+ // Copy the plugins list (since it's mutable on the strategy),
1809
+ // so any mutations don't affect this handler instance.
1810
+ this._plugins = [...strategy.plugins];
1811
+ this._pluginStateMap = new Map();
1812
+ for (const plugin of this._plugins) {
1813
+ this._pluginStateMap.set(plugin, {});
1814
+ }
1815
+ this.event.waitUntil(this._handlerDeferred.promise);
1816
+ }
1817
+ /**
1818
+ * Fetches a given request (and invokes any applicable plugin callback
1819
+ * methods) using the `fetchOptions` (for non-navigation requests) and
1820
+ * `plugins` defined on the `Strategy` object.
1821
+ *
1822
+ * The following plugin lifecycle methods are invoked when using this method:
1823
+ * - `requestWillFetch()`
1824
+ * - `fetchDidSucceed()`
1825
+ * - `fetchDidFail()`
1826
+ *
1827
+ * @param {Request|string} input The URL or request to fetch.
1828
+ * @return {Promise<Response>}
1829
+ */
1830
+ async fetch(input) {
1831
+ const {
1832
+ event
1833
+ } = this;
1834
+ let request = toRequest(input);
1835
+ if (request.mode === 'navigate' && event instanceof FetchEvent && event.preloadResponse) {
1836
+ const possiblePreloadResponse = await event.preloadResponse;
1837
+ if (possiblePreloadResponse) {
1838
+ {
1839
+ logger.log(`Using a preloaded navigation response for ` + `'${getFriendlyURL(request.url)}'`);
1840
+ }
1841
+ return possiblePreloadResponse;
1842
+ }
1843
+ }
1844
+ // If there is a fetchDidFail plugin, we need to save a clone of the
1845
+ // original request before it's either modified by a requestWillFetch
1846
+ // plugin or before the original request's body is consumed via fetch().
1847
+ const originalRequest = this.hasCallback('fetchDidFail') ? request.clone() : null;
1848
+ try {
1849
+ for (const cb of this.iterateCallbacks('requestWillFetch')) {
1850
+ request = await cb({
1851
+ request: request.clone(),
1852
+ event
1853
+ });
1854
+ }
1855
+ } catch (err) {
1856
+ if (err instanceof Error) {
1857
+ throw new WorkboxError('plugin-error-request-will-fetch', {
1858
+ thrownErrorMessage: err.message
1859
+ });
1860
+ }
1861
+ }
1862
+ // The request can be altered by plugins with `requestWillFetch` making
1863
+ // the original request (most likely from a `fetch` event) different
1864
+ // from the Request we make. Pass both to `fetchDidFail` to aid debugging.
1865
+ const pluginFilteredRequest = request.clone();
1866
+ try {
1867
+ let fetchResponse;
1868
+ // See https://github.com/GoogleChrome/workbox/issues/1796
1869
+ fetchResponse = await fetch(request, request.mode === 'navigate' ? undefined : this._strategy.fetchOptions);
1870
+ if ("development" !== 'production') {
1871
+ logger.debug(`Network request for ` + `'${getFriendlyURL(request.url)}' returned a response with ` + `status '${fetchResponse.status}'.`);
1872
+ }
1873
+ for (const callback of this.iterateCallbacks('fetchDidSucceed')) {
1874
+ fetchResponse = await callback({
1875
+ event,
1876
+ request: pluginFilteredRequest,
1877
+ response: fetchResponse
1878
+ });
1879
+ }
1880
+ return fetchResponse;
1881
+ } catch (error) {
1882
+ {
1883
+ logger.log(`Network request for ` + `'${getFriendlyURL(request.url)}' threw an error.`, error);
1884
+ }
1885
+ // `originalRequest` will only exist if a `fetchDidFail` callback
1886
+ // is being used (see above).
1887
+ if (originalRequest) {
1888
+ await this.runCallbacks('fetchDidFail', {
1889
+ error: error,
1890
+ event,
1891
+ originalRequest: originalRequest.clone(),
1892
+ request: pluginFilteredRequest.clone()
1893
+ });
1894
+ }
1895
+ throw error;
1896
+ }
1897
+ }
1898
+ /**
1899
+ * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on
1900
+ * the response generated by `this.fetch()`.
1901
+ *
1902
+ * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
1903
+ * so you do not have to manually call `waitUntil()` on the event.
1904
+ *
1905
+ * @param {Request|string} input The request or URL to fetch and cache.
1906
+ * @return {Promise<Response>}
1907
+ */
1908
+ async fetchAndCachePut(input) {
1909
+ const response = await this.fetch(input);
1910
+ const responseClone = response.clone();
1911
+ void this.waitUntil(this.cachePut(input, responseClone));
1912
+ return response;
1913
+ }
1914
+ /**
1915
+ * Matches a request from the cache (and invokes any applicable plugin
1916
+ * callback methods) using the `cacheName`, `matchOptions`, and `plugins`
1917
+ * defined on the strategy object.
1918
+ *
1919
+ * The following plugin lifecycle methods are invoked when using this method:
1920
+ * - cacheKeyWillByUsed()
1921
+ * - cachedResponseWillByUsed()
1922
+ *
1923
+ * @param {Request|string} key The Request or URL to use as the cache key.
1924
+ * @return {Promise<Response|undefined>} A matching response, if found.
1925
+ */
1926
+ async cacheMatch(key) {
1927
+ const request = toRequest(key);
1928
+ let cachedResponse;
1929
+ const {
1930
+ cacheName,
1931
+ matchOptions
1932
+ } = this._strategy;
1933
+ const effectiveRequest = await this.getCacheKey(request, 'read');
1934
+ const multiMatchOptions = Object.assign(Object.assign({}, matchOptions), {
1935
+ cacheName
1936
+ });
1937
+ cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
1938
+ {
1939
+ if (cachedResponse) {
1940
+ logger.debug(`Found a cached response in '${cacheName}'.`);
1941
+ } else {
1942
+ logger.debug(`No cached response found in '${cacheName}'.`);
1943
+ }
1944
+ }
1945
+ for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) {
1946
+ cachedResponse = (await callback({
1947
+ cacheName,
1948
+ matchOptions,
1949
+ cachedResponse,
1950
+ request: effectiveRequest,
1951
+ event: this.event
1952
+ })) || undefined;
1953
+ }
1954
+ return cachedResponse;
1955
+ }
1956
+ /**
1957
+ * Puts a request/response pair in the cache (and invokes any applicable
1958
+ * plugin callback methods) using the `cacheName` and `plugins` defined on
1959
+ * the strategy object.
1960
+ *
1961
+ * The following plugin lifecycle methods are invoked when using this method:
1962
+ * - cacheKeyWillByUsed()
1963
+ * - cacheWillUpdate()
1964
+ * - cacheDidUpdate()
1965
+ *
1966
+ * @param {Request|string} key The request or URL to use as the cache key.
1967
+ * @param {Response} response The response to cache.
1968
+ * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response
1969
+ * not be cached, and `true` otherwise.
1970
+ */
1971
+ async cachePut(key, response) {
1972
+ const request = toRequest(key);
1973
+ // Run in the next task to avoid blocking other cache reads.
1974
+ // https://github.com/w3c/ServiceWorker/issues/1397
1975
+ await timeout(0);
1976
+ const effectiveRequest = await this.getCacheKey(request, 'write');
1977
+ {
1978
+ if (effectiveRequest.method && effectiveRequest.method !== 'GET') {
1979
+ throw new WorkboxError('attempt-to-cache-non-get-request', {
1980
+ url: getFriendlyURL(effectiveRequest.url),
1981
+ method: effectiveRequest.method
1982
+ });
1983
+ }
1984
+ // See https://github.com/GoogleChrome/workbox/issues/2818
1985
+ const vary = response.headers.get('Vary');
1986
+ if (vary) {
1987
+ logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} ` + `has a 'Vary: ${vary}' header. ` + `Consider setting the {ignoreVary: true} option on your strategy ` + `to ensure cache matching and deletion works as expected.`);
1988
+ }
1989
+ }
1990
+ if (!response) {
1991
+ {
1992
+ logger.error(`Cannot cache non-existent response for ` + `'${getFriendlyURL(effectiveRequest.url)}'.`);
1993
+ }
1994
+ throw new WorkboxError('cache-put-with-no-response', {
1995
+ url: getFriendlyURL(effectiveRequest.url)
1996
+ });
1997
+ }
1998
+ const responseToCache = await this._ensureResponseSafeToCache(response);
1999
+ if (!responseToCache) {
2000
+ {
2001
+ logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` + `will not be cached.`, responseToCache);
2002
+ }
2003
+ return false;
2004
+ }
2005
+ const {
2006
+ cacheName,
2007
+ matchOptions
2008
+ } = this._strategy;
2009
+ const cache = await self.caches.open(cacheName);
2010
+ const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate');
2011
+ const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(
2012
+ // TODO(philipwalton): the `__WB_REVISION__` param is a precaching
2013
+ // feature. Consider into ways to only add this behavior if using
2014
+ // precaching.
2015
+ cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions) : null;
2016
+ {
2017
+ logger.debug(`Updating the '${cacheName}' cache with a new Response ` + `for ${getFriendlyURL(effectiveRequest.url)}.`);
2018
+ }
2019
+ try {
2020
+ await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
2021
+ } catch (error) {
2022
+ if (error instanceof Error) {
2023
+ // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError
2024
+ if (error.name === 'QuotaExceededError') {
2025
+ await executeQuotaErrorCallbacks();
2026
+ }
2027
+ throw error;
2028
+ }
2029
+ }
2030
+ for (const callback of this.iterateCallbacks('cacheDidUpdate')) {
2031
+ await callback({
2032
+ cacheName,
2033
+ oldResponse,
2034
+ newResponse: responseToCache.clone(),
2035
+ request: effectiveRequest,
2036
+ event: this.event
2037
+ });
2038
+ }
2039
+ return true;
2040
+ }
2041
+ /**
2042
+ * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and
2043
+ * executes any of those callbacks found in sequence. The final `Request`
2044
+ * object returned by the last plugin is treated as the cache key for cache
2045
+ * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have
2046
+ * been registered, the passed request is returned unmodified
2047
+ *
2048
+ * @param {Request} request
2049
+ * @param {string} mode
2050
+ * @return {Promise<Request>}
2051
+ */
2052
+ async getCacheKey(request, mode) {
2053
+ const key = `${request.url} | ${mode}`;
2054
+ if (!this._cacheKeys[key]) {
2055
+ let effectiveRequest = request;
2056
+ for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) {
2057
+ effectiveRequest = toRequest(await callback({
2058
+ mode,
2059
+ request: effectiveRequest,
2060
+ event: this.event,
2061
+ // params has a type any can't change right now.
2062
+ params: this.params // eslint-disable-line
2063
+ }));
2064
+ }
2065
+ this._cacheKeys[key] = effectiveRequest;
2066
+ }
2067
+ return this._cacheKeys[key];
2068
+ }
2069
+ /**
2070
+ * Returns true if the strategy has at least one plugin with the given
2071
+ * callback.
2072
+ *
2073
+ * @param {string} name The name of the callback to check for.
2074
+ * @return {boolean}
2075
+ */
2076
+ hasCallback(name) {
2077
+ for (const plugin of this._strategy.plugins) {
2078
+ if (name in plugin) {
2079
+ return true;
2080
+ }
2081
+ }
2082
+ return false;
2083
+ }
2084
+ /**
2085
+ * Runs all plugin callbacks matching the given name, in order, passing the
2086
+ * given param object (merged ith the current plugin state) as the only
2087
+ * argument.
2088
+ *
2089
+ * Note: since this method runs all plugins, it's not suitable for cases
2090
+ * where the return value of a callback needs to be applied prior to calling
2091
+ * the next callback. See
2092
+ * {@link workbox-strategies.StrategyHandler#iterateCallbacks}
2093
+ * below for how to handle that case.
2094
+ *
2095
+ * @param {string} name The name of the callback to run within each plugin.
2096
+ * @param {Object} param The object to pass as the first (and only) param
2097
+ * when executing each callback. This object will be merged with the
2098
+ * current plugin state prior to callback execution.
2099
+ */
2100
+ async runCallbacks(name, param) {
2101
+ for (const callback of this.iterateCallbacks(name)) {
2102
+ // TODO(philipwalton): not sure why `any` is needed. It seems like
2103
+ // this should work with `as WorkboxPluginCallbackParam[C]`.
2104
+ await callback(param);
2105
+ }
2106
+ }
2107
+ /**
2108
+ * Accepts a callback and returns an iterable of matching plugin callbacks,
2109
+ * where each callback is wrapped with the current handler state (i.e. when
2110
+ * you call each callback, whatever object parameter you pass it will
2111
+ * be merged with the plugin's current state).
2112
+ *
2113
+ * @param {string} name The name fo the callback to run
2114
+ * @return {Array<Function>}
2115
+ */
2116
+ *iterateCallbacks(name) {
2117
+ for (const plugin of this._strategy.plugins) {
2118
+ if (typeof plugin[name] === 'function') {
2119
+ const state = this._pluginStateMap.get(plugin);
2120
+ const statefulCallback = param => {
2121
+ const statefulParam = Object.assign(Object.assign({}, param), {
2122
+ state
2123
+ });
2124
+ // TODO(philipwalton): not sure why `any` is needed. It seems like
2125
+ // this should work with `as WorkboxPluginCallbackParam[C]`.
2126
+ return plugin[name](statefulParam);
2127
+ };
2128
+ yield statefulCallback;
2129
+ }
2130
+ }
2131
+ }
2132
+ /**
2133
+ * Adds a promise to the
2134
+ * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}
2135
+ * of the event event associated with the request being handled (usually a
2136
+ * `FetchEvent`).
2137
+ *
2138
+ * Note: you can await
2139
+ * {@link workbox-strategies.StrategyHandler~doneWaiting}
2140
+ * to know when all added promises have settled.
2141
+ *
2142
+ * @param {Promise} promise A promise to add to the extend lifetime promises
2143
+ * of the event that triggered the request.
2144
+ */
2145
+ waitUntil(promise) {
2146
+ this._extendLifetimePromises.push(promise);
2147
+ return promise;
2148
+ }
2149
+ /**
2150
+ * Returns a promise that resolves once all promises passed to
2151
+ * {@link workbox-strategies.StrategyHandler~waitUntil}
2152
+ * have settled.
2153
+ *
2154
+ * Note: any work done after `doneWaiting()` settles should be manually
2155
+ * passed to an event's `waitUntil()` method (not this handler's
2156
+ * `waitUntil()` method), otherwise the service worker thread my be killed
2157
+ * prior to your work completing.
2158
+ */
2159
+ async doneWaiting() {
2160
+ let promise;
2161
+ while (promise = this._extendLifetimePromises.shift()) {
2162
+ await promise;
2163
+ }
2164
+ }
2165
+ /**
2166
+ * Stops running the strategy and immediately resolves any pending
2167
+ * `waitUntil()` promises.
2168
+ */
2169
+ destroy() {
2170
+ this._handlerDeferred.resolve(null);
2171
+ }
2172
+ /**
2173
+ * This method will call cacheWillUpdate on the available plugins (or use
2174
+ * status === 200) to determine if the Response is safe and valid to cache.
2175
+ *
2176
+ * @param {Request} options.request
2177
+ * @param {Response} options.response
2178
+ * @return {Promise<Response|undefined>}
2179
+ *
2180
+ * @private
2181
+ */
2182
+ async _ensureResponseSafeToCache(response) {
2183
+ let responseToCache = response;
2184
+ let pluginsUsed = false;
2185
+ for (const callback of this.iterateCallbacks('cacheWillUpdate')) {
2186
+ responseToCache = (await callback({
2187
+ request: this.request,
2188
+ response: responseToCache,
2189
+ event: this.event
2190
+ })) || undefined;
2191
+ pluginsUsed = true;
2192
+ if (!responseToCache) {
2193
+ break;
2194
+ }
2195
+ }
2196
+ if (!pluginsUsed) {
2197
+ if (responseToCache && responseToCache.status !== 200) {
2198
+ responseToCache = undefined;
2199
+ }
2200
+ {
2201
+ if (responseToCache) {
2202
+ if (responseToCache.status !== 200) {
2203
+ if (responseToCache.status === 0) {
2204
+ logger.warn(`The response for '${this.request.url}' ` + `is an opaque response. The caching strategy that you're ` + `using will not cache opaque responses by default.`);
2205
+ } else {
2206
+ logger.debug(`The response for '${this.request.url}' ` + `returned a status code of '${response.status}' and won't ` + `be cached as a result.`);
2207
+ }
2208
+ }
2209
+ }
2210
+ }
2211
+ }
2212
+ return responseToCache;
2213
+ }
2214
+ }
2215
+
2216
+ /*
2217
+ Copyright 2020 Google LLC
2218
+
2219
+ Use of this source code is governed by an MIT-style
2220
+ license that can be found in the LICENSE file or at
2221
+ https://opensource.org/licenses/MIT.
2222
+ */
2223
+ /**
2224
+ * An abstract base class that all other strategy classes must extend from:
2225
+ *
2226
+ * @memberof workbox-strategies
2227
+ */
2228
+ class Strategy {
2229
+ /**
2230
+ * Creates a new instance of the strategy and sets all documented option
2231
+ * properties as public instance properties.
2232
+ *
2233
+ * Note: if a custom strategy class extends the base Strategy class and does
2234
+ * not need more than these properties, it does not need to define its own
2235
+ * constructor.
2236
+ *
2237
+ * @param {Object} [options]
2238
+ * @param {string} [options.cacheName] Cache name to store and retrieve
2239
+ * requests. Defaults to the cache names provided by
2240
+ * {@link workbox-core.cacheNames}.
2241
+ * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
2242
+ * to use in conjunction with this caching strategy.
2243
+ * @param {Object} [options.fetchOptions] Values passed along to the
2244
+ * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
2245
+ * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
2246
+ * `fetch()` requests made by this strategy.
2247
+ * @param {Object} [options.matchOptions] The
2248
+ * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
2249
+ * for any `cache.match()` or `cache.put()` calls made by this strategy.
2250
+ */
2251
+ constructor(options = {}) {
2252
+ /**
2253
+ * Cache name to store and retrieve
2254
+ * requests. Defaults to the cache names provided by
2255
+ * {@link workbox-core.cacheNames}.
2256
+ *
2257
+ * @type {string}
2258
+ */
2259
+ this.cacheName = cacheNames.getRuntimeName(options.cacheName);
2260
+ /**
2261
+ * The list
2262
+ * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
2263
+ * used by this strategy.
2264
+ *
2265
+ * @type {Array<Object>}
2266
+ */
2267
+ this.plugins = options.plugins || [];
2268
+ /**
2269
+ * Values passed along to the
2270
+ * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters}
2271
+ * of all fetch() requests made by this strategy.
2272
+ *
2273
+ * @type {Object}
2274
+ */
2275
+ this.fetchOptions = options.fetchOptions;
2276
+ /**
2277
+ * The
2278
+ * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
2279
+ * for any `cache.match()` or `cache.put()` calls made by this strategy.
2280
+ *
2281
+ * @type {Object}
2282
+ */
2283
+ this.matchOptions = options.matchOptions;
2284
+ }
2285
+ /**
2286
+ * Perform a request strategy and returns a `Promise` that will resolve with
2287
+ * a `Response`, invoking all relevant plugin callbacks.
2288
+ *
2289
+ * When a strategy instance is registered with a Workbox
2290
+ * {@link workbox-routing.Route}, this method is automatically
2291
+ * called when the route matches.
2292
+ *
2293
+ * Alternatively, this method can be used in a standalone `FetchEvent`
2294
+ * listener by passing it to `event.respondWith()`.
2295
+ *
2296
+ * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
2297
+ * properties listed below.
2298
+ * @param {Request|string} options.request A request to run this strategy for.
2299
+ * @param {ExtendableEvent} options.event The event associated with the
2300
+ * request.
2301
+ * @param {URL} [options.url]
2302
+ * @param {*} [options.params]
2303
+ */
2304
+ handle(options) {
2305
+ const [responseDone] = this.handleAll(options);
2306
+ return responseDone;
2307
+ }
2308
+ /**
2309
+ * Similar to {@link workbox-strategies.Strategy~handle}, but
2310
+ * instead of just returning a `Promise` that resolves to a `Response` it
2311
+ * it will return an tuple of `[response, done]` promises, where the former
2312
+ * (`response`) is equivalent to what `handle()` returns, and the latter is a
2313
+ * Promise that will resolve once any promises that were added to
2314
+ * `event.waitUntil()` as part of performing the strategy have completed.
2315
+ *
2316
+ * You can await the `done` promise to ensure any extra work performed by
2317
+ * the strategy (usually caching responses) completes successfully.
2318
+ *
2319
+ * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
2320
+ * properties listed below.
2321
+ * @param {Request|string} options.request A request to run this strategy for.
2322
+ * @param {ExtendableEvent} options.event The event associated with the
2323
+ * request.
2324
+ * @param {URL} [options.url]
2325
+ * @param {*} [options.params]
2326
+ * @return {Array<Promise>} A tuple of [response, done]
2327
+ * promises that can be used to determine when the response resolves as
2328
+ * well as when the handler has completed all its work.
2329
+ */
2330
+ handleAll(options) {
2331
+ // Allow for flexible options to be passed.
2332
+ if (options instanceof FetchEvent) {
2333
+ options = {
2334
+ event: options,
2335
+ request: options.request
2336
+ };
2337
+ }
2338
+ const event = options.event;
2339
+ const request = typeof options.request === 'string' ? new Request(options.request) : options.request;
2340
+ const params = 'params' in options ? options.params : undefined;
2341
+ const handler = new StrategyHandler(this, {
2342
+ event,
2343
+ request,
2344
+ params
2345
+ });
2346
+ const responseDone = this._getResponse(handler, request, event);
2347
+ const handlerDone = this._awaitComplete(responseDone, handler, request, event);
2348
+ // Return an array of promises, suitable for use with Promise.all().
2349
+ return [responseDone, handlerDone];
2350
+ }
2351
+ async _getResponse(handler, request, event) {
2352
+ await handler.runCallbacks('handlerWillStart', {
2353
+ event,
2354
+ request
2355
+ });
2356
+ let response = undefined;
2357
+ try {
2358
+ response = await this._handle(request, handler);
2359
+ // The "official" Strategy subclasses all throw this error automatically,
2360
+ // but in case a third-party Strategy doesn't, ensure that we have a
2361
+ // consistent failure when there's no response or an error response.
2362
+ if (!response || response.type === 'error') {
2363
+ throw new WorkboxError('no-response', {
2364
+ url: request.url
2365
+ });
2366
+ }
2367
+ } catch (error) {
2368
+ if (error instanceof Error) {
2369
+ for (const callback of handler.iterateCallbacks('handlerDidError')) {
2370
+ response = await callback({
2371
+ error,
2372
+ event,
2373
+ request
2374
+ });
2375
+ if (response) {
2376
+ break;
2377
+ }
2378
+ }
2379
+ }
2380
+ if (!response) {
2381
+ throw error;
2382
+ } else {
2383
+ logger.log(`While responding to '${getFriendlyURL(request.url)}', ` + `an ${error instanceof Error ? error.toString() : ''} error occurred. Using a fallback response provided by ` + `a handlerDidError plugin.`);
2384
+ }
2385
+ }
2386
+ for (const callback of handler.iterateCallbacks('handlerWillRespond')) {
2387
+ response = await callback({
2388
+ event,
2389
+ request,
2390
+ response
2391
+ });
2392
+ }
2393
+ return response;
2394
+ }
2395
+ async _awaitComplete(responseDone, handler, request, event) {
2396
+ let response;
2397
+ let error;
2398
+ try {
2399
+ response = await responseDone;
2400
+ } catch (error) {
2401
+ // Ignore errors, as response errors should be caught via the `response`
2402
+ // promise above. The `done` promise will only throw for errors in
2403
+ // promises passed to `handler.waitUntil()`.
2404
+ }
2405
+ try {
2406
+ await handler.runCallbacks('handlerDidRespond', {
2407
+ event,
2408
+ request,
2409
+ response
2410
+ });
2411
+ await handler.doneWaiting();
2412
+ } catch (waitUntilError) {
2413
+ if (waitUntilError instanceof Error) {
2414
+ error = waitUntilError;
2415
+ }
2416
+ }
2417
+ await handler.runCallbacks('handlerDidComplete', {
2418
+ event,
2419
+ request,
2420
+ response,
2421
+ error: error
2422
+ });
2423
+ handler.destroy();
2424
+ if (error) {
2425
+ throw error;
2426
+ }
2427
+ }
2428
+ }
2429
+ /**
2430
+ * Classes extending the `Strategy` based class should implement this method,
2431
+ * and leverage the {@link workbox-strategies.StrategyHandler}
2432
+ * arg to perform all fetching and cache logic, which will ensure all relevant
2433
+ * cache, cache options, fetch options and plugins are used (per the current
2434
+ * strategy instance).
2435
+ *
2436
+ * @name _handle
2437
+ * @instance
2438
+ * @abstract
2439
+ * @function
2440
+ * @param {Request} request
2441
+ * @param {workbox-strategies.StrategyHandler} handler
2442
+ * @return {Promise<Response>}
2443
+ *
2444
+ * @memberof workbox-strategies.Strategy
2445
+ */
2446
+
2447
+ /*
2448
+ Copyright 2020 Google LLC
2449
+
2450
+ Use of this source code is governed by an MIT-style
2451
+ license that can be found in the LICENSE file or at
2452
+ https://opensource.org/licenses/MIT.
2453
+ */
2454
+ /**
2455
+ * A {@link workbox-strategies.Strategy} implementation
2456
+ * specifically designed to work with
2457
+ * {@link workbox-precaching.PrecacheController}
2458
+ * to both cache and fetch precached assets.
2459
+ *
2460
+ * Note: an instance of this class is created automatically when creating a
2461
+ * `PrecacheController`; it's generally not necessary to create this yourself.
2462
+ *
2463
+ * @extends workbox-strategies.Strategy
2464
+ * @memberof workbox-precaching
2465
+ */
2466
+ class PrecacheStrategy extends Strategy {
2467
+ /**
2468
+ *
2469
+ * @param {Object} [options]
2470
+ * @param {string} [options.cacheName] Cache name to store and retrieve
2471
+ * requests. Defaults to the cache names provided by
2472
+ * {@link workbox-core.cacheNames}.
2473
+ * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins}
2474
+ * to use in conjunction with this caching strategy.
2475
+ * @param {Object} [options.fetchOptions] Values passed along to the
2476
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init}
2477
+ * of all fetch() requests made by this strategy.
2478
+ * @param {Object} [options.matchOptions] The
2479
+ * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions}
2480
+ * for any `cache.match()` or `cache.put()` calls made by this strategy.
2481
+ * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to
2482
+ * get the response from the network if there's a precache miss.
2483
+ */
2484
+ constructor(options = {}) {
2485
+ options.cacheName = cacheNames.getPrecacheName(options.cacheName);
2486
+ super(options);
2487
+ this._fallbackToNetwork = options.fallbackToNetwork === false ? false : true;
2488
+ // Redirected responses cannot be used to satisfy a navigation request, so
2489
+ // any redirected response must be "copied" rather than cloned, so the new
2490
+ // response doesn't contain the `redirected` flag. See:
2491
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1
2492
+ this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
2493
+ }
2494
+ /**
2495
+ * @private
2496
+ * @param {Request|string} request A request to run this strategy for.
2497
+ * @param {workbox-strategies.StrategyHandler} handler The event that
2498
+ * triggered the request.
2499
+ * @return {Promise<Response>}
2500
+ */
2501
+ async _handle(request, handler) {
2502
+ const response = await handler.cacheMatch(request);
2503
+ if (response) {
2504
+ return response;
2505
+ }
2506
+ // If this is an `install` event for an entry that isn't already cached,
2507
+ // then populate the cache.
2508
+ if (handler.event && handler.event.type === 'install') {
2509
+ return await this._handleInstall(request, handler);
2510
+ }
2511
+ // Getting here means something went wrong. An entry that should have been
2512
+ // precached wasn't found in the cache.
2513
+ return await this._handleFetch(request, handler);
2514
+ }
2515
+ async _handleFetch(request, handler) {
2516
+ let response;
2517
+ const params = handler.params || {};
2518
+ // Fall back to the network if we're configured to do so.
2519
+ if (this._fallbackToNetwork) {
2520
+ {
2521
+ logger.warn(`The precached response for ` + `${getFriendlyURL(request.url)} in ${this.cacheName} was not ` + `found. Falling back to the network.`);
2522
+ }
2523
+ const integrityInManifest = params.integrity;
2524
+ const integrityInRequest = request.integrity;
2525
+ const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;
2526
+ // Do not add integrity if the original request is no-cors
2527
+ // See https://github.com/GoogleChrome/workbox/issues/3096
2528
+ response = await handler.fetch(new Request(request, {
2529
+ integrity: request.mode !== 'no-cors' ? integrityInRequest || integrityInManifest : undefined
2530
+ }));
2531
+ // It's only "safe" to repair the cache if we're using SRI to guarantee
2532
+ // that the response matches the precache manifest's expectations,
2533
+ // and there's either a) no integrity property in the incoming request
2534
+ // or b) there is an integrity, and it matches the precache manifest.
2535
+ // See https://github.com/GoogleChrome/workbox/issues/2858
2536
+ // Also if the original request users no-cors we don't use integrity.
2537
+ // See https://github.com/GoogleChrome/workbox/issues/3096
2538
+ if (integrityInManifest && noIntegrityConflict && request.mode !== 'no-cors') {
2539
+ this._useDefaultCacheabilityPluginIfNeeded();
2540
+ const wasCached = await handler.cachePut(request, response.clone());
2541
+ {
2542
+ if (wasCached) {
2543
+ logger.log(`A response for ${getFriendlyURL(request.url)} ` + `was used to "repair" the precache.`);
2544
+ }
2545
+ }
2546
+ }
2547
+ } else {
2548
+ // This shouldn't normally happen, but there are edge cases:
2549
+ // https://github.com/GoogleChrome/workbox/issues/1441
2550
+ throw new WorkboxError('missing-precache-entry', {
2551
+ cacheName: this.cacheName,
2552
+ url: request.url
2553
+ });
2554
+ }
2555
+ {
2556
+ const cacheKey = params.cacheKey || (await handler.getCacheKey(request, 'read'));
2557
+ // Workbox is going to handle the route.
2558
+ // print the routing details to the console.
2559
+ logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL(request.url));
2560
+ logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);
2561
+ logger.groupCollapsed(`View request details here.`);
2562
+ logger.log(request);
2563
+ logger.groupEnd();
2564
+ logger.groupCollapsed(`View response details here.`);
2565
+ logger.log(response);
2566
+ logger.groupEnd();
2567
+ logger.groupEnd();
2568
+ }
2569
+ return response;
2570
+ }
2571
+ async _handleInstall(request, handler) {
2572
+ this._useDefaultCacheabilityPluginIfNeeded();
2573
+ const response = await handler.fetch(request);
2574
+ // Make sure we defer cachePut() until after we know the response
2575
+ // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737
2576
+ const wasCached = await handler.cachePut(request, response.clone());
2577
+ if (!wasCached) {
2578
+ // Throwing here will lead to the `install` handler failing, which
2579
+ // we want to do if *any* of the responses aren't safe to cache.
2580
+ throw new WorkboxError('bad-precaching-response', {
2581
+ url: request.url,
2582
+ status: response.status
2583
+ });
2584
+ }
2585
+ return response;
2586
+ }
2587
+ /**
2588
+ * This method is complex, as there a number of things to account for:
2589
+ *
2590
+ * The `plugins` array can be set at construction, and/or it might be added to
2591
+ * to at any time before the strategy is used.
2592
+ *
2593
+ * At the time the strategy is used (i.e. during an `install` event), there
2594
+ * needs to be at least one plugin that implements `cacheWillUpdate` in the
2595
+ * array, other than `copyRedirectedCacheableResponsesPlugin`.
2596
+ *
2597
+ * - If this method is called and there are no suitable `cacheWillUpdate`
2598
+ * plugins, we need to add `defaultPrecacheCacheabilityPlugin`.
2599
+ *
2600
+ * - If this method is called and there is exactly one `cacheWillUpdate`, then
2601
+ * we don't have to do anything (this might be a previously added
2602
+ * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).
2603
+ *
2604
+ * - If this method is called and there is more than one `cacheWillUpdate`,
2605
+ * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,
2606
+ * we need to remove it. (This situation is unlikely, but it could happen if
2607
+ * the strategy is used multiple times, the first without a `cacheWillUpdate`,
2608
+ * and then later on after manually adding a custom `cacheWillUpdate`.)
2609
+ *
2610
+ * See https://github.com/GoogleChrome/workbox/issues/2737 for more context.
2611
+ *
2612
+ * @private
2613
+ */
2614
+ _useDefaultCacheabilityPluginIfNeeded() {
2615
+ let defaultPluginIndex = null;
2616
+ let cacheWillUpdatePluginCount = 0;
2617
+ for (const [index, plugin] of this.plugins.entries()) {
2618
+ // Ignore the copy redirected plugin when determining what to do.
2619
+ if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {
2620
+ continue;
2621
+ }
2622
+ // Save the default plugin's index, in case it needs to be removed.
2623
+ if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {
2624
+ defaultPluginIndex = index;
2625
+ }
2626
+ if (plugin.cacheWillUpdate) {
2627
+ cacheWillUpdatePluginCount++;
2628
+ }
2629
+ }
2630
+ if (cacheWillUpdatePluginCount === 0) {
2631
+ this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);
2632
+ } else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {
2633
+ // Only remove the default plugin; multiple custom plugins are allowed.
2634
+ this.plugins.splice(defaultPluginIndex, 1);
2635
+ }
2636
+ // Nothing needs to be done if cacheWillUpdatePluginCount is 1
2637
+ }
2638
+ }
2639
+ PrecacheStrategy.defaultPrecacheCacheabilityPlugin = {
2640
+ async cacheWillUpdate({
2641
+ response
2642
+ }) {
2643
+ if (!response || response.status >= 400) {
2644
+ return null;
2645
+ }
2646
+ return response;
2647
+ }
2648
+ };
2649
+ PrecacheStrategy.copyRedirectedCacheableResponsesPlugin = {
2650
+ async cacheWillUpdate({
2651
+ response
2652
+ }) {
2653
+ return response.redirected ? await copyResponse(response) : response;
2654
+ }
2655
+ };
2656
+
2657
+ /*
2658
+ Copyright 2019 Google LLC
2659
+
2660
+ Use of this source code is governed by an MIT-style
2661
+ license that can be found in the LICENSE file or at
2662
+ https://opensource.org/licenses/MIT.
2663
+ */
2664
+ /**
2665
+ * Performs efficient precaching of assets.
2666
+ *
2667
+ * @memberof workbox-precaching
2668
+ */
2669
+ class PrecacheController {
2670
+ /**
2671
+ * Create a new PrecacheController.
2672
+ *
2673
+ * @param {Object} [options]
2674
+ * @param {string} [options.cacheName] The cache to use for precaching.
2675
+ * @param {string} [options.plugins] Plugins to use when precaching as well
2676
+ * as responding to fetch events for precached assets.
2677
+ * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to
2678
+ * get the response from the network if there's a precache miss.
2679
+ */
2680
+ constructor({
2681
+ cacheName,
2682
+ plugins = [],
2683
+ fallbackToNetwork = true
2684
+ } = {}) {
2685
+ this._urlsToCacheKeys = new Map();
2686
+ this._urlsToCacheModes = new Map();
2687
+ this._cacheKeysToIntegrities = new Map();
2688
+ this._strategy = new PrecacheStrategy({
2689
+ cacheName: cacheNames.getPrecacheName(cacheName),
2690
+ plugins: [...plugins, new PrecacheCacheKeyPlugin({
2691
+ precacheController: this
2692
+ })],
2693
+ fallbackToNetwork
2694
+ });
2695
+ // Bind the install and activate methods to the instance.
2696
+ this.install = this.install.bind(this);
2697
+ this.activate = this.activate.bind(this);
2698
+ }
2699
+ /**
2700
+ * @type {workbox-precaching.PrecacheStrategy} The strategy created by this controller and
2701
+ * used to cache assets and respond to fetch events.
2702
+ */
2703
+ get strategy() {
2704
+ return this._strategy;
2705
+ }
2706
+ /**
2707
+ * Adds items to the precache list, removing any duplicates and
2708
+ * stores the files in the
2709
+ * {@link workbox-core.cacheNames|"precache cache"} when the service
2710
+ * worker installs.
2711
+ *
2712
+ * This method can be called multiple times.
2713
+ *
2714
+ * @param {Array<Object|string>} [entries=[]] Array of entries to precache.
2715
+ */
2716
+ precache(entries) {
2717
+ this.addToCacheList(entries);
2718
+ if (!this._installAndActiveListenersAdded) {
2719
+ self.addEventListener('install', this.install);
2720
+ self.addEventListener('activate', this.activate);
2721
+ this._installAndActiveListenersAdded = true;
2722
+ }
2723
+ }
2724
+ /**
2725
+ * This method will add items to the precache list, removing duplicates
2726
+ * and ensuring the information is valid.
2727
+ *
2728
+ * @param {Array<workbox-precaching.PrecacheController.PrecacheEntry|string>} entries
2729
+ * Array of entries to precache.
2730
+ */
2731
+ addToCacheList(entries) {
2732
+ {
2733
+ finalAssertExports.isArray(entries, {
2734
+ moduleName: 'workbox-precaching',
2735
+ className: 'PrecacheController',
2736
+ funcName: 'addToCacheList',
2737
+ paramName: 'entries'
2738
+ });
2739
+ }
2740
+ const urlsToWarnAbout = [];
2741
+ for (const entry of entries) {
2742
+ // See https://github.com/GoogleChrome/workbox/issues/2259
2743
+ if (typeof entry === 'string') {
2744
+ urlsToWarnAbout.push(entry);
2745
+ } else if (entry && entry.revision === undefined) {
2746
+ urlsToWarnAbout.push(entry.url);
2747
+ }
2748
+ const {
2749
+ cacheKey,
2750
+ url
2751
+ } = createCacheKey(entry);
2752
+ const cacheMode = typeof entry !== 'string' && entry.revision ? 'reload' : 'default';
2753
+ if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
2754
+ throw new WorkboxError('add-to-cache-list-conflicting-entries', {
2755
+ firstEntry: this._urlsToCacheKeys.get(url),
2756
+ secondEntry: cacheKey
2757
+ });
2758
+ }
2759
+ if (typeof entry !== 'string' && entry.integrity) {
2760
+ if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {
2761
+ throw new WorkboxError('add-to-cache-list-conflicting-integrities', {
2762
+ url
2763
+ });
2764
+ }
2765
+ this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
2766
+ }
2767
+ this._urlsToCacheKeys.set(url, cacheKey);
2768
+ this._urlsToCacheModes.set(url, cacheMode);
2769
+ if (urlsToWarnAbout.length > 0) {
2770
+ const warningMessage = `Workbox is precaching URLs without revision ` + `info: ${urlsToWarnAbout.join(', ')}\nThis is generally NOT safe. ` + `Learn more at https://bit.ly/wb-precache`;
2771
+ {
2772
+ logger.warn(warningMessage);
2773
+ }
2774
+ }
2775
+ }
2776
+ }
2777
+ /**
2778
+ * Precaches new and updated assets. Call this method from the service worker
2779
+ * install event.
2780
+ *
2781
+ * Note: this method calls `event.waitUntil()` for you, so you do not need
2782
+ * to call it yourself in your event handlers.
2783
+ *
2784
+ * @param {ExtendableEvent} event
2785
+ * @return {Promise<workbox-precaching.InstallResult>}
2786
+ */
2787
+ install(event) {
2788
+ // waitUntil returns Promise<any>
2789
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
2790
+ return waitUntil(event, async () => {
2791
+ const installReportPlugin = new PrecacheInstallReportPlugin();
2792
+ this.strategy.plugins.push(installReportPlugin);
2793
+ // Cache entries one at a time.
2794
+ // See https://github.com/GoogleChrome/workbox/issues/2528
2795
+ for (const [url, cacheKey] of this._urlsToCacheKeys) {
2796
+ const integrity = this._cacheKeysToIntegrities.get(cacheKey);
2797
+ const cacheMode = this._urlsToCacheModes.get(url);
2798
+ const request = new Request(url, {
2799
+ integrity,
2800
+ cache: cacheMode,
2801
+ credentials: 'same-origin'
2802
+ });
2803
+ await Promise.all(this.strategy.handleAll({
2804
+ params: {
2805
+ cacheKey
2806
+ },
2807
+ request,
2808
+ event
2809
+ }));
2810
+ }
2811
+ const {
2812
+ updatedURLs,
2813
+ notUpdatedURLs
2814
+ } = installReportPlugin;
2815
+ {
2816
+ printInstallDetails(updatedURLs, notUpdatedURLs);
2817
+ }
2818
+ return {
2819
+ updatedURLs,
2820
+ notUpdatedURLs
2821
+ };
2822
+ });
2823
+ }
2824
+ /**
2825
+ * Deletes assets that are no longer present in the current precache manifest.
2826
+ * Call this method from the service worker activate event.
2827
+ *
2828
+ * Note: this method calls `event.waitUntil()` for you, so you do not need
2829
+ * to call it yourself in your event handlers.
2830
+ *
2831
+ * @param {ExtendableEvent} event
2832
+ * @return {Promise<workbox-precaching.CleanupResult>}
2833
+ */
2834
+ activate(event) {
2835
+ // waitUntil returns Promise<any>
2836
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
2837
+ return waitUntil(event, async () => {
2838
+ const cache = await self.caches.open(this.strategy.cacheName);
2839
+ const currentlyCachedRequests = await cache.keys();
2840
+ const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
2841
+ const deletedURLs = [];
2842
+ for (const request of currentlyCachedRequests) {
2843
+ if (!expectedCacheKeys.has(request.url)) {
2844
+ await cache.delete(request);
2845
+ deletedURLs.push(request.url);
2846
+ }
2847
+ }
2848
+ {
2849
+ printCleanupDetails(deletedURLs);
2850
+ }
2851
+ return {
2852
+ deletedURLs
2853
+ };
2854
+ });
2855
+ }
2856
+ /**
2857
+ * Returns a mapping of a precached URL to the corresponding cache key, taking
2858
+ * into account the revision information for the URL.
2859
+ *
2860
+ * @return {Map<string, string>} A URL to cache key mapping.
2861
+ */
2862
+ getURLsToCacheKeys() {
2863
+ return this._urlsToCacheKeys;
2864
+ }
2865
+ /**
2866
+ * Returns a list of all the URLs that have been precached by the current
2867
+ * service worker.
2868
+ *
2869
+ * @return {Array<string>} The precached URLs.
2870
+ */
2871
+ getCachedURLs() {
2872
+ return [...this._urlsToCacheKeys.keys()];
2873
+ }
2874
+ /**
2875
+ * Returns the cache key used for storing a given URL. If that URL is
2876
+ * unversioned, like `/index.html', then the cache key will be the original
2877
+ * URL with a search parameter appended to it.
2878
+ *
2879
+ * @param {string} url A URL whose cache key you want to look up.
2880
+ * @return {string} The versioned URL that corresponds to a cache key
2881
+ * for the original URL, or undefined if that URL isn't precached.
2882
+ */
2883
+ getCacheKeyForURL(url) {
2884
+ const urlObject = new URL(url, location.href);
2885
+ return this._urlsToCacheKeys.get(urlObject.href);
2886
+ }
2887
+ /**
2888
+ * @param {string} url A cache key whose SRI you want to look up.
2889
+ * @return {string} The subresource integrity associated with the cache key,
2890
+ * or undefined if it's not set.
2891
+ */
2892
+ getIntegrityForCacheKey(cacheKey) {
2893
+ return this._cacheKeysToIntegrities.get(cacheKey);
2894
+ }
2895
+ /**
2896
+ * This acts as a drop-in replacement for
2897
+ * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match)
2898
+ * with the following differences:
2899
+ *
2900
+ * - It knows what the name of the precache is, and only checks in that cache.
2901
+ * - It allows you to pass in an "original" URL without versioning parameters,
2902
+ * and it will automatically look up the correct cache key for the currently
2903
+ * active revision of that URL.
2904
+ *
2905
+ * E.g., `matchPrecache('index.html')` will find the correct precached
2906
+ * response for the currently active service worker, even if the actual cache
2907
+ * key is `'/index.html?__WB_REVISION__=1234abcd'`.
2908
+ *
2909
+ * @param {string|Request} request The key (without revisioning parameters)
2910
+ * to look up in the precache.
2911
+ * @return {Promise<Response|undefined>}
2912
+ */
2913
+ async matchPrecache(request) {
2914
+ const url = request instanceof Request ? request.url : request;
2915
+ const cacheKey = this.getCacheKeyForURL(url);
2916
+ if (cacheKey) {
2917
+ const cache = await self.caches.open(this.strategy.cacheName);
2918
+ return cache.match(cacheKey);
2919
+ }
2920
+ return undefined;
2921
+ }
2922
+ /**
2923
+ * Returns a function that looks up `url` in the precache (taking into
2924
+ * account revision information), and returns the corresponding `Response`.
2925
+ *
2926
+ * @param {string} url The precached URL which will be used to lookup the
2927
+ * `Response`.
2928
+ * @return {workbox-routing~handlerCallback}
2929
+ */
2930
+ createHandlerBoundToURL(url) {
2931
+ const cacheKey = this.getCacheKeyForURL(url);
2932
+ if (!cacheKey) {
2933
+ throw new WorkboxError('non-precached-url', {
2934
+ url
2935
+ });
2936
+ }
2937
+ return options => {
2938
+ options.request = new Request(url);
2939
+ options.params = Object.assign({
2940
+ cacheKey
2941
+ }, options.params);
2942
+ return this.strategy.handle(options);
2943
+ };
2944
+ }
2945
+ }
2946
+
2947
+ /*
2948
+ Copyright 2019 Google LLC
2949
+
2950
+ Use of this source code is governed by an MIT-style
2951
+ license that can be found in the LICENSE file or at
2952
+ https://opensource.org/licenses/MIT.
2953
+ */
2954
+ let precacheController;
2955
+ /**
2956
+ * @return {PrecacheController}
2957
+ * @private
2958
+ */
2959
+ const getOrCreatePrecacheController = () => {
2960
+ if (!precacheController) {
2961
+ precacheController = new PrecacheController();
2962
+ }
2963
+ return precacheController;
2964
+ };
2965
+
2966
+ /*
2967
+ Copyright 2018 Google LLC
2968
+
2969
+ Use of this source code is governed by an MIT-style
2970
+ license that can be found in the LICENSE file or at
2971
+ https://opensource.org/licenses/MIT.
2972
+ */
2973
+ /**
2974
+ * Removes any URL search parameters that should be ignored.
2975
+ *
2976
+ * @param {URL} urlObject The original URL.
2977
+ * @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against
2978
+ * each search parameter name. Matches mean that the search parameter should be
2979
+ * ignored.
2980
+ * @return {URL} The URL with any ignored search parameters removed.
2981
+ *
2982
+ * @private
2983
+ * @memberof workbox-precaching
2984
+ */
2985
+ function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) {
2986
+ // Convert the iterable into an array at the start of the loop to make sure
2987
+ // deletion doesn't mess up iteration.
2988
+ for (const paramName of [...urlObject.searchParams.keys()]) {
2989
+ if (ignoreURLParametersMatching.some(regExp => regExp.test(paramName))) {
2990
+ urlObject.searchParams.delete(paramName);
2991
+ }
2992
+ }
2993
+ return urlObject;
2994
+ }
2995
+
2996
+ /*
2997
+ Copyright 2019 Google LLC
2998
+
2999
+ Use of this source code is governed by an MIT-style
3000
+ license that can be found in the LICENSE file or at
3001
+ https://opensource.org/licenses/MIT.
3002
+ */
3003
+ /**
3004
+ * Generator function that yields possible variations on the original URL to
3005
+ * check, one at a time.
3006
+ *
3007
+ * @param {string} url
3008
+ * @param {Object} options
3009
+ *
3010
+ * @private
3011
+ * @memberof workbox-precaching
3012
+ */
3013
+ function* generateURLVariations(url, {
3014
+ ignoreURLParametersMatching = [/^utm_/, /^fbclid$/],
3015
+ directoryIndex = 'index.html',
3016
+ cleanURLs = true,
3017
+ urlManipulation
3018
+ } = {}) {
3019
+ const urlObject = new URL(url, location.href);
3020
+ urlObject.hash = '';
3021
+ yield urlObject.href;
3022
+ const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);
3023
+ yield urlWithoutIgnoredParams.href;
3024
+ if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) {
3025
+ const directoryURL = new URL(urlWithoutIgnoredParams.href);
3026
+ directoryURL.pathname += directoryIndex;
3027
+ yield directoryURL.href;
3028
+ }
3029
+ if (cleanURLs) {
3030
+ const cleanURL = new URL(urlWithoutIgnoredParams.href);
3031
+ cleanURL.pathname += '.html';
3032
+ yield cleanURL.href;
3033
+ }
3034
+ if (urlManipulation) {
3035
+ const additionalURLs = urlManipulation({
3036
+ url: urlObject
3037
+ });
3038
+ for (const urlToAttempt of additionalURLs) {
3039
+ yield urlToAttempt.href;
3040
+ }
3041
+ }
3042
+ }
3043
+
3044
+ /*
3045
+ Copyright 2020 Google LLC
3046
+
3047
+ Use of this source code is governed by an MIT-style
3048
+ license that can be found in the LICENSE file or at
3049
+ https://opensource.org/licenses/MIT.
3050
+ */
3051
+ /**
3052
+ * A subclass of {@link workbox-routing.Route} that takes a
3053
+ * {@link workbox-precaching.PrecacheController}
3054
+ * instance and uses it to match incoming requests and handle fetching
3055
+ * responses from the precache.
3056
+ *
3057
+ * @memberof workbox-precaching
3058
+ * @extends workbox-routing.Route
3059
+ */
3060
+ class PrecacheRoute extends Route {
3061
+ /**
3062
+ * @param {PrecacheController} precacheController A `PrecacheController`
3063
+ * instance used to both match requests and respond to fetch events.
3064
+ * @param {Object} [options] Options to control how requests are matched
3065
+ * against the list of precached URLs.
3066
+ * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will
3067
+ * check cache entries for a URLs ending with '/' to see if there is a hit when
3068
+ * appending the `directoryIndex` value.
3069
+ * @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/, /^fbclid$/]] An
3070
+ * array of regex's to remove search params when looking for a cache match.
3071
+ * @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will
3072
+ * check the cache for the URL with a `.html` added to the end of the end.
3073
+ * @param {workbox-precaching~urlManipulation} [options.urlManipulation]
3074
+ * This is a function that should take a URL and return an array of
3075
+ * alternative URLs that should be checked for precache matches.
3076
+ */
3077
+ constructor(precacheController, options) {
3078
+ const match = ({
3079
+ request
3080
+ }) => {
3081
+ const urlsToCacheKeys = precacheController.getURLsToCacheKeys();
3082
+ for (const possibleURL of generateURLVariations(request.url, options)) {
3083
+ const cacheKey = urlsToCacheKeys.get(possibleURL);
3084
+ if (cacheKey) {
3085
+ const integrity = precacheController.getIntegrityForCacheKey(cacheKey);
3086
+ return {
3087
+ cacheKey,
3088
+ integrity
3089
+ };
3090
+ }
3091
+ }
3092
+ {
3093
+ logger.debug(`Precaching did not find a match for ` + getFriendlyURL(request.url));
3094
+ }
3095
+ return;
3096
+ };
3097
+ super(match, precacheController.strategy);
3098
+ }
3099
+ }
3100
+
3101
+ /*
3102
+ Copyright 2019 Google LLC
3103
+ Use of this source code is governed by an MIT-style
3104
+ license that can be found in the LICENSE file or at
3105
+ https://opensource.org/licenses/MIT.
3106
+ */
3107
+ /**
3108
+ * Add a `fetch` listener to the service worker that will
3109
+ * respond to
3110
+ * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}
3111
+ * with precached assets.
3112
+ *
3113
+ * Requests for assets that aren't precached, the `FetchEvent` will not be
3114
+ * responded to, allowing the event to fall through to other `fetch` event
3115
+ * listeners.
3116
+ *
3117
+ * @param {Object} [options] See the {@link workbox-precaching.PrecacheRoute}
3118
+ * options.
3119
+ *
3120
+ * @memberof workbox-precaching
3121
+ */
3122
+ function addRoute(options) {
3123
+ const precacheController = getOrCreatePrecacheController();
3124
+ const precacheRoute = new PrecacheRoute(precacheController, options);
3125
+ registerRoute(precacheRoute);
3126
+ }
3127
+
3128
+ /*
3129
+ Copyright 2019 Google LLC
3130
+
3131
+ Use of this source code is governed by an MIT-style
3132
+ license that can be found in the LICENSE file or at
3133
+ https://opensource.org/licenses/MIT.
3134
+ */
3135
+ /**
3136
+ * Adds items to the precache list, removing any duplicates and
3137
+ * stores the files in the
3138
+ * {@link workbox-core.cacheNames|"precache cache"} when the service
3139
+ * worker installs.
3140
+ *
3141
+ * This method can be called multiple times.
3142
+ *
3143
+ * Please note: This method **will not** serve any of the cached files for you.
3144
+ * It only precaches files. To respond to a network request you call
3145
+ * {@link workbox-precaching.addRoute}.
3146
+ *
3147
+ * If you have a single array of files to precache, you can just call
3148
+ * {@link workbox-precaching.precacheAndRoute}.
3149
+ *
3150
+ * @param {Array<Object|string>} [entries=[]] Array of entries to precache.
3151
+ *
3152
+ * @memberof workbox-precaching
3153
+ */
3154
+ function precache(entries) {
3155
+ const precacheController = getOrCreatePrecacheController();
3156
+ precacheController.precache(entries);
3157
+ }
3158
+
3159
+ /*
3160
+ Copyright 2019 Google LLC
3161
+
3162
+ Use of this source code is governed by an MIT-style
3163
+ license that can be found in the LICENSE file or at
3164
+ https://opensource.org/licenses/MIT.
3165
+ */
3166
+ /**
3167
+ * This method will add entries to the precache list and add a route to
3168
+ * respond to fetch events.
3169
+ *
3170
+ * This is a convenience method that will call
3171
+ * {@link workbox-precaching.precache} and
3172
+ * {@link workbox-precaching.addRoute} in a single call.
3173
+ *
3174
+ * @param {Array<Object|string>} entries Array of entries to precache.
3175
+ * @param {Object} [options] See the
3176
+ * {@link workbox-precaching.PrecacheRoute} options.
3177
+ *
3178
+ * @memberof workbox-precaching
3179
+ */
3180
+ function precacheAndRoute(entries, options) {
3181
+ precache(entries);
3182
+ addRoute(options);
3183
+ }
3184
+
3185
+ /*
3186
+ Copyright 2018 Google LLC
3187
+
3188
+ Use of this source code is governed by an MIT-style
3189
+ license that can be found in the LICENSE file or at
3190
+ https://opensource.org/licenses/MIT.
3191
+ */
3192
+ const SUBSTRING_TO_FIND = '-precache-';
3193
+ /**
3194
+ * Cleans up incompatible precaches that were created by older versions of
3195
+ * Workbox, by a service worker registered under the current scope.
3196
+ *
3197
+ * This is meant to be called as part of the `activate` event.
3198
+ *
3199
+ * This should be safe to use as long as you don't include `substringToFind`
3200
+ * (defaulting to `-precache-`) in your non-precache cache names.
3201
+ *
3202
+ * @param {string} currentPrecacheName The cache name currently in use for
3203
+ * precaching. This cache won't be deleted.
3204
+ * @param {string} [substringToFind='-precache-'] Cache names which include this
3205
+ * substring will be deleted (excluding `currentPrecacheName`).
3206
+ * @return {Array<string>} A list of all the cache names that were deleted.
3207
+ *
3208
+ * @private
3209
+ * @memberof workbox-precaching
3210
+ */
3211
+ const deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => {
3212
+ const cacheNames = await self.caches.keys();
3213
+ const cacheNamesToDelete = cacheNames.filter(cacheName => {
3214
+ return cacheName.includes(substringToFind) && cacheName.includes(self.registration.scope) && cacheName !== currentPrecacheName;
3215
+ });
3216
+ await Promise.all(cacheNamesToDelete.map(cacheName => self.caches.delete(cacheName)));
3217
+ return cacheNamesToDelete;
3218
+ };
3219
+
3220
+ /*
3221
+ Copyright 2019 Google LLC
3222
+
3223
+ Use of this source code is governed by an MIT-style
3224
+ license that can be found in the LICENSE file or at
3225
+ https://opensource.org/licenses/MIT.
3226
+ */
3227
+ /**
3228
+ * Adds an `activate` event listener which will clean up incompatible
3229
+ * precaches that were created by older versions of Workbox.
3230
+ *
3231
+ * @memberof workbox-precaching
3232
+ */
3233
+ function cleanupOutdatedCaches() {
3234
+ // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
3235
+ self.addEventListener('activate', event => {
3236
+ const cacheName = cacheNames.getPrecacheName();
3237
+ event.waitUntil(deleteOutdatedCaches(cacheName).then(cachesDeleted => {
3238
+ {
3239
+ if (cachesDeleted.length > 0) {
3240
+ logger.log(`The following out-of-date precaches were cleaned up ` + `automatically:`, cachesDeleted);
3241
+ }
3242
+ }
3243
+ }));
3244
+ });
3245
+ }
3246
+
3247
+ /*
3248
+ Copyright 2018 Google LLC
3249
+
3250
+ Use of this source code is governed by an MIT-style
3251
+ license that can be found in the LICENSE file or at
3252
+ https://opensource.org/licenses/MIT.
3253
+ */
3254
+ /**
3255
+ * NavigationRoute makes it easy to create a
3256
+ * {@link workbox-routing.Route} that matches for browser
3257
+ * [navigation requests]{@link https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests}.
3258
+ *
3259
+ * It will only match incoming Requests whose
3260
+ * {@link https://fetch.spec.whatwg.org/#concept-request-mode|mode}
3261
+ * is set to `navigate`.
3262
+ *
3263
+ * You can optionally only apply this route to a subset of navigation requests
3264
+ * by using one or both of the `denylist` and `allowlist` parameters.
3265
+ *
3266
+ * @memberof workbox-routing
3267
+ * @extends workbox-routing.Route
3268
+ */
3269
+ class NavigationRoute extends Route {
3270
+ /**
3271
+ * If both `denylist` and `allowlist` are provided, the `denylist` will
3272
+ * take precedence and the request will not match this route.
3273
+ *
3274
+ * The regular expressions in `allowlist` and `denylist`
3275
+ * are matched against the concatenated
3276
+ * [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname}
3277
+ * and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search}
3278
+ * portions of the requested URL.
3279
+ *
3280
+ * *Note*: These RegExps may be evaluated against every destination URL during
3281
+ * a navigation. Avoid using
3282
+ * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077),
3283
+ * or else your users may see delays when navigating your site.
3284
+ *
3285
+ * @param {workbox-routing~handlerCallback} handler A callback
3286
+ * function that returns a Promise resulting in a Response.
3287
+ * @param {Object} options
3288
+ * @param {Array<RegExp>} [options.denylist] If any of these patterns match,
3289
+ * the route will not handle the request (even if a allowlist RegExp matches).
3290
+ * @param {Array<RegExp>} [options.allowlist=[/./]] If any of these patterns
3291
+ * match the URL's pathname and search parameter, the route will handle the
3292
+ * request (assuming the denylist doesn't match).
3293
+ */
3294
+ constructor(handler, {
3295
+ allowlist = [/./],
3296
+ denylist = []
3297
+ } = {}) {
3298
+ {
3299
+ finalAssertExports.isArrayOfClass(allowlist, RegExp, {
3300
+ moduleName: 'workbox-routing',
3301
+ className: 'NavigationRoute',
3302
+ funcName: 'constructor',
3303
+ paramName: 'options.allowlist'
3304
+ });
3305
+ finalAssertExports.isArrayOfClass(denylist, RegExp, {
3306
+ moduleName: 'workbox-routing',
3307
+ className: 'NavigationRoute',
3308
+ funcName: 'constructor',
3309
+ paramName: 'options.denylist'
3310
+ });
3311
+ }
3312
+ super(options => this._match(options), handler);
3313
+ this._allowlist = allowlist;
3314
+ this._denylist = denylist;
3315
+ }
3316
+ /**
3317
+ * Routes match handler.
3318
+ *
3319
+ * @param {Object} options
3320
+ * @param {URL} options.url
3321
+ * @param {Request} options.request
3322
+ * @return {boolean}
3323
+ *
3324
+ * @private
3325
+ */
3326
+ _match({
3327
+ url,
3328
+ request
3329
+ }) {
3330
+ if (request && request.mode !== 'navigate') {
3331
+ return false;
3332
+ }
3333
+ const pathnameAndSearch = url.pathname + url.search;
3334
+ for (const regExp of this._denylist) {
3335
+ if (regExp.test(pathnameAndSearch)) {
3336
+ {
3337
+ logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL matches this denylist pattern: ` + `${regExp.toString()}`);
3338
+ }
3339
+ return false;
3340
+ }
3341
+ }
3342
+ if (this._allowlist.some(regExp => regExp.test(pathnameAndSearch))) {
3343
+ {
3344
+ logger.debug(`The navigation route ${pathnameAndSearch} ` + `is being used.`);
3345
+ }
3346
+ return true;
3347
+ }
3348
+ {
3349
+ logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL being navigated to doesn't ` + `match the allowlist.`);
3350
+ }
3351
+ return false;
3352
+ }
3353
+ }
3354
+
3355
+ /*
3356
+ Copyright 2019 Google LLC
3357
+
3358
+ Use of this source code is governed by an MIT-style
3359
+ license that can be found in the LICENSE file or at
3360
+ https://opensource.org/licenses/MIT.
3361
+ */
3362
+ /**
3363
+ * Helper function that calls
3364
+ * {@link PrecacheController#createHandlerBoundToURL} on the default
3365
+ * {@link PrecacheController} instance.
3366
+ *
3367
+ * If you are creating your own {@link PrecacheController}, then call the
3368
+ * {@link PrecacheController#createHandlerBoundToURL} on that instance,
3369
+ * instead of using this function.
3370
+ *
3371
+ * @param {string} url The precached URL which will be used to lookup the
3372
+ * `Response`.
3373
+ * @param {boolean} [fallbackToNetwork=true] Whether to attempt to get the
3374
+ * response from the network if there's a precache miss.
3375
+ * @return {workbox-routing~handlerCallback}
3376
+ *
3377
+ * @memberof workbox-precaching
3378
+ */
3379
+ function createHandlerBoundToURL(url) {
3380
+ const precacheController = getOrCreatePrecacheController();
3381
+ return precacheController.createHandlerBoundToURL(url);
3382
+ }
3383
+
3384
+ exports.NavigationRoute = NavigationRoute;
3385
+ exports.cleanupOutdatedCaches = cleanupOutdatedCaches;
3386
+ exports.clientsClaim = clientsClaim;
3387
+ exports.createHandlerBoundToURL = createHandlerBoundToURL;
3388
+ exports.precacheAndRoute = precacheAndRoute;
3389
+ exports.registerRoute = registerRoute;
3390
+
3391
+ }));